It looks like my game object is currently receiving a contact_point_response message containing a horizontal contact point (assuming that’s what the debug arrows indicate) while walking along a flat surface, which causes the horizontal movement to be abrupt. The movement is fine while the game object is in the air as it isn’t colliding with any surfaces. Here’s an example screen recording of what is currently happening:
Here is what the game object’s update function looks like:
function update(self, dt)
local target_speed = self.move_input * self.max_speed
local speed_diff = target_speed - self.velocity.x
local acceleration = vmath.vector3(0, self.gravity, 0)
if speed_diff ~= 0 then
if speed_diff < 0 then
acceleration.x = -self.move_acceleration
else
acceleration.x = self.move_acceleration
end
end
local dv = acceleration * dt
if math.abs(dv.x) > math.abs(speed_diff) then
dv.x = speed_diff
end
local v0 = self.velocity
self.velocity = self.velocity + dv
local dp = (v0 + self.velocity) * dt * 0.5
go.set_position(go.get_position() + dp)
self.correction = vmath.vector3()
self.move_input = 0
self.ground_contact = false
end
Here’s what the function that handles collision detection looks like:
function on_message(self, message_id, message, sender)
if message_id == msg_contact_point_response then
if message.group == group_geometry then
handle_geometry_contact(self, message.normal, message.distance)
end
end
end
local function handle_geometry_contact(self, normal, distance)
local proj = vmath.dot(self.correction, normal)
local comp = (distance - proj) * normal
self.correction = self.correction + comp
go.set_position(go.get_position() + comp)
if normal.y > 0.7 then
self.ground_contact = true
end
proj = vmath.dot(self.velocity, normal)
if proj < 0 then
self.velocity = self.velocity - proj * normal
end
end
Is your ground made with a tilemap or otherwise with multiple collision shapes? If so then read this post.
Assuming that’s the issue, as far as I can find there is no perfect solution that solves this correctly in all circumstances. The simplest solution is to make your player body rounded or beveled on the bottom. That works well enough if you’re not too picky.
I thought it may be an issue with multiple collision shapes, as if I ignore tilesource collisions and just draw a single collision body over the entire tilemap, the collisions work as expected. It appears to draw a single contiguous rectangle over the tile map while debug draw is enabled though so I wasn’t sure.
Using a rounded collision body doesn’t seem to provide as precise collision detection as I would like (the game object “bounces” every time it leaves a tile) so I may try to implement something else or tweak it more. Thanks for the advice.
This is an annoying problem which has impacted my projects in the past. Tilemap collision shapes need smarter welding of points. It would be nice if we could visualize points with debug on too.
Yeah, the debug drawing doesn’t show the edges, so you can’t really tell, but each tile is a separate shape. You can change tiles however you want at runtime, so this makes sense.
This reddit thread has a few different suggestions, I haven’t tried to implement them with Defold.
Using raycasts instead of shape collision is probably the the most precise and reliable method you can use with Defold. @dmitriy, have you done this?
The typical way (…) can have this done in hours.
An intermediate solution (…) have this done in 1 evening.
The “correct” solution (…) took me 1 year to get all the details right.
Here(don’t forget to jump to the well) and here some sort of old unfinished short demos. You can try them if you want to “feel” how ray casts works. They are still implemented with async raycast, so no slopes in them.