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)
function round(n)
	return n % 1 >= 0.5 and math.ceil(n) or math.floor(n)

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!



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

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 = {}


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

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)

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