How to Use the Destructible Terrain Converter (GMS2)

Hey! I just released an asset for making destructible terrain. Basically, it can convert your existing level (made with either objects or tiles) into a destructible terrain, and has some pretty useful functions for interacting with that terrain.

You can purchase the asset here, for only $1.99.

In this tutorial, I’ll show how you can use that asset.

1.gif
The example we’ll be making in this tutorial

This post is split into three parts: Summary, Example and Performance. In Summary, I will explain how the asset basically works. In Example, I will build a basic example of a game using this asset. And finally, in Performance, I will test the performance of the asset.

1. Summary

First, I’ll summarize the process of using the asset.

Conversion

The asset converts your existing level (made with objects/tiles) into a destructible level, removing your original objects/tiles (that were converted) from the game, when it runs.

To convert objects, you use dest_create(). You can specify up to 16 objects/instances to convert. You have to store the return value in a variable so that you can modify & collide with the terrain.

Terr = dest_create(oSolid, oRock, oHill);

In the code above, I am creating a terrain called Terr from the objects oSolid, oRock and oHill. As a result, they will be removed from the game when this code runs.

To convert tiles, you use dest_create_tiles(). As with the previous function, you can specify up to 16 tile layers to convert.

Terr = dest_create_tiles("Tiles_Down", "Tiles_Up");

You have to specify the layer name (as seen in the room), or the layer ID (from layer_get_id() or layer_create()).

Collisions

Collisions are handled using dest_collision(). The function is similar to place_meeting: you specify the coordinates of the collision (x, y) and the terrain to collide with. In this function, you can also specify multiple terrains, to check for collisions in all of them.

var colliding = dest_collision(x, y, Terr);
var colliding_right = dest_collision(x + 8, y, Terr);
var colliding_all = dest_collision(x, y, Terr, Terr2, Terr3);

The function in the first line will return true if that instance is colliding with the terrain Terr at (x, y).
The function in the second line will return true if that instance is colliding with Terr at (x + 8, y).
The function in the third line will return true if that instance is colliding with the terrains Terr, Terr2 and Terr3 at (x, y).

This function uses the bounding box of the instance for collision checking, so it won’t be precise – that means that your instance will be handled like a rectangle.

You can also use dest_collision_circle() to check for collisions using a circle.

If you need your instance’s mask to be precise, you can use dest_collision_precise() instead of dest_collision(). Keep in mind that this precise function is slow and should be avoided.

Destruction

You can destroy parts of the terrain after it has been created. You can destroy circles, rectangles, triangles, and also sprites (say your sprite is heart shaped, a heart shaped “hole” will be created on the terrain).

I’ll just copy this part from the documentation included with the asset:

Circle

        Use dest_circle_empty() to destroy a circular region inside a terrain.
        Example:
dest_circle_empty(global.terrain, x+10, y+10, 32); //(id, x, y, radius)

Rectangle

        Use dest_rect_empty() to destroy a rectangular region inside a terrain.
        Example:
dest_rect_empty(global.terrain, x-10, y-10, x+10, y+10); //(id, x1, y1, x2, y2)

Triangle

        Use dest_triangle_empty() to destroy a triangular region inside a terrain.
        Example:
dest_triangle_empty(global.terrain, x1, y1, x2, y2, x3, y3);

 Sprite

        Use dest_sprite_empty() to remove a sprite’s shape from a terrain. In other words, the sprite will clear the area it covers on the terrain.
        The sprite should be have a white shape will full opacity (alpha).
        Example:
dest_sprite_empty(global.terrain, x, y, sDest, 0); //(id, x, y, sprite, subimg)

Drawing

You can also draw stuff on the terrain. As with the destruction, you can draw circles, rectangles, triangles, and sprites. When you draw a shape, you have to specify a color for it.

I’ll copy this part from the documentation as well:

Circle

        Use dest_circle_fill() to draw a circle inside a terrain.
        Example:
dest_circle_fill(global.terrain, x, y, 32, c_red); //(id, x, y, radius, color)

Rectangle

        Use dest_rect_fill() to draw a rectangle inside a terrain.
        Example:
dest_rect_fill(global.terrain, x-10, y-10, x+10, y+10, c_blue); //(id, x1, y1, x2, y2, color)

Triangle

        Use dest_triangle_fill() to draw a triangle inside a terrain.
        Example:
dest_triangle_fill(global.terrain, x1, y1, x2, y2, x3, y3);

Sprite

        Use dest_sprite_draw() to draw a sprite to a terrain.
        Example:
dest_sprite_draw(global.terrain, x, y, sRock, 0); //(id, x, y, sprite, subimg)

2. Example

Now, we’ll build a simple example to make use of this converter. This game will involve shooting projectiles to destroy the terrain made with objects.

Importing

After purchasing the asset, it will be added to your library. You can access it from the “Marketplace” menu (make sure you are logged in in GameMaker).

0.PNG

If you can’t see the asset in the library, restart GameMaker.

1.PNG

In the far right, you’ll see four buttons. Press the second one to download the asset, then the first one to import it. This window will open:

2.PNG

Click on “Add All”, then click on “Import”.

The asset will be imported into the project.

Conversion

This is the current state of our game:

0.gif

The walls are made with an object called oWall:

3.PNG

I’ll convert the level into a destructible terrain. For that, I’ll open the Create event of oPlayer and add this:

Terr = dest_create(oWall);

This will create a terrain from all the instances of oWall, and remove them from the room.

Collisions

This is the code I am using for collisions:

if (place_meeting(x+hsp, y, oWall)){
    while(!place_meeting(x+sign(hsp), y, oWall)){
        x += sign(hsp);   
    }
    hsp = 0;
}

if (place_meeting(x, y+vsp, oWall)){
    while(!place_meeting(x, y+sign(vsp), oWall)){
        y += sign(vsp);   
    }
    vsp = 0;
}

Now, we need to replace the place_meeting() functions with dest_collision(), the function used for colliding with a terrain. And instead of oWall, we will use the terrain ID (Terr) that we created in the Create event.

Here’s the result:

if (dest_collision(x+hsp, y, Terr)){
    while(!dest_collision(x+sign(hsp), y, Terr)){
        x += sign(hsp);
    }
    hsp = 0;
}

if (dest_collision(x, y+vsp, Terr)){
    while(!dest_collision(x, y+sign(vsp), Terr)){
        y += sign(vsp);
    }
    vsp = 0;
}

Now the collisions work with the terrain, if I run the game.

Destroying

Inside oBullet (the projectile that the player shoots), I’ll add this in the Step event:

if (dest_collision_circle(x, y, sprite_width/2, oPlayer.Terr)){
    dest_circle_empty(oPlayer.Terr, x, y, 24);
    instance_destroy();
}

dest_collision_circle() will check for a collision using a circle. If a collision is found, dest_circle_empty() will empty a circular region inside the terrain. I am creating a hole with 24 radius at the bullet’s location, then destroying that instance.

1.gif

3. Performance

Now I will briefly test the performance of this asset.

Here are the specs I am testing on:

○ Pentium G4560 3.5ghz
○ 8 GB DDR4 RAM
○ Nvidia GTX 1050 ti

  • In the test project shown above, with only one terrain and a bullet destroying a circle, the FPS stayed well over 1000.
  • With 100 terrains statically present inside the room, the FPS were generally around 300.
  • With 10 terrains present inside the room, and the player checking for (box) collisions with all of them around 4 times each step, the FPS were around 500.
  • With the projectile checking collisions with all 10 terrains, the FPS count was similar – but when it actually collided with one, it ran a loop that created a hole in all of those 10 terrains – the FPS dropped, only for a split second – and it wasn’t anything palpable. This was stress testing though, and you should generally never need to destroy so many terrains simultaneously.

Conclusion

Hope this helped! You can get the asset here, for only $1.99.

If you encounter any problems with this tutorial or just want to ask something, contact me on Discord through this server (my username is Senpai Matharoo).

See you later, and till then, happy dev’ing!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s