14.04.2016 gamemaker gamedev

GameMaker: Fun with Surfaces

Surfaces are one of the keys to success in GameMaker. It allows us to use advanced features, such as mini-maps, shadows, fog of war and other fun things. I am going to show you in this crash course how to use them.

Prerequesites

You need an understanding of GML. If you are still using Drag and Drop Items, you might not understand this tutorial and want to dive into programming first.

Still here? Good, grab a cup of coffee and some cookies.

Next you are going to need one or two different backgrounds, an enemy sprite and a player sprite. I used sprites from this pack: http://opengameart.org/content/space-shooter-redux Consider donating to Kenny if you like his art, he does all this stuff for free.

All set? Good, now onwards, my minions, to the tutorial!

Creatonism

Alright, the first section should be pretty straightforward.

  • Create one or two different backgrounds, depending on your gusto. I use one bright and one dark background.

  • Create a sprite, name it sprEnemy and load one of the sprites from the pack.

  • Create a sprite, name it sprPlayer and load (a different) one of the sprites from the pack.

  • Create objects for the previously created sprites (they don’t need to do anything, but you can make the player move if you want)

  • Create a room.

  • Set it’s height to 720 and it’s width to 2560 in the settings

  • Switch to views

  • Make the room follow the player

  • Set “View in Room” to 1280x720

  • Set “Viewport on Screen” to 1280x720

  • Add one instance of the player and some random number of enemies randomly to the room.

  • Set the lighter background for the room.

This is how it should look like:

room settings

Fun with Parents

Parenting is pretty much polymorphism in GM:S. So if you set a parent, all events in that parent are transferred to the child. Or, you can make this object the parent of all objects that need to be drawn on the mini-map and you can walk them without having to search for each object that you want on your mini-map. This is quite handy.

Create an object, call it objShowMiniMapParent and assign it as parent to objEnemy and objPlayer.

Quick looks are the best looks

So we do not want our player having to stare at the mini-map all the time. You want to have a quick look and he knows what is happening. Since we are using a parent to walk through all objects, we do not know what kind of object it really is. If it is the player or an enemy. For this reason, we use 2 new images:

red circle green circle

Now double click your player sprite in GameMaker. Go to “Edit sprite” and in the File menu, click on “Add from File”. Now GameMaker will complain that this file has not the same size. Answer as shown here:

Settings for import

This will ensure your sprite will not get cropped and the circle is placed on a transparent background. Later in the code, we are going to use this to display the circles on the mini-map.

You should have it like this now:

Finished Enemy

Finished Player

But, if you would start the game right now, the game would “play” it as animation. That is why we need to add some code to the player object and the enemies. Add a create event to both of them and add this code:

1
2
image_speed = 0;
image_index = 0;

Setting image_speed to zero stops the animation and image_index set’s the normal sprite of your space ship as current sprite, provided it is 0, which it should be since you added the circle after the normal sprite, right?

Coding Time

Now we are going to create the mini-map. Create an object and name it objMinimap. I always like to add a sprite with an ugly background and some text on it to it, so I find it better in the editor. But that is up to you. Place that object in the level.

Now, coding time. We are going to use a surface to draw our mini-map. So first, we create a “Create Event” and add this code:

1
2
/// Init
oSurface = -1;

Next, we are going to create the surface with the exact size of our room and draw the content of our level to this surface. Create a new script in the step event for this code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/// Create and populate Surface

/* If the surface does not exist, we create it.
    Surfaces can be discarded in memory at any time,
    so we need to always check this before we use it.
*/
if (!surface_exists(oSurface)){
    oSurface = surface_create(room_width, room_height);
}

// Set the surface as texture target
surface_set_target(oSurface);

// Clear it with black
draw_clear(c_black);

/* Draw the second, dark background.
   You can leave this out if you prefer the mini-map to have a black background
   or you can draw the same background as the real room, but I don't like that.
   However, it is ultimately up to you.
*/
draw_background_tiled(background1, 0, 0);

// Here we find out how many objects exist that want to be drawn on the mini-map.
var numObjects = instance_number(objShowMiniMapParent)

// Here we loop through all those objects from 0 to the max number of objects.
for ( var i = 0; i < numObjects; i++ )
{
    var obj = instance_find( objShowMiniMapParent, i );
    with(obj){
        // Remember that we have 2 images inside our sprites?
        // This code draws the second one, which is the red or green circle.
        draw_sprite( sprite_index, 1, x, y );
    }
}

// Tell GMS to stop drawing to this surface.
surface_reset_target();

Drawing the surface

This part is easy. We need to draw the surface to our screen. Add a “Draw GUI” Event to objMiniMap and add this code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/// Draw Surface to Screen

// Check for surface before drawing, it could be dead already. Should not happen, but can.
if (surface_exists(oSurface))
{
    /*
       Draw the surface first.
       But, since this surface is quite big,
       we are going to scale it down a bit and
       draw it to the top left on the screen.
    */
    draw_surface_stretched( oSurface, 40, 40, room_width/8, room_height/8 );

    // Save the old color that was set
    var old = draw_get_color();

    // Get us a nice red
    draw_set_color(c_red);

    // Draw a red rectangle around it, so we can even better distinguish it from the actual room.
    draw_rectangle( 40, 40, room_width/8+40, room_height/8+40, true);

    // Set the old draw color
    draw_set_color(old);
}

As you can see, using the surface is pretty straightforward and easy. And this is what it looks like:

Finished mini-map

Conclusion

There are easier ways to add mini-maps and you can actually use views for that. But in my opinion, that is just plain ugly. This way we have a nice mini-map that can be distinguished from the actual level and gameplay and you only need a quick look to see what is going on. You can use this technique for space games, platformers, RTS, does not matter. The approach can always be similar. Of course, if you have really a lot of objects, this might cost significant performance!

But it should be good enough for your average household 2D Game. Hope you have fun using this tutorial in your game.

If you want to save memory, scale down the actual size of the map when you create it, by dividing the room size with e.g. 3 or 4. Don’t forget to adjust the coordinates of objects too, by dividing them through the same number. This is especially important on mobile devices.

Material

You can download this example with the GameMaker Player on Steam .

Link to the author's twitter Link to the authors ko-fi page

comments

Characters: 0/1000

gravatar portrait

 Pinned by contact@tuxstash.de

Come join the discussion and write something nice. You will have to confirm your comment by mail, so make sure it is legit and not a throwaway. Only the name part of it will be displayed, so don't worry about spam. If it does not show up after confirming it, it may be considered spam, but I curate them manually, so don't worry. Please read the privacy statement for more.