Sliding Tile Puzzle - Collision Bounds and Tileset
Posted: Mon Aug 22, 2016 11:13 pm
Hi all,
I'm trying to create a sliding tile puzzle - ie. an image made of various tiles with one blank space that must be rearranged into the correct configuration. Here is an example.
I'm new to Love2D so have been working my way through the various tutorials. In particular the gridlocked player example, the 0-1 collision system described here, and the tileset loading example using quads in the first iteration found here.
Now i'm trying to combine these concepts to get closer to what I want. I rewrote the keypressed code to use mousepressed so that the tiles swap when clicked on but I have two issues/queries:
Firstly, i'm getting an 'attempt to index a nil value' on my left and right detection calls (presumably an out of bounds exception) in the mousepressed function when clicking certain tiles near the edges of the set from the following code:
Secondly, as the above 'map' table essentially only serves as a collision map, I want to combine it with a tilemap of different images.
I'm not sure if it's the best approach, but I thought to have a second table to hold this info. I know the format of this can be made more efficient than using numerical values (such as using strings and ipairs) but for now I just want to get it working this way.
However, i'm not sure how I would update the graphics similar to how I swap the collision map values, or indeed if there is a simpler approach to this? I did try using the tile_x, tile_y values from the collision check to look up the corresponding values in the timely table, store them, and swap them but it seemed to affect tiles elsewhere.
I would be grateful for any help, thank you
I'm trying to create a sliding tile puzzle - ie. an image made of various tiles with one blank space that must be rearranged into the correct configuration. Here is an example.
I'm new to Love2D so have been working my way through the various tutorials. In particular the gridlocked player example, the 0-1 collision system described here, and the tileset loading example using quads in the first iteration found here.
Now i'm trying to combine these concepts to get closer to what I want. I rewrote the keypressed code to use mousepressed so that the tiles swap when clicked on but I have two issues/queries:
Firstly, i'm getting an 'attempt to index a nil value' on my left and right detection calls (presumably an out of bounds exception) in the mousepressed function when clicking certain tiles near the edges of the set from the following code:
Code: Select all
function love.load()
map = {
{ 1, 1, 1, 1 },
{ 1, 1, 1, 1 },
{ 1, 1, 1, 1 },
{ 1, 1, 1, 0 }
}
end
function love.update(dt)
end
function love.draw()
-- highlight solid tiles
for row=1, #map do
for column=1, #map[row] do
if map[row][column] == 1 then
love.graphics.rectangle("line", row * 32, column * 32, 32, 32)
end
end
end
-- print some debugging information
local grid_x, grid_y = getTile() -- mouse cursor location, snapped to grid
print_info(grid_x, grid_y) -- print grid position
end
function love.mousepressed(x, y, button)
if button == 1 then
-- if left mouse button pressed, get tile coordinates
local tile_x, tile_y = getTile()
-- check we have clicked on a solid tile
if(map[tile_x][tile_y] == 1) then
-- if there is space above
if (map[tile_x][tile_y-1] == 0) then
map[tile_x][tile_y-1] = 1
map[tile_x][tile_y] = 0
-- if there is space below
elseif (map[tile_x][tile_y+1] == 0) then
map[tile_x][tile_y+1] = 1
map[tile_x][tile_y] = 0
-- if there is space to the left
elseif (map[tile_x-1][tile_y] == 0) then
map[tile_x-1][tile_y] = 1
map[tile_x][tile_y] = 0
-- if there is space to the right
elseif (map[tile_x+1][tile_y] == 0) then
map[tile_x+1][tile_y] = 1
map[tile_x][tile_y] = 0
end
end
end
end
function getTile()
local mouse_x, mouse_y = love.mouse.getPosition()
-- convert cursor coordinates to grid coordinates
mouse_x = math.floor(mouse_x/32)
mouse_y = math.floor(mouse_y/32)
return mouse_x, mouse_y
end
function print_info(grid_x, grid_y)
love.graphics.setColor(255, 0, 0)
love.graphics.rectangle("line", grid_x * 32, grid_y * 32, 32, 32)
love.graphics.setColor(255, 255, 255)
love.graphics.print(grid_x .." , "..grid_y, grid_x * 32, grid_y * 32)
end
I'm not sure if it's the best approach, but I thought to have a second table to hold this info. I know the format of this can be made more efficient than using numerical values (such as using strings and ipairs) but for now I just want to get it working this way.
However, i'm not sure how I would update the graphics similar to how I swap the collision map values, or indeed if there is a simpler approach to this? I did try using the tile_x, tile_y values from the collision check to look up the corresponding values in the timely table, store them, and swap them but it seemed to affect tiles elsewhere.
Code: Select all
function love.load()
map = {
{ 1, 1, 1, 1 },
{ 1, 1, 1, 1 },
{ 1, 1, 1, 1 },
{ 1, 1, 1, 0 }
}
Tileset = love.graphics.newImage("/assets/countryside.png") -- load the tileset
TileW, TileH = 32, 32 -- global tile width and height
local tilesetW, tilesetH = Tileset:getWidth(), Tileset:getHeight() -- tileset dimensions
-- define the tile images from the tileset using quads: tile left(x),tile top(y), width, height, tilset width, tileset height
Quads = {
love.graphics.newQuad(0, 0, TileW, TileH, tilesetW, tilesetH), -- 1 grass
love.graphics.newQuad(32, 0, TileW, TileH, tilesetW, tilesetH), -- 2 box
love.graphics.newQuad(0, 32, TileW, TileH, tilesetW, tilesetH), -- 3 flowers
love.graphics.newQuad(32, 32, TileW, TileH, tilesetW, tilesetH) -- 4 boxtop
}
tilemap = {
{ 3, 2, 3, 4 },
{ 3, 3, 3, 4 },
{ 4, 2, 3, 4 },
{ 4, 2, 2, 1 }
}
end
function love.update(dt)
end
function love.draw()
-- display the graphic tiles from the timely
for rowIndex = 1, #tilemap do -- for each 'row' of tilemap
local row = tilemap[rowIndex] -- store the current row index (row)
for columnIndex = 1, #row do -- for each 'column' of the row
local number = row[columnIndex] -- store the current column index (number)
local x = (columnIndex)*TileW -- get x coordinate using column and tile width
local y = (rowIndex)*TileH -- get y coordinate using row and tile height
-- draw the relevant Tile Type from the Tileset, at the x and y
love.graphics.draw(Tileset, Quads[number], x, y)
end
end
-- highlight solid tiles
for row=1, #map do
for column=1, #map[row] do
if map[row][column] == 1 then
love.graphics.rectangle("line", row * 32, column * 32, 32, 32)
end
end
end
-- print some debugging information
local grid_x, grid_y = getTile() -- mouse cursor location, snapped to grid
print_info(grid_x, grid_y) -- print grid position
end
function love.mousepressed(x, y, button)
if button == 1 then
-- if left mouse button pressed, get tile coordinates
local tile_x, tile_y = getTile()
-- check we have clicked on a solid tile
if(map[tile_x][tile_y] == 1) then
-- if there is space above
if (map[tile_x][tile_y-1] == 0) then
map[tile_x][tile_y-1] = 1
map[tile_x][tile_y] = 0
-- if there is space below
elseif (map[tile_x][tile_y+1] == 0) then
map[tile_x][tile_y+1] = 1
map[tile_x][tile_y] = 0
-- if there is space to the left
elseif (map[tile_x-1][tile_y] == 0) then
map[tile_x-1][tile_y] = 1
map[tile_x][tile_y] = 0
-- if there is space to the right
elseif (map[tile_x+1][tile_y] == 0) then
map[tile_x+1][tile_y] = 1
map[tile_x][tile_y] = 0
end
end
end
end
function getTile()
local mouse_x, mouse_y = love.mouse.getPosition()
-- convert cursor coordinates to grid coordinates
mouse_x = math.floor(mouse_x/32)
mouse_y = math.floor(mouse_y/32)
return mouse_x, mouse_y
end
function print_info(grid_x, grid_y)
-- highlight the tile under the mouse
love.graphics.setColor(255, 0, 0)
love.graphics.rectangle("line", grid_x * 32, grid_y * 32, 32, 32)
-- print the tile row and column numbers
love.graphics.setColor(255, 255, 255)
love.graphics.print(grid_x .." , "..grid_y, grid_x * 32, grid_y * 32)
end