Tutorial: Working With a Grid Layout

Tutorial: Working With a Grid Layout

Many games use a grid where pieces or items are positioned in rows and columns. Classic examples would be chess or checkers, but even more modern games like Candy Crush™ position the pieces in a grid as well.

Mechanically, a grid is nothing more than a two-dimensional array, and each element in the array represents a single spot on the grid. In Lua, two-dimensional arrays are basically a table in which each child table represents a row for the grid, and each entry in that row table represents a column index within the row.

Let’s look at the core Lua code used in a demo project which you should download and experiment with alongside this tutorial.

local GRID_WIDTH = 8
local GRID_HEIGHT = 8
local CELL_WIDTH = 40
local CELL_HEIGHT = 40
--
-- Create a 2D array to hold our objects.
local grid = {}
for i = 1, GRID_HEIGHT do
grid[i] = {}
end
--
-- load the background image:
local checkerBoard = display.newImageRect("table.png", 320, 320)
checkerBoard.x = display.contentCenterX
checkerBoard.y = display.contentCenterY
--
-- Calculate some values
--
local gbOffsetX = checkerBoard.x - ( checkerBoard.width * checkerBoard.anchorX )
local gbOffsetY = checkerBoard.y - ( checkerBoard.height * checkerBoard.anchorY )
--
-- Using positions 1, 8 for X and Y, draw the object at the right place in the grid
--
local function spawnPiece( xPos, yPos, pieceType )
if pieceType ~= "red" and pieceType ~= "white" then
print( "Invalid piece type", pieceType )
return nil
end
if xPos < 1 or xPos > GRID_WIDTH or yPos < 1 or yPos > GRID_HEIGHT then
print( "Position out of range:", xPos, yPos )
return nil
end
local piece = display.newImageRect( "token_" .. pieceType .. ".png", CELL_WIDTH, CELL_HEIGHT )
--
-- record the pieces logical position on the board
--
piece.xPos = xPos
piece.yPos = yPos
--
-- Position the piece
--
piece.x = (xPos - 1) * CELL_WIDTH + (CELL_WIDTH * 0.5) + gbOffsetX
piece.y = (yPos - 1) * CELL_HEIGHT + (CELL_HEIGHT * 0.5) + gbOffsetY
return piece
end
local function movePiece(piece, xPos, yPos )
-- check to see if the position is occupied. You can do either:
-- 1. "Capture the piece". This would involve removeing the piece
-- that is there before moving to the spot or
-- 2. "Reject the move" because the spot is occupied. For the purpose
-- of this tutorial we will reject the move.
--
if xPos < 1 or xPos > GRID_WIDTH or yPos < 1 or yPos > GRID_HEIGHT then
return false
end
if grid[yPos][xPos] == nil then -- got an empty spot
--
-- get the screen x, y for where we are moving to
--
local x = (xPos - 1) * CELL_WIDTH + (CELL_WIDTH * 0.5) + gbOffsetX
local y = (yPos - 1) * CELL_HEIGHT + (CELL_HEIGHT * 0.5) + gbOffsetY
--
-- save the old grid x, y
--
local oldXPos = piece.xPos
local oldYPos = piece.yPos
--
-- Move the object in the table
--
grid[yPos][xPos] = piece
grid[yPos][xPos].xPos = xPos
grid[yPos][xPos].yPos = yPos
grid[oldYPos][oldXPos] = nil
--
-- Now move the physical graphic
--
transition.to(grid[yPos][xPos], { time = 500, x = x, y = y})
return true
end
end
--
-- Generate a few objects
--
--
-- a holding piece for moving the object
--
local lastObject
for i = 1, 10 do
local xPos = math.random( 1, GRID_WIDTH )
local yPos = math.random( 1, GRID_HEIGHT )
local color = "red"
if math.random(2) == 2 then
color = "white"
end
grid[yPos][xPos] = spawnPiece(xPos, yPos, color, checkerBoard)
lastObject = grid[yPos][xPos]
end
timer.performWithDelay(2000, function() movePiece( lastObject, 4, 5); end, 1)
view raw gistfile1.lua hosted with ❤ by GitHub

First, we define some constants: the number of rows and columns as GRID_WIDTH and GRID_HEIGHT respectively. We also define the width and height of each cell on the grid as CELL_WIDTH and CELL_HEIGHT. Following this, we create an empty table for the overall grid, then we execute a loop for the total number of rows in the grid (GRID_HEIGHT) and, in that index position, we create another empty table which will represent the overall row.

Next, we place the grid’s image on the screen and position it in the center of the content area. Then we create two more constants for offsetting pieces relative to the image. Here, we calculate gbOffsetX and gbOffsetY based on the grid image’s location and size.

Referencing Cells

Because we have effectively created a “stack of rows,” a specific cell must be referenced in a [row][column] manner as follows:

local somePiece = grid[4][5]

This code will reference the cell 5 positions from the left (5th column) and 4 positions from the top (4th row). Thus, the row value comes first and the column value second.

Now conceptually, it may be more logical to reference cell positions as an x,y coordinate position, or in other words, a “column, row” format. For example, the top-left space on a checkers board would be 1,1 and the top-right space would be 8,1 (remember that checkers uses an 8×8 board). Thus, when calling the spawnPiece() function, two parameters, xPos and yPos, are expected to represent the position in the grid.

Spawning Pieces

Within the actual spawnPiece() function, we first validate the parameters passed in. This includes checking the piece type and ensuring that the cell position is not outside the range (size) of the grid. If the validation succeeds, we generate the piece’s display object and store the piece’s position on the board as attributes of that object. Finally, we place the piece in the actual screen position (pixel coordinates) based on the constants we defined earlier.

Moving Pieces

The next function is used to move a piece. In this example, we test this function by calling it after a timer of 2 seconds, but this would likely be done via a touch, swipe, selecting the piece and tapping its destination, or any number of other methods.

In any case, within the movePiece() function, we first validate that the requested position is within range of the grid and confirm that the position is not occupied by another piece. If the validation passes, we move the piece’s position within the grid table and then visually slide (transition) the piece to the new position.

Conclusion

As shown in this tutorial, it’s simple to use a two-dimensional array to create a grid, store the position of pieces within the array, and use basic math to position and move the visual pieces based on logical coordinates. Remember to download the demo project to experiment with and edit for use in your own grid-based app project.


Tags:
,
Rob Miracle
[email protected]

Rob is the Developer Relations Manager for Corona Labs. Besides being passionate about helping other developers make great games using Corona, he is also enjoys making games in his spare time. Rob has been coding games since 1979 from personal computers to mainframes. He has over 16 years professional experience in the gaming industry.

1Comment
  • J. A. Whye
    Posted at 02:53h, 09 April

    A while back I created a utility that helped me with a grid-based problem. Given any location in any sized grind, I wanted to know which grid squares were around it (left, right, up, down, and diagonals).

    I ended up with the code you can see here:
    http://masteringcoronasdk.com/free-corona-sdk-tutorial/wont-you-be-my-grid-neighbor/

    There’s probably some cool math-based way that makes the solution a one-liner, but until someone posts that, feel free to use that code if it helps solve your problem.

    Jay

The website is no longer being updated, and is for archival purposes only.

 

Corona SDK is now Solar2D

https://Solar2D.com/

×