Creating a 2D grid for sokoban clone

Godot Version

Godot 4.5

Question

I’m trying to create a sokoban and create my own 2D grid system for it. I’m really struggling on how to go about doing it. I’ve looked at some code github projects and I got the idea that I can create the logic of the grid without using tilemap layers but I don’t just want to copy and paste someone else’s code. So yeah I’m a bit stuck, maybe I should just copy and paste and see if it works.

Any thoughts?

Or even any resources that might point me in the right direction would be helpful.

Thanks

Maybe this will help: Tile Map - True Top-Down 2D

I’m not sure why you don’t want you use a time map… Maybe you can describe what you are trying you do and why in a bit more detail?

What part of using TileMapLayers are you having trouble with?

So from what I’ve seen (and correct me if I’m wrong), you can seperate the logic from the visuals by having a GridManager (logic) and then using TileMapLayers just for the visuals.

So with Sokoban, the GridManager would handle all the logic e.g can the player move to a tile, with the TileMapLayer just providing the visuals for things.

Is that the right approach?

Yes, that sounds right to me. You could put all the logic on the TileMapLayer but then the script might get too long.

Personally, I’d be inclined to use a tilemap for the floor, but use sprites for the boxes. That’s certainly how it would have been done in the old school hardware that tiles/sprites are emulating.

This is very basic description of what a pure ‘logical’ grid based on numbers/math may look like (i.e. without using grid container or such):

You will basically end up with two coordinate systems:

  1. Your grid units, how many cells width and height (e.g. 10 x 10) .
  2. The real pixels to which your grid should translate.

Here is a simplified example of how this might work.

const GRID_WIDTH: int = 10 # ← units
const GRID_HEIGHT: int = 10 # ← units
const TILE_SIZE: int = 150 # ← pixels

You have a main screen (or game screen) and your board on it. It may have the following node structure:

Main (Control)
– BoardGrid (Control)
– – Tiles (Node2D)
– – – Tile instance 1(Control)
– – – Tile instance 2 (Control)
etc.

You should set custom min size for the BoardGrid node:

func _ready() -> void:
# assuming you reference BoardGrid node in the code
var grid_px := Vector2(GRID_WIDTH * TILE_SIZE, GRID_HEIGHT * TILE_SIZE)
BoardGrid.custom_minimum_size = grid_px

Center the BoardGrid node or align it otherwise - this is a Control node so you can use anchors.

You should have a separate scene for a tile which should also be a Control node. Set the custom min size for this node to your TILE_SIZE. Instantiate the tiles within the Tiles ‘folder’ under the BoardGrid using a nested loop. The pixel coordinates of each tile (its top-left corner) should be derived from a function, e.g :

func grid_to_pixel(pos: Vector2i) -> Vector2:
return Vector2(pos.x, pos.y) * TILE_SIZE

That is, you feed your tile position, e.g. (1, 1) to this function and it returns the pixel position coordinates relative to the BoardGrid node (your tiles are within the coordinate space of BoardGrid, so it’s top-left corner is the origin). So for each tile Godot takes the position of its top-left corner from the grid_to_pixel function, and the size of it from custom_min_size set for the tile scene/class. These two numbers are used to position the tile in the grid in the real (pixel) coordinates.

That’s pretty much it. BoardGrid is responsible for global positioning, the grid_to_pixel function and your nested loop tile spawn function are reponsible for positioning the tiles within the BoardGrid. The tile scene is responsible for the texture and other properties of the tile itself.

Hope this helps.