What I mean by using the built-in system is that I am using collision objects in Defold:
I then set a physics listener and run this script to apply accumulated adjustments:
local CONTACT_POINT_EVENT = hash("contact_point_event")
local PLAYER = hash("player")
local UNIT = hash("unit")
local OBSTACLE = hash("obstacle")
local crowd_adjustments = {}
local crowd_positions = {}
local function accumulate_crowd_adjustments(id, position, offset)
if not crowd_adjustments[id] then
crowd_adjustments[id] = vmath.vector3(0, 0, 0)
end
crowd_adjustments[id] = crowd_adjustments[id] + offset
crowd_positions[id] = position
end
local function apply_crowd_adjustments()
for id, adjustment in pairs(crowd_adjustments) do
go.set_position(crowd_positions[id] + adjustment, id)
end
crowd_adjustments = {} --clear adjustments after applying
crowd_positions = {}
end
local function perform_crowding(data, data_other, distance)
--player exceptions (don't collide with units, but respect obstacles)
if data.group == PLAYER then
if data_other.group ~= OBSTACLE then
return
end
end
--obstacle exceptions (only collide with other obstacles)
if data.group == OBSTACLE then
if data_other.group ~= OBSTACLE then
return
end
end
if data_other.group == OBSTACLE then
--complete and immediate adjustment for obstacles
accumulate_crowd_adjustments(data.id, data.instance_position, data.normal * distance)
else
--gradual adjustment for crowding
accumulate_crowd_adjustments(data.id, data.instance_position, data.normal * distance * 0.25)
end
end
local function physics_callback(self, event, data)
if event == CONTACT_POINT_EVENT then
perform_crowding(data.a, data.b, data.distance)
perform_crowding(data.b, data.a, data.distance)
end
end
function init(self)
physics.set_listener(physics_callback)
end
function update(self, dt)
apply_crowd_adjustments()
end
function final(self)
crowd_adjustments = {}
end
The end result is something similar to the video you linked!
Mid-2025 earliest