Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tile collisions #1891

Closed
wants to merge 10 commits into from
8 changes: 7 additions & 1 deletion src/characters/abed.json
Expand Up @@ -37,5 +37,11 @@
{ "category": "s2e6", "name": "Zombie", "sheet": "zombie", "ow": 19 }
],
"name": "abed",
"bbox": {
"width": 14,
"height": 41,
"x": 17,
"y": 7
},
"offset": 5
}
}
286 changes: 286 additions & 0 deletions src/hawk/collision.lua
@@ -0,0 +1,286 @@
local module = {}

function module.find_collision_layer(map)
for _, layer in ipairs(map.tilelayers) do
if layer.name == "collision" then
return layer
end
end
return nil
end

function module.platform_type(tile_id)
if tile_id >= 21 and tile_id <= 42 then
return 'oneway'
end

if tile_id >= 0 and tile_id <= 20 then
return 'block'
end

error('Unknown collision type')
end

function module.is_sloped(tile_id)
return (tile_id > 0 and tile_id < 21) or (tile_id > 21 and tile_id < 43)
end

local _slopes = {
nil,
{23, 0},
{0, 23},
{23, 12},
{11, 0},
{0, 11},
{12, 23},
}

function module.slope_edges(tile_id)
local tile_id = tile_id % 21
return _slopes[tile_id + 1][1], _slopes[tile_id + 1][2]
end

-- if the character (that is, his bottom-center pixel) is on a
-- {0, *} slope, ignore left tile, and, if on a {*, 0} slope,
-- ignore the right tile.
--
-- Also, remember that tile ids are indexed startin at 1
-- We assume that the tile at tile_index is sloped
function module.is_adjacent(current_index, current_id, tile_index, tile_id, direction)
if tile_id ~= 0 then
return false
end

-- Check if the tile is adjacent
-- FIXME: Corner case where these ids overlap rows
if direction == "right" and tile_index - current_index ~= 1 then
return false
end

-- Check if the tile is adjacent
-- FIXME: Corner case where these ids overlap rows
if direction == "left" and current_index - tile_index ~= 1 then
return false
end

if not module.is_sloped(current_id) then
return false
end

return true
end

-- Returns the current tile index, using the bottom center pixel
function module.current_tile(map, x, y, width, height)
local x1 = math.floor(x + width / 2)
local y1 = y + height

local current_col = math.floor(x1 / map.tilewidth) + 1
local current_row = math.floor(y1 / map.tileheight)

local result = current_row * map.width + current_col

return result
end

function module.move_x(map, player, x, y, width, height, dx, dy)
local collision_layer = module.find_collision_layer(map)
local direction = player.character.direction
local new_x = x + dx

local current_index = module.current_tile(map, x, y, width, height)
local current_tile = collision_layer.tiles[current_index]

for _, i in ipairs(module.scan_rows(map, x, y, width, height, direction)) do
local tile = collision_layer.tiles[i]

if tile then
local platform_type = module.platform_type(tile.id)
local sloped = module.is_sloped(tile.id)

local adjacent_slope = false

if current_tile then
adjacent_slope = module.is_adjacent(current_index, current_tile.id, i,
tile.id, direction)
end

local ignore = sloped or adjacent_slope

if direction == "left" then
local tile_x = math.floor(i % map.width) * map.tileheight

if platform_type == "block" and not ignore then

if new_x <= tile_x and tile_x <= x then
return tile_x
end

end
end

if direction == "right" then
local tile_x = math.floor((i % map.width) - 1) * map.tilewidth

if platform_type == "block" and not ignore then

if x <= tile_x and tile_x <= (new_x + width) then
return tile_x - width
end

end
end
end
end

return new_x
end

--local center_x = player_x + bbox_width / 2

function module.interpolate(tile_x, center_x, left_edge, right_edge, tilesize)
local t = (center_x - tile_x) / tilesize;
local y = math.floor(((1-t) * left_edge + t * right_edge))
return math.min(math.max(y, 0), tilesize)
end


function module.move_y(map, player, x, y, width, height, dx, dy)
local direction = dy <= 0 and 'up' or 'down'
local new_y = y + dy
local collision_layer = module.find_collision_layer(map)

for _, i in ipairs(module.scan_cols(map, x, y, width, height, direction)) do
local tile = collision_layer.tiles[i]

if tile then
local platform_type = module.platform_type(tile.id)
local sloped = module.is_sloped(tile.id)

if direction == "down" then
local tile_x = math.floor((i % map.width) - 1) * map.tilewidth
local tile_y = math.floor(i / map.width) * map.tileheight
local slope_y = math.floor(i / map.width) * map.tileheight

if sloped then
local center_x = x + (width / 2)
local ledge, redge = module.slope_edges(tile.id)
local slope_change = module.interpolate(tile_x, center_x, ledge, redge,
map.tilewidth)
slope_y = tile_y + slope_change
end

if platform_type == "block" then

-- If the block is sloped, interpolate the y value to be correct
if slope_y <= (y + dy + height) then
-- FIXME: Leaky abstraction
player.jumping = false
player.velocity.y = 0
player:restore_solid_ground()
return slope_y - height
end
end

if platform_type == "oneway" then
local above_tile = (y + height) <= slope_y

-- If player is in a sloped tile, keep them there
local foot = y + height
local in_tile = sloped and foot > tile_y and foot <= tile_y + map.tileheight

if (above_tile or in_tile) and slope_y <= (y + dy + height) then
player.jumping = false
player.velocity.y = 0
player:restore_solid_ground()
return slope_y - height
end
end
end

if direction == "up" then
if platform_type == "block" then
local tile_y = math.floor(i / map.width + 1) * map.tileheight

if y > tile_y and tile_y >= (y + dy) then
player.velocity.y = 0
return tile_y
end
end

if platform_type == "oneway" then
-- Oneway platforms never collide when going up
end
end
end
end

return new_y
end


-- Returns the new position for x and y
function module.move(map, player, x, y, width, height, dx, dy)
local new_x = module.move_x(map, player, x, y, width, height, dx, dy)
local new_y = module.move_y(map, player, new_x, y, width, height, dx, dy)
return new_x, new_y
end

function module.scan_rows(map, x, y, width, height, direction)
if direction ~= "left" and direction ~= "right" then
error("Direction must be left or right")
end

local rows = {}

-- Default value for left
local edge_x = x
local stop, change = 1, -1

if direction == "right" then
stop, change = map.width, 1
end

local current_col = math.floor(edge_x / map.tilewidth) + 1
local top_row = math.floor(y / map.tileheight)
local bottom_row = math.floor((y + height - 1) / map.tileheight)

for i=current_col,stop,change do
for j=top_row,bottom_row,1 do
table.insert(rows, i + (j * map.width))
end
end

return rows
end

function module.scan_cols(map, x, y, width, height, direction)
if direction ~= "up" and direction ~= "down" then
error("Direction must be up or down")
end

local cols = {}

-- Default value for left
local edge_y = y
local stop, change = 0, -1

if direction == "down" then
stop, change = map.height - 1, 1
end

local current_row = math.floor(edge_y / map.tileheight)
local left_column = math.floor(x / map.tilewidth) + 1
local right_column = math.floor((x + width - 1) / map.tilewidth) + 1

for i=current_row,stop,change do
for j=left_column,right_column,1 do
table.insert(cols, i * map.width + j)
end
end

return cols
end


return module
Binary file added src/images/tilesets/collisions.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/images/tilesets/town.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/level.lua
Expand Up @@ -378,7 +378,7 @@ function Level:update(dt)
end

if self.state == 'active' or self.respawn == true then
self.player:update(dt)
self.player:update(dt, self.map)
end

-- falling off the bottom of the map
Expand Down Expand Up @@ -406,7 +406,7 @@ function Level:update(dt)
end
end

self.collider:update(dt)
--self.collider:update(dt)

self:updatePan(dt)
self:moveCamera()
Expand Down
44 changes: 15 additions & 29 deletions src/maps/town.tmx
Expand Up @@ -12,6 +12,9 @@
<tileset firstgid="1" name="town" tilewidth="24" tileheight="24">
<image source="../images/tilesets/town.png" width="360" height="192"/>
</tileset>
<tileset firstgid="121" name="collisions" tilewidth="24" tileheight="24">
<image source="../images/tilesets/collisions.png" width="512" height="512"/>
</tileset>
<layer name="mountains" width="80" height="14">
<properties>
<property name="parallax" value="0.2"/>
Expand Down Expand Up @@ -56,16 +59,26 @@
eJztlDEOwjAMRc1IlIEFhjDARZBYunGDjr1IjsJRepQeBVsQVLkphBglspQvfampZOXnxQ5A05pu6Asz/vM1M2lSD42fVLwHa/Bz6APuuwe403qr6A6pB5HZFNxXyG4BrkeAM3F0z/VYOoNmITsf2NG3dn4dnqEruJ9jtormNyYpP3zH6D1LlsH5nfOjWQZ4v4m+VI5/KZcfnnuK9NL4rc6yGuI3n2my+YFjyFHrTc3lh2dczKJLqIvV5NzDpxxGAT/L5jCVH5/fFX5ekqNk/w2YdXh9b5pF3jWLfGoW+QEeRjcV
</data>
</layer>
<layer name="foreground" width="80" height="14" opacity="0.56">
<layer name="foreground" width="80" height="14">
<data encoding="base64" compression="zlib">
eJztlM0KgCAQhL1G1yC69kd1649ez0dvJSKTUFJhtfaDOZjOMO1hGSOIb5OCkkMcuUr0ZDRDJyrsAhHTYhcIBNtdtnrK8eXHRuyyVHPfgXpFw+W95byZQ/Lg1/UICbG/alADmgxvt4dvuXI2Zeh6CAqHDGxKw708v/N/1fmZMnz0CI0ZtIBGC28u+ZllhtwDxF0yCIL4LzuNIwup
</data>
</layer>
<layer name="accents" width="80" height="14" opacity="0.49">
<layer name="accents" width="80" height="14">
<data encoding="base64" compression="zlib">
eJztks0KQEAUhZX/BUrKBi+AlYXCw82jO5KapBR3Qs5XX01Tc2buaSyLEEKIDR3oQg/6MHj0RfKYnDE93lNC8a/gaMbcYDb7u5et9xfCTOiuMyIYw0Q49y//r4AlrIRz/9JfDRvYCud+ob9uVfUXzy3rAY5w2u3fZZtRz5TqjxDyLWYJuAqz
</data>
</layer>
<layer name="funstuff" width="80" height="14">
<data encoding="base64" compression="zlib">
eJzt0L0KABAYhWFWk/JzvS7dKZ/FwCh5n3pLMug4BwB/C5twxn4AMCSVlbd0brprVz/1gLlbUVVFq1rrjvM9ALymA8WFA8k=
</data>
</layer>
<layer name="collision" width="80" height="14">
<data encoding="base64" compression="zlib">
eJztld0KgDAIRn3toJ83WL8PnIOiELKLGabzwLkYyJCPTQGCwDcJndFNuxEHrOAzx4bRE+nBUmrJ7w9IzbLSe6zN1NzvcjvnWca9fe6v0HtoDuNROwn0EdijRTu4ZmCPDqod2YDmRqU5nvXS+Uruxi/h9m74bhBosgOvNkyi
</data>
</layer>
<objectgroup color="#000000" name="nodes" width="80" height="14">
<object type="cow" x="415" y="206" width="84" height="60"/>
<object name="main" type="door" x="48" y="216" width="24" height="48"/>
Expand Down Expand Up @@ -131,31 +144,4 @@
</properties>
</object>
</objectgroup>
<objectgroup color="#12a460" name="block" width="80" height="14">
<object x="0" y="264" width="1920" height="72"/>
</objectgroup>
<objectgroup color="#a42118" name="platform" width="80" height="14">
<object x="720" y="185" width="110" height="22">
<polyline points="0,0 47,-25 117,10 -2,10 -1,0"/>
</object>
<object x="562" y="172" width="110" height="22"/>
<object x="625" y="74">
<polyline points="1,2 0,22 91,22 22,-11 1,0"/>
</object>
<object x="385" y="168" width="24" height="24"/>
<object x="600" y="48" width="24" height="24"/>
<object x="1082" y="192">
<polyline points="0,0 67,-32 116,-9 116,-1 1,1"/>
</object>
<object x="1200" y="168" width="24" height="24"/>
<object x="270" y="192">
<polyline points="0,0 65,-31 113,-9 113,6 1,6 1,1"/>
</object>
<object x="72" y="96" width="144" height="24"/>
<object x="1440" y="96" width="144" height="24"/>
<object x="912" y="191">
<polygon points="0,0 25,-23 49,-23 70,0"/>
</object>
<object x="913" y="240" width="71" height="12"/>
</objectgroup>
</map>