Grid Snap workaround

Hi folks. As far as I’ve know there is currently no way in the Editor to snap objects to a grid. Tilemaps might work as an alternative in some cases, but not always. I’ve done some googling and apparently it is a feature that has been requested a few times, so I thought I could share my own workaround. Please bear in mind that I’m still new to Defold, so this is a really simplistic fix. Its good enough for me, but it might not work in some special cases and there are probably better ways to do it.

local cellSize = 32
function init(self)
	local posX = go.get(".", "position.x")
	local posY = go.get(".", "position.y")
	go.set(".", "position.x", round(posX/cellSize) * cellSize)
	go.set(".", "position.y", round(posY/cellSize) * cellSize)
end
function round(n)
	return n % 1 >= 0.5 and math.ceil(n) or math.floor(n)
end

Take into account this only works when objects are created, if you need to align them again while running the game you need more code for that. This just saves time while fiddling with dirty prototypes by not having to write exact coordinates for each object. Thanks to user Gmanicus as I got the ROUND function from him.

EDIT: added a couple of pictures to show what it does.


EDIT2: Look below for a much better version by Britzl himself!

7 Likes

Thank you for sharing. Some things to consider:

  • I’d probably make the round() function local and declare it above the init(). If it is global you can’t be certain that there aren’t other scripts also also declaring a round function that may or may not have a different behaviour.
  • While it is perfectly fine to use go.get(".", “position.x”) and go.set() to get and set x and y position I’d probably still use go.get_position() and go.set_position()
local pos = go.get_position()
pos.x = round(pos.x/cellSize) * cellSize
pos.y = round(pos.y/cellSize) * cellSize
go.set_position(pos)

The difference in performance is probably negligible but I find my version more readable.

Another improvement might be to stick this into a Lua module:

-- snap.lua
local M = {}

M.CELL_SIZE = 32

local function round(n)
	return n % 1 >= 0.5 and math.ceil(n) or math.floor(n)
end

function M.snap(id)
    local pos = go.get_position(id)
    pos.x = round(pos.x/M.CELL_SIZE) * M.CELL_SIZE
    pos.y = round(pos.y/M.CELL_SIZE) * M.CELL_SIZE
    go.set_position(pos, id)
end

return setmetatable(M, { __call = function(t, ...)
   return  M.snap(...)
end })

And use it like this:

local snap = require "some.path.snap"

function init(self)
    snap() -- snap the current game object
    snap(".") -- same

    snap("enemy") -- snap some other game object
end
8 Likes

The solutions here are great, but I just wanted to add that it won’t work for static collision objects. You can’t move a static object after it’s been created, so you need to create it at the correct location.

My solution was to use Tiled for grid based level editing. Defold doesn’t parse object layers automatically, but parsing the object layers yourself is pretty easy if you export a .lua file from Tiled.

2 Likes