We would love you to play this but your browser doesn't support HTML5 canvas.
Just use a different browser and you can happily play this game.

Unlimited Surface Destruction in GameMaker (HTML5 Compatible)

So I’ve been using surfaces loads recently in GameMaker and I wanted to show people how I made the demo above and how you can add huge amounts of destructible damage into your GameMaker games without impacting performance.

Space to Shoot
E to pick up new weapons

How can it be unlimited destruction without racking up ram?

In the above demo it can certainly look like there are hundreds or thousands (and if you play long enough tens of thousands) of tiny bits of bullet casing and debris bouncing around and scattered on the floor but this is an illusion. The secret is that as soon as they are stationary I draw them onto a big surface and delete the object. So yes each one of those wreckages and rubble does start off as its own object that bounces around and creates sounds. But after a second or two they are actually just combined into an image which is always drawn and doesn’t care if it only has one bullet case on the floor or if you somehow manage to fill in every single pixel with damage from the walls.

On top of this I am using the same surface to draw all of the walls onto, once I have done that I can erase any parts of those walls to make it looks like bullet holes in them and display another image underneath. So this surface is used for displaying both the mess on the floor AND any holes in the walls.

Example of wall damage in GameMaker

If you are new to this kind of thing I have written a very similar guide for doing blood on surfaces in GameMaker which is slightly easier to follow. It’s the exact same technique and looks at it from a side on platformer style game so for some people that will be easier to follow.

How it works :

For some people the above description will be enough to replicate the concept, for us other mortals let’s look in more detail at how it works and then I will put the code I have used.

First I start by making a surface the same size as the room that I can draw all these debris on. If you haven’t used surfaces I wrote guides on drop shadows or top down lighting that are easier to learn from, however they are basically an empty canvas you can draw onto in the same way you draw any sprite onto the screen and anything you draw is preserved, these surfaces can then themselves be drawn onto the screen with everything you have added onto them.

I called my surface damagemask and I put it in its own object called obj_damage. You can call yours whatever you like and all you need to know is you want to make it once the room is created and this is the one surface we will be using that all damage will be drawn onto.

damagemask = surface_create(room_width, room_height)
    

I mentioned before that we will be using this same surface to do both the damage on the walls and the rubble on the floor. I’m going to talk about the damage on the floor first because the concept is a little easier and it probably has more impact on a game.

Making the Bullet Cases :

The bullet cases and the debris that are knocked out of the wall are virtually the same, they just use different sprites, sounds and move at different speeds. The bullet cases are just made every time the player shoots, I give them some velocity 90degrees away from how the player is facing as well as giving it a little spin and do some very simple collision code to make it scatter off the walls. Once it is created I set a timer for 2 seconds after that time this code will be run:

if (surface_exists(obj_damage.damagemask)) {
    surface_set_target(obj_damage.damagemask)
    draw_self()
    surface_reset_target();
}
instance_destroy()
    

This will take the surface we made at the start and using surface_set_target() we tell GameMaker to draw onto this surface rather than onto the screen. We then draw the final resting position of this bullet case onto the surface and destroy the object freeing up the memory.

Making the floor debris :

The wall damage is the same however I create it at the impact point where the bullet hits the wall and give it forwards momentum in the same direction the bullet was moving.

One of the crazy side effects of making it like this is the fact that if the bullet penetrates all the way to the other side it looks like the damage gets blasted out of other side of the wall. However if the bullet looks like it just stops as soon as it hits the wall the debris will be created on the side of the wall closest the player and thus the damage lands on the side the player is and looks like parts of the wall falling in. This means that if you shoot diagonally along a wall the bullet is less likely to come out the other side so damage on the wall actually looks pretty realistic. I didn’t account for this when writing the code but looks amazingly good!

Another thing I am very happy with on the wall damage is the fact it starts off as just a simple 2px by 2px image, but I give it a slight variation just to make the damage look a bit more sporadic and this really helps the whole thing look less uniform.

image_yscale=random_range(0.5,2)
image_xscale=random_range(0.5,2)
    

When each bullet lands I actually make 3 of these debris objects with their own little deviation in the angle they are moving at. If you wanted to change the amount of damage you could increase this number and obviously decrease it for less damage scattering everywhere.

So that’s it for all the mess that gets put onto the floor, it might sound complex but as you will see later on the code is actually very short.

Making damage to walls :

Let’s move onto how I did structural damage to the wall, this is the path of the bullet in the wall ending at a small textured circle. The reason I have done this part second is because it introduces one additional technique draw_set_blend_mode(bm_subtract) which basically allows us to do masking.

I have written other guides on how to do masking in GameMaker. If you are not familiar with masking those guides are far simpler and would make a better starting point, but it isn’t all that complex and it is likely if you have got this far you will find masking pretty easy.

How this works is by having all of the walls drawn on our damagemask surface and having another image under that shows what we want the wall to look like when it gets damaged, I can then just erase any bullet damage from the wall and the player will be able to see the image below.

When I made the damagemask surface I actually drew all of the walls onto it and stopped drawing them in their own draw event. For you this is likely to look like the following code:

damagemask = surface_create(room_width, room_height)

surface_set_target(damagemask)
with(obj_wall) {
	draw_self()
}
surface_reset_target();
    

This gives us a single image with every wall drawn on it.

The top surface image with every wall on:

Unlimited Surface Destruction in GameMaker (HTML5 Compatible)

The bottom background image with the damage on it:

Unlimited Surface Destruction in GameMaker (HTML5 Compatible)

So how can we erase part of the top image when a bullet hits it? This is actually very simple and can be done with the following code:

    /// In the step event if the bullet has hit the wall
    if (surface_exists(obj_damage.damagemask)) {
        surface_set_target(obj_damage.damagemask)
        
        draw_set_blend_mode(bm_subtract)
        draw_set_colour(c_black)
        draw_circle(x+hspeed,y+vspeed,4,false)
        draw_line_width(x,y,x+hspeed,y+vspeed,2)
        draw_set_blend_mode(bm_normal)
        
        surface_reset_target();
    }
    

The magic here is just the line draw_set_blend_mode(bm_subtract) this makes it so rather than drawing onto the mask like we did with the bullet cases we actually want to erase like an eraser anything that we draw. For me I just drew a circle of radius 4 at the point the bullet would be at the next step, I then draw a line from this point to its current location.

Remember everything we draw here will be removed from the surface with the walls on so we can see the layer below it that has the grey version of the wall that looks like damage.

That is it for how we do unlimited damage to the walls. The one last thing we have to do is actually draw this image onto the screen. That is done with the very simple line:

if (surface_exists(damagemask)) {
    draw_surface(damagemask,x,y)
}

With that we are basically all done! Of course I have lots of other code for having different guns and moving the player around etc. However there are better guides out there for doing top down shooter games in GameMaker, and basically anything I have put so far would work equally well on a platformer/side scroller game.

Before I actually put the code I want to point out that every time we want to use the surface we have to put ask if it exists using surface_exists(damagemask) this is because surfaces can be volatile in GameMaker and you cannot always access them.

The Code:
obj_damage:
///obj_damage - create
damagemask = surface_create(room_width, room_height)

surface_set_target(damagemask)
draw_sprite(spr_room1walls,0,0,0);
surface_reset_target();
	
/// obj_damage – draw
if (surface_exists(damagemask)) {
    draw_surface(damagemask,x,y)
}
    
obj_bulletcase:
///  obj_bulletcase - create
rotatespeed = random(4); // how fast I spin 
alarm[0] = 80 // destroy me

///  obj_bulletcase – alarm 0
if (surface_exists(obj_damage.damagemask)) {
    surface_set_target(obj_damage.damagemask)
    
    draw_self()
    
    surface_reset_target();
}

instance_destroy()

/// obj_bullercase – step
if (speed > 0) {
    speed -= 0.1
    image_angle += rotatespeed 
} else {
    speed = 0
}

// Horizontal wall bounce
if (collision_point( x+hspeed, y, obj_wall, false, false )) {
    hspeed = -hspeed
}
// Vertical wall bounce
if (collision_point( x, y+vspeed, obj_wall, false, false )) {
    vspeed = -vspeed
}
    
obj_wallknockout:
/// obj_wallknockout - create
rotatespeed = random(4); // how fast I spin 

alarm[0] = 80 // destroy me

image_yscale=random_range(0.5,2)
image_xscale=random_range(0.5,2)


/// obj_wallknockout – alarm 0



if (surface_exists(obj_damage.damagemask)) {
    surface_set_target(obj_damage.damagemask)
    
    draw_self()
    
    surface_reset_target();
}

instance_destroy()

/// obj_wallknockout – step 
if (speed > 0) {
    speed -= 0.1
    image_angle += rotatespeed 
} else {
    speed = 0
}

// Horizontal wall bounce
if (collision_point( x+hspeed, y, obj_wall, false, false )) {
    hspeed = -hspeed
}
// Vertical wall bounce
if (collision_point( x, y+vspeed, obj_wall, false, false )) {
    vspeed = -vspeed
}
    
obj_bullet:
///obj_bullet – step 

if (collision_line(xstart,ystart,x+hspeed,y+vspeed,obj_wall,false,false)) {

    /// remove this part from the wall
    if (surface_exists(obj_damage.damagemask)) {
        surface_set_target(obj_damage.damagemask)
        
        draw_set_blend_mode(bm_subtract)
        draw_set_colour(c_black)
        draw_circle(x+hspeed,y+vspeed,4,false)
        draw_line_width(x,y,x+hspeed,y+vspeed,2)
        draw_set_blend_mode(bm_normal)
        
        surface_reset_target();
    }

    // make wall knockout 
    repeat(3) {
        var wallknockout = instance_create(x+hspeed,y+vspeed,obj_wallknockout);
        wallknockout.direction = direction+random_range(-5,5)+180;
        wallknockout.speed = random_range(1,5)
    }
    
    // make sound
    var playsound = choose( sound_impact2,
                            sound_impact7,
                            sound_impact8,
                            sound_impact9,
                            sound_impact10,
                            )

    audio_play_sound(playsound, 2, false);
    audio_sound_pitch(playsound, random_range(0.9,1.1)); 
    
    instance_destroy()
}
    

And that is it for the code!

WebGL and HTML5 :

I wanted to highlight here that all of this code is compatible with the HTML5 module in GameMaker so if you wanted you can export it to work in a browser like I did at the very top. I’m fairly sure it requires WebGL to be turned on however this shouldn’t be a problem because basically every modern browser supports WebGL.

Blood effects and other types of damage :

I have written a guide here that is a little easier specifically for doing unlimited blood splashes in GameMaker. However you could put any damage on the floor.

In the same way there is no reason why you couldn’t have more complex debris, you could do bits of metal and wood coming out of the walls or wires and pipes. You could even go further than this and have photos and painting falling off and get saved onto the surface.

Also for the hole in the wall I just drew a little circle with a line coming out of it towards the player, originally this was only for debugging but I grew to like the simplicity of it. If you were using it in your game you could draw anything here to remove it from the wall. I think having a star shaped blast mark with semitransparency and little bits of shrapnel coming out would look really cool.

Sound effects :

Before I leave you I would like to point out one of the nice things I have done with the sound in the example at the top. By adjusting the pitch of the sound a very slight amount it makes it sound like I have recorded far more sounds than I actually did. I don’t see this in enough games, but adjusting the pitch of sounds in GameMaker is very easy and you can see the code here:

var playsound = choose(sound_case1,sound_case2,sound_case3)
audio_play_sound(playsound, 5, false);
audio_sound_pitch(playsound, random_range(0.9,1.1));
    
Credits :

Player art - Riley Gombart

Sounds - Mike Koenig, Kibblesbob, Ghost Rider, GunGuru, RA The Sun God, Kibblesbob, Marcel, Dion Stapper, Caroline Ford, KevanGC, snottyboi