Wow I wish that I came back here to read this sooner. Yesterday I bashed my head into the wall trying to get it perfect with just overlap testing.
I switched to using the sort variant of query so I could get the closest collision.
function M.query_player()
local result, count = daabbcc.query_id_sort(M.group, M.player_aabb_id, collision_bits.GROUND)
return result, count
end
This handles most of the collision.
function check_collisions(self, pos)
local query_result, result_count = collision.query_player()
if not query_result and (self.wall_contact_left or self.wall_contact_right) then
print("No query result")
self.wall_contact_left = false
self.wall_contact_right = false
end
if query_result and result_count > 0 then
local first_collision = query_result[1]
local aabb_id = first_collision.id
local data = collision.ground_data[aabb_id]
if data and data.type == "GROUND" then
local y_offset = 18
local x_offset = 15.01
local tile_top = data.y + data.height
local tile_bottom = data.y
local tile_left = data.x
local tile_right = data.x + data.width
-- Check for above and below collisions
local is_above_tile = pos.y >= tile_top
local is_below_tile = pos.y + (54 / 2) < tile_bottom
local is_right_of_tile = pos.x - (46 / 2) < tile_right and pos.x > tile_left
local is_left_of_tile = pos.x + (46 / 2) + 1000000 > tile_left and pos.x < tile_right
-- Handle collision from above (landing)
if is_above_tile then
-- print("Hit from above")
pos.y = tile_top + y_offset
self.ground_contact = true
msg.post("/camera#controller", "follow_player_y", { toggle = false })
self.velocity.y = 0
-- Handle collision from below
elseif is_below_tile then
-- print("Hit from below")
pos.y = tile_bottom - (y_offset * 2)
self.velocity.y = 0
self.is_jumping = false
-- Handle left collision
elseif is_right_of_tile then
-- print("Hit from the left")
pos.x = tile_right + x_offset
self.velocity.x = 0
self.wall_contact_left = true
-- Handle right collision
elseif is_left_of_tile then
-- print("Hit from the right")
pos.x = tile_left - (x_offset * 2) - 1
self.velocity.x = 0
self.wall_contact_right = true
end
else
print("Unknown collision!")
end
end
return pos
end
This mostly works but due to the way the collision is handled, it causes oscillating self.wall_contact_*
values which has a variety of undesired effects for my game.
So it came to me when I woke up this morning that a combination of raycasting and overlap testing would do the trick.
function M.raycast_player(player_pos, sprite_flipped, max_distance)
local player_height = 54
local direction = sprite_flipped and -1 or 1
local ray_offsets = {
0, -- center
player_height / 2 - 10, -- top
-player_height / 2 + 10 -- bottom
}
local results = {}
for _, offset in ipairs(ray_offsets) do
local ray_start = vmath.vector3(player_pos.x, player_pos.y + offset, 0)
local ray_end = vmath.vector3(player_pos.x + direction * max_distance, player_pos.y + offset, 0)
local result, count = daabbcc.raycast(M.group, ray_start.x, ray_start.y, ray_end.x, ray_end.y, collision_bits.GROUND)
table.insert(results, {result = result, count = count, ray_start = ray_start, ray_end = ray_end})
end
if debug then
local blue = vmath.vector4(0, 0, 1, 1)
for _, ray_data in ipairs(results) do
debug_draw_raycast(ray_data.ray_start, ray_data.ray_end, blue)
if ray_data.count then
for _, aabb_id in ipairs(ray_data.result) do
local tile = M.ground_data[aabb_id]
if tile then
debug_draw_aabb({ tile }, blue, tile_draw_x_offset, tile_draw_y_offset)
end
end
end
end
end
return results
end
function M.update_wall_contact(player, player_pos)
local raycast_results = M.raycast_player(player_pos, player.sprite_flipped, 26)
player.wall_contact_left = false
player.wall_contact_right = false
for _, result in ipairs(raycast_results) do
if result.result then
if player.sprite_flipped then
player.wall_contact_left = true
else
player.wall_contact_right = true
end
end
end
end
It’s a mess but it works.
I think Pawel’s solution is more robust so I will probably study that once I run into issues with this one.
Thanks to selim for the library. It’s hard to figure out but once you do its not bad, like anything I guess.
nevermind the low quality video.