Skip to content

Instantly share code, notes, and snippets.

@eevee
Created March 19, 2017 07:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eevee/d468ffae641d71429be152d86958b4cd to your computer and use it in GitHub Desktop.
Save eevee/d468ffae641d71429be152d86958b4cd to your computer and use it in GitHub Desktop.
tile-based pico8 collision i wrote in like an hour or two
platscene = {
xaccel = 0.5,
xmax = 2,
gravity = 0.5,
ymax = 16,
}
function platscene:switch()
self.mapbbox = {0, 0, 16, 16}
self.playerbbox = {
0, 0,
(critter.w or 1) * 8,
(critter.h or 1) * 8,
}
self.vel = {x=0, y=0}
self.ground = false
self.walktimer = 0
end
function platscene:update()
self.debug = {}
if self.ground then
self.walktimer += 1
end
-- collision
-- tile-based
-- one axis at a time
-- only move by pixels
local d = {
x = int(self.vel.x),
y = int(self.vel.y),
}
self.debugd = d
local pbb = self.playerbbox
local wasground = self.ground
self.ground = false
for axis in all{"x", "y"} do
local move = abs(d[axis])
local movedir = sgn(self.vel[axis])
local idx = 1
if axis == "y" then
idx = 2
end
-- move to tile boundary
local edge = pbb[idx]
local mapedge = self.mapbbox[idx]
if movedir > 0 then
edge += pbb[idx + 2]
mapedge += self.mapbbox[idx + 2]
end
mapedge *= movedir * 8
local step = min(move, edge * -movedir % 8)
move -= step
pbb[idx] += movedir * step
edge += movedir * step
-- move one tile at a time
-- always run this loop at
-- least once, so we know if
-- we're on the ground even
-- if not moving
local stopped
while edge % 8 == 0 do
if edge * movedir >= mapedge then
stopped = true
break
end
local blocked
local mx = edge / 8
if movedir < 0 then
mx -= 1
end
local my = mx
for other = flr(pbb[3 - idx] / 8), flr((pbb[3 - idx] + pbb[5 - idx] - 1) / 8) do
if axis == "x" then
my = other
else
mx = other
end
local s = mget(mx, my)
add(self.debug, {mx, my, fget(s, 7)})
if fget(s, 7) or (fget(s, 6) and axis == "y" and movedir > 0) then
blocked = true
break
end
end
if blocked then
stopped = true
break
end
local step = min(8, move)
move -= step
pbb[idx] += movedir * step
edge += movedir * step
if move <= 0 then
break
end
end
-- handle solid collision
if stopped then
self.vel[axis] = 0
if axis == "y" and movedir > 0 then
self.ground = true
if not wasground then
self.walktimer = 0
sfx(59)
end
end
end
end
-- passive
self.vel.y = min(self.ymax, self.vel.y + self.gravity)
-- player movement
-- yes, after the collision!
-- i do this for reasons: for
-- example, now i can tell if
-- the player is trying to
-- move via self.vel.x
if btn(0) then -- \139
self.vel.x = max(-self.xmax, self.vel.x - self.xaccel)
elseif btn(1) then -- \145
self.vel.x = min(self.xmax, self.vel.x + self.xaccel)
else
self.vel.x = self.vel.x * 3/4
if abs(self.vel.x < self.xaccel) then
self.vel.x = 0
end
end
if btnp(4) then -- \142
if self.ground then
self.vel.y = -sqrt(2 * self.gravity * 8 * 1.25)
sfx(58)
end
end
end
function platscene:draw()
cls()
local bb = self.mapbbox
map(bb[1], bb[2], 0, 0, bb[3] - bb[1], bb[4] - bb[2])
critter:draw_tl(
self.playerbbox[1],
self.playerbbox[2],
not self.ground or (self.vel.x ~= 0 and self.walktimer % 30 > 15))
rect(self.playerbbox[1], self.playerbbox[2], self.playerbbox[1] + self.playerbbox[3] - 1, self.playerbbox[2] + self.playerbbox[4] - 1, 9)
for d in all(self.debug) do
rect(d[1] * 8, d[2] * 8, d[1] * 8 + 7, d[2] * 8 + 7, d[3] and 8 or 11)
end
print(""..self.playerbbox[1]..","..self.playerbbox[2].."/"..self.playerbbox[3]..","..self.playerbbox[4], 0, 0, 9)
print(self.vel.x..","..self.vel.y, 0, 6, 8)
if self.debugd then
print(self.debugd.x..","..self.debugd.y, 0, 12, 7)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment