Creating a grid with Game Maker Studio 2

If you want to create a checkerboard level for a chess game, or a turn-based game like Advance Wars 2 (which by the way is a great game, if you don't know it, play it!), you're in the right place to start. In this tutorial, we will learn how to create a grid using a 2-dimensional board. Then we'll fill in the grid with the different objects in our level.

Advance Wars 2 - The original, not the other.

Quick introduction: the 2D table

When I talk about a 2D array in GameMaker Studio, it is simply an array where each entry contains... another array. Most development languages have this type of variable, and it is a must in video games. In this way, you can store a lot of information in an orderly way, whether it's your future level or a list of characters and their favorite colors:

// Player
array_2d = [
    ["Mario", "Red", 1],
    ["Luigi", "Green", 2],
    ["Peach", "Pink" , 3]
];

// Output result
array_2d[0,0] = "Mario";
array_2d[0,1] = "Red";
array_2d[0,2] = "1";

To learn more, I invite you to visit this page.

Initialization and creation of the grid

To begin, we will create an object called oGrid and initialize in its create event a 2D table which will be used as a level. We wish to obtain a level of 20 squares in length and width, with squares of 32 pixels.

// Create event oGrid

depth = -1000;

// Width & Height size
gridWidth  = 20;
gridHeight = 20;

// Box size
boxSize = 32;

// Create 2D array filled with 0
for(var i = 0; i < gridWidth; i++){
    for(var j = 0; j < gridHeight; j ++){
        global.grid[i, j] = 0;
    }
}

Let's put this object in our room and... Congratulations! We have an object oGrid which contains a 2D array:

  • depth -1000 so that the grid is above the rest
  • gridWidth and gridHeight is the size of the grid
  • boxSize is the size of each square
  • And global.grid the variable that contains our grid, currently all cells contain the value "0

As you have seen, we use the word global to define our grid. Being an essential element of our game, and as we will use it often, it is better to use this type of variable.

A basic description of a global variable is one that, once declared, it belongs to no instance in particular and yet can be accessed by all.

Game Maker Studio

Draw the grid with the draw_line() function

To view our 2D table we can use the debugging tool provided by Game Maker Studio, and then look at what is in the variable global.grid. The second solution is to draw it on the screen. Knowing that this solution facilitates our life during the debugging phase, here is what we must write in the draw event of the object oGrid to make our grid appear:

// Draw event oGrid

// Set opacity & color
draw_set_alpha(.5);
draw_set_color(c_green);

// Loop trought each row and column
for (var i = 0; i < gridWidth; i+=1){
    draw_line(0, i*boxSize, room_width, i*boxSize);
}

for (var j = 0; j < gridHeight; j+=1) {
    draw_line(j*boxSize, 0, j*boxSize, room_height);
}

// Reset opacity & color
draw_set_color(c_white);
draw_set_alpha(1);

With these few lines of code, we now display the 400 cells (20 * 20, I'm a math whiz, but this is good) of our level:

  • draw_set_alpha and draw_set_opacity allow you to change the opacity and color of the lines
  • The two loops for go through the grid
  • And draw_line draws horizontal and vertical lines at each iteration
As promised, a 20×20 grid with GameMaker

Adding walls in the level

Let's add elements to our level and then transfer them to our grid. I propose that we add walls: to do this, we need to create a oWall and assign it a sprite. For this tutorial, I advise to use a sprite of 32px by 32px.

Then use the room editor in Game Maker to place the object in question in the level. To make it easier let's set the grid size to 32px and use the snapThis way the elements will always be positioned on a multiple of 32.

The "snap" option in GameMaker Studio

At this point we have a grid drawn and objects in our level, but the two are not linked. Indeed, the objects are present in the room, but at no time have we added them to our global.grid. I don't want to keep the suspense going any longer, that's what we'll do now.

Inserting the walls in the grid

// Create event oWall

// Array position
var _x = x / oGrid.boxSize;
var _y = y / oGrid.boxSize;

// Add id to the array
array_set(global.grid[_x], _y, id);

Congratulations, every object oWall of our room is now added to our grid when it is created. Be careful, for this to work, make sure that the oGrid is created before your objects oWall in the order of creation of your room, otherwise you will try to access a variable that does not yet exist.

Our level is now surrounded by walls

Add objects on several squares at the same time

Let's imagine a world where one of your walls is 32px wide and 16px high (incredible, I know). With our code, it will be added only on the square where it originates, and not on all the squares it overlaps. Here's how to fix this problem:

// Create event oWall

// Array position
var _x = x / oGrid.boxSize;
var _y = y / oGrid.boxSize;

// Obj size
var _width  = sprite_width / oGrid.boxSize;
var _height = sprite_height / oGrid.boxSize;

// Add the id of the object on the grid
for (var i = 0; i < _width; ++i) { 
    for (var j = 0; j < _height; ++j) {
        // Add id to the array
        array_set(global.grid[i + _x], j + _y, id);
    }
}
  • We calculate the size of the object in relation to its variables sprite_width and sprite_height
  • We translate this into number of cells with / oGrid.boxSize
  • We loop on these and modify array_set() to add the object on the boxes in question

Detect the walls contained in the grid

That's great, now our grid contains the ID of the objects added to the room. Let's try to modify our code to view the grid to show the boxes blocked by walls.

// Draw event oGrid

// Set opacity & color
draw_set_alpha(.5);
draw_set_color(c_green);

// Loop trought each row and column
for (var i = 0; i < gridWidth; i+=1){
    draw_line_width(0, i*boxSize, room_width, i*boxSize, 1);
}

for (var j = 0; j < gridHeight; j+=1) {
    draw_line_width(j*boxSize, 0, j*boxSize, room_height, 1);
}

// Display values
for (var i = 0; i < gridWidth; i+=1) {
    for (var j = 0; j < gridHeight; j+=1) {
        var _value = array_get(global.grid[i], j);
        if(_value != 0) {
            draw_set_color(c_red);
            draw_rectangle(i * boxSize, j * boxSize, i * boxSize + boxSize, j * boxSize + boxSize, false);
        }

    }
}

// Reset opacity & color
draw_set_color(c_white);
draw_set_alpha(1);
  • Our table is browsed with the loops for
  • Then the values contained for each iteration are retrieved with array_get(global.grid[i], j)
  • And finally, if it is different from 0 which is our base value, we display a red rectangle with draw_rectangle()
The red rectangles represent the walls stored in our table

Conclusion

We have a grid, which contains the walls of our room, and we know how to detect them. And that's it for this tutorial! Here is what you can do next:

  • Add more than one object per box
  • Destroy a wall by clicking on the right square
  • Adding elements on the fly
  • Move a character square by square, checking for collision
  • Or use path finding algorithms to ensure that a path is available

If you have any questions about this tutorial or a specific need, let me know in comments so we can talk about it together.

See you later!

Clément 'Indieklem' Jacquelin - Author
Clément "Indieklem" Jacquelin

Leave a comment

Your email address will not be published. Required fields are marked with *.