Turn-Based Board Game Using DS Grids

Hey there! In this tutorial, you’ll learn how to create a turn-based board game using DS grids.


This is what we’ll create in this tutorial:

0.gif

Using this method, you can even build a Chess game. Here is one I made, which is free to look at on GitHub:

chess_trailer.gif


In this project, there will be two teams. The board will contain different pieces, with each piece belonging to either team, like Chess.



DS Grids

DS Grids are 2D data structures that contain a certain number of cells, which are determined by the width & height of the grid. Each cell can hold a value.

A grid can be created using ds_grid_create(w, h), where w and h are the width & height of the grid.

All cells in a new grid hold 0. If you want to access or set a value in the grid, you can do it like you would with a 2D array, but using the # accessor:

//Writing
grid[# x, y] = value;
//x, y is the cell position to access,
//where 0, 0 would be the top-left cell

//Reading
value = grid[# x, y];

Read more about grids here.



Concept


Board (Grid) & Pieces

First we’ll set up the pieces inside an enum. Then we’ll create a DS grid for the main board.

That grid, in each cell, will contain an array. The arrays will contain only two elements: the first one will store the piece type (from the enum), and the second element will contain the team that piece belongs to (0 or 1).

So, if a cell in that grid is empty, its value will be 0. But if there is a piece at a cell, it will hold an array with the two aforementioned elements.


Turns & States

Both sides will take turns moving their pieces. A variable called turn will store whose turn it is (0 or 1). When it is a team’s turn, they will be able to move one of their pieces to some other cell.

There will be another variable called state. When a side is moving a piece, this variable will keep track of where that action has progressed. In our project, there will be two states: 0 and 1.

So, it will start at 0. When the player clicks on any piece, it will check whether that piece belongs to that team. If it does, it will store that cell’s info (location, piece type) to some variables, and set the state to 1.

When the state is 1 and the player clicks on a cell, it will check whether that cell is empty. If it is, it will delete the piece at its original location, and move it to the new cell.

After that, the other team will be given their turn, and state will be reset to 0.

This way a team will be able to move one piece each turn. It will go something like this:

gridbased.png



Project


Now let’s start making the actual project. I’ve imported sprites for 3 example pieces: circle, square, and triangle.

0.PNG


Setting up the board (Create)


I’ll start by creating an object called oBoard. In its Create event, I’ll add this:

//Array contents
enum ar{
    piece,
    team
}

//Pieces
enum piece{
    circle,
    square,
    triangle
}

pieceSprite[piece.circle] = sCircle;
pieceSprite[piece.square] = sSquare;
pieceSprite[piece.triangle] = sTriangle;

First I’m setting up an enum called ar, that will contain two members: piece and team. As you might have guessed, this will be used in the arrays that will be in the grid cells.

Then I’m setting up the pieces in an enum called piece. The array pieceSprite will store the sprite for each piece.

In this example, we’ll only be needing the sprite for each piece, but if you want to set up more data for them (like health, attacks, etc.), you can create more arrays like this, or use a 2D array.

I’ll continue by adding this in the Create event:

//Board
cellSize = 64;
boardW = 8;
boardH = 8;

board = ds_grid_create(boardW, boardH);

//Vars
turn = 0;
state = 0;

selectedX = -1;
selectedY = -1;
selectedPiece = -1;

//Board
cellSize is the size of each square cell in pixels.

boardW & boardH store how many cells there are in the board, horizontally and vertically, respectively. So, our board will have 8×8 cells (64).

Then the board grid will be created in the variable board. The arguments in the function are the grid width & height.

//Vars
turn will store the side whose turn it is, and state will store the state of a turn.

The selected variables will be used to store the properties of the piece that would be selected during state 0, so that they could be moved to a new cell in state 1. They are the x and y position of the selected cell, and its piece type.

Now we need to place the starting pieces for each side, so I’ll add this:

//Place pieces
for(var i=0; i<boardW; i++){
    //Team 0
    var aPiece = choose(piece.circle, piece.square, piece.triangle);
    var aTeam = 0;

    board[# i, 0] = [aPiece, aTeam];
   
    //Team 1
    var aPiece = choose(piece.circle, piece.square, piece.triangle);
    var aTeam = 1;
   
    board[# i, boardH-1] = [aPiece, aTeam];
}

This loop will go from 0 to the last cell on the horizontal axis (using boardW, the width of the board). It will place a piece for each side.

The //Team 0 part will place the piece for the first team. It will get a random piece type using choose() in aPiece. The team value for that cell will be 0, stored in aTeam, because this is the first team.

It will set the cell at [i, 0]. i is the horizontal cell position that will change with the loop, and the vertical cell position is 0 to place the piece at the top of the board. An array is being set to that cell, with two elements: [aPiece, aTeam]. The piece type is in the first place, and the team in the second.

Array declaration doesn’t work like this in versions before GMS2. So, if you are using an older version, you’d have to apply the array like this:

var arr;
arr[ar.piece] = aPiece;
arr[ar.team] = aTeam;

board[# x, y] = arr;

The //Team 1 part will place the piece for the second team. Everything in it is similar to the the //Team 0 part, the only difference being the y position of the cell. Instead of 0, it is boardH-1. This will place the piece at the bottom of the board.

To get the last cell in a grid, you have to subtract 1 from its size, because whereas the size starts at 1, the cell coordinates start at 0. So, there’s a difference of -1.

So this code will place the pieces for each side, like this:

2.PNG

Of course, you can’t see anything when you run the game, because we haven’t set up the drawing yet. Let’s focus on the game mechanics first.


Turn-Based Gameplay (Step)


We’ll set up the turn-based gameplay in the Step event. So, I’ll add this in it:

//Mouse position
mouseX = mouse_x - x;
mouseY = mouse_y - y;
mouseCellX = mouseX div cellSize;
mouseCellY = mouseY div cellSize;

mouseIn = point_in_rectangle(mouseX, mouseY, 0, 0, boardW*cellSize, boardH*cellSize);

var mousePress = mouse_check_button_pressed(mb_left);

mouseX and mouseY will get the position relative to the board, by subtracting its position from the mouse’s coordinates.

mouseCellX and mouseCellY will get the cell the mouse is on, by dividing the relative mouse coordinates by the cell size.

div is integer division, so the result is floored.

If you do 5/2, you get 2.5.
But if you do 5 div 2, you get 2, because floor(2.5) = 2.

mouseIn will store whether the mouse is inside the board, by checking whether a point (the mouse cursor) is inside a rectangle (the board).

mousePress will simply store whether the left mouse button is pressed.

Now I’ll add this:

//Press
if (mouseIn && mousePress){
    //State 0
    if (state==0){
        //Get array
        var arr = board[# mouseCellX, mouseCellY];
       
        if (is_array(arr)){
            //Get info
            var aPiece = arr[ar.piece];
            var aTeam = arr[ar.team];
       
            //Accept
            if (aTeam==turn){
                //Save selected cell
                selectedX = mouseCellX;
                selectedY = mouseCellY;
                selectedPiece = aPiece;
               
                //State
                state = 1;
            }
        }
    }
    //State 1
}

//Press
This body of code will only run if mouseIn && mousePress, that is, if the mouse is inside the board and the left mouse button is pressed.

//State 0
First we’re setting up state 0. So, if the state is 0, the code inside its block will run.

//Get array
It will get the array from the cell where the mouse cursor is. But, we don’t know for sure whether there will be an array in the cell – what if it’s empty? So, we’ll check that using the function is_array(). It’ll make sure that the value retrieved from the cell is an array, and if it is, it’ll run the code inside its block.

//Get info
It will get the piece type & team from the array and store them in local variables.

//Accept
It will check whether that piece belongs to the side whose turn it currently is.

//Save selected cell
If that piece does belong to the playing side, then the location of the cell and the piece type will be stored in the selected variables.

//State
The state will be set to 1.

Now, the code for state 0 is set up. You can select the pieces. Let’s set up state 1 now, at the place where the //State 1 comment is placed:

    //State 1
    else if (state==1){
        //Get array
        var arr = board[# mouseCellX, mouseCellY];
       
        //If empty, accept
        if (arr==0){
            //Empty selected cell
            board[# selectedX, selectedY] = 0;
           
            //Move to new place
            board[# mouseCellX, mouseCellY] = [selectedPiece, turn];
           
            //State
            state = 0;
           
            //Turn
            turn = !turn;
           
            //Reset selected
            selectedX = -1;
            selectedY = -1;
            selectedPiece = -1;
        }
    }
//State 1
This block of code will only run if the state is 1.

//Get array
Again, it will get a cell array from the board, using the mouse’s cell coordinates.

//If empty, accept
We need to make sure that the cell is empty before proceeding. So, we’ll check whether it is 0. The code following this condition will only run if that cell is empty.

//Empty selected cell
It will empty the cell that was selected in state 0, by setting it to 0.

//Move to new place
It will create a new array with selectedPiece as the piece type and the current turn as the team. That array will be applied to the cell that is at the mouse’s position.

So, by erasing the original cell and applying that cell’s values to the new one, the piece is moved.

//State
The state is reset back to 0.

//Turn
turn is set to !turn. Using ! before a boolean returns the opposite of it. Since our turn variable is basically a boolean (with 0/false and 1/true as its possible values), it can be flipped this way. So, this will set the turn to the other side.

//Reset selected
This will reset the selected variables back to -1.

Now, both of the states have been set up. Let’s set up the drawing.


Drawing


I’ll add the Draw event, and add this there:

//Draw board
for(var yy=0; yy<boardH; yy++){
    for(var xx=0; xx<boardW; xx++){
        //Cell position
        var cX = x + xx*cellSize;
        var cY = y + yy*cellSize;
     
        //Alpha
        var alpha = 0.2;
        if (mouseCellX==xx && mouseCellY==yy){
            alpha = 0.4;
        }
       
        //Color
        var color = c_white;
        if (selectedX==xx && selectedY==yy){
            color = c_green;
        }
       
        //Draw cell
        draw_set_alpha(alpha);
        draw_set_color(color);
        draw_rectangle(cX+1, cY+1, cX + cellSize-1, cY + cellSize-1, 0);
        draw_set_alpha(1);
        draw_set_color(c_white);
       
        //Get array
        var arr = board[# xx, yy];
       
        if (is_array(arr)){
            var aPiece = arr[ar.piece];
            var aTeam = arr[ar.team];
           
            var spr = pieceSprite[aPiece];
            var sprColor = c_white;
           
            if (aTeam==1)
                sprColor = c_black;
               
            draw_sprite_ext(spr, 0, cX + cellSize/2, cY + cellSize/2,
                1, 1, 0, sprColor, 1);
        }
    }
}
Since a grid is 2-dimensional, we have to run two loops, one for each axis. So, the first one will run on the y axis, with its loop variable being yy, and the second one will run on the x axis, with its loop variable being xx.

//Cell position
This will calculate the position of the cell in the room, by multiplying the loop variables (xx, yy) with cellSize, and adding it to the board instance’s position.

//Alpha
This part will set up the alpha for the cell rectangle.

The alpha will normally be 0.2. But if the mouse cell coordinates are equal to that cell’s coordinates, which means that the mouse is hovering over that cell, the alpha will be changed to 0.4.

//Color
This part will set up the color for the cell rectangle.

The color will normally be c_white. If the position of the selected cell is equal to this cell’s position, which means that that cell was selected in state 0, the color will be changed to c_green. This way you’ll know which cell was selected.

//Draw cell
This part will draw the cell rectangle.

First, the alpha and the color will be set to the variables we created above. Then the rectangle will be drawn. You’ll notice that 1 is being added to the rectangle’s starting position and subtracted from its ending position, to give the cell a margin.

Then the alpha and color will be reset to their default values.

//Get array
This part will get the piece info from the cell array (if any) and draw its sprite.

arr will get the cell value. Then using is_array() it’ll check whether it is, well, an array.

aPiece and aTeam will get the piece properties from the array.

spr will get the sprite for that piece, from the array we set up in the Create event.

sprColor will be the blend color of the piece. It will be white for team 0, and black for team 1. So it will be declared at c_white. If the team is 1, it’ll be changed to c_black.

The draw_sprite_ext() function will draw the piece sprite at the center of the cell, by adding half the cell’s size to the cell’s position. So, make sure the origin of the piece sprites is centered as well.

I am using the sprColor variable in the color argument of that function to apply the blend color to the piece sprite.

You can also add this in the Draw event to draw whose turn it is:

draw_text(8, 8, "Turn: " + string(turn));

Testing


Now the main game has been set up, so you can place the board object in a room and run the game.

0


Cancelling Moves


What if you want to cancel a move, or in other words, deselect a cell? Here’s what I’ll add in the Step event for cancelling a move using the right mouse button:

//Canceling
if (mouse_check_button_pressed(mb_right) && state==1){
    state = 0;
   
    selectedX = -1;
    selectedY = -1;
    selectedPiece = -1;
}

If the right mouse button is pressed and the state is 1 (which means that a cell has been chosen in state 0), the state will be set to 0, and the selected variables will be reset.


Piece Behavior


In this project, any piece can move anywhere on the board. But what if you want to do something like Chess, where each piece has its own set of conditions for moving? You can easily do that in state 1.

In that state, we only check whether a cell is empty. There you can add another condition to the mix to have more factors that determine whether a piece can be moved to a cell.

You can run a switch statement on the selectedPiece variable (that stores the piece type of the selected cell), and check whether that piece would be able to move to that particular cell, by assigning either true or false to a variable. If that variable is true, only then move the piece to that cell.

For example; a pawn can only move one (or two) steps at a time, only towards one direction, whereas a rook can only move either horizontally or vertically.


Conclusion


You can download the project here (GMS2)

Make sure you check out the homepage of this site to read more tutorials for GameMaker. I regularly update my site with new ones as well, so make sure you follow now (at the bottom of the page) to stay updated!

follow.gif

If you need free GML help or want to provide GML help to those who need it, join our Discord server! We have a fun little community there (and a fun bot); it’ll be good to have you too!

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

Advertisements

One thought on “Turn-Based Board Game Using DS Grids

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