Skip to content

Instantly share code, notes, and snippets.

@kevinthompson
Last active January 27, 2020 04:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kevinthompson/d53548f4d3fe3053a1ddeef8e5364b73 to your computer and use it in GitHub Desktop.
Save kevinthompson/d53548f4d3fe3053a1ddeef8e5364b73 to your computer and use it in GitHub Desktop.
-- Platformer Engine
-- by @kevinthompson
-- adapted from @enichan's platformer https://github.com/Enichan/Pico8Platformer
platformer={
config={
logfile="log",
precision=1,
tilesize=8,
friction=0.125,
gravity=0.25,
max_gravity=4,
flags={
solid=0
}
},
entities={},
configure=function(self, config)
local default_config=self.config
self.config=self:merge({ default_config, config })
end,
init=function(self)
for entity in all(self.entities) do
entity:init()
end
end,
update=function(self)
for entity in all(self.entities) do
entity:update()
self.physics.update(self, entity)
self.animation.update(self, entity)
end
if (self.camera) self.camera.update(self)
end,
draw=function(self)
for entity in all(self.entities) do
entity:draw()
end
end,
entity=function(self, attributes)
attributes=attributes or {}
local default_attributes={
animation="idle",
prev_animation=nil,
x=64,
y=64,
width=3,
height=5,
jumping=false,
frame=1,
color=7,
flip=false,
move_speed=0.5,
speed={
x=0,
y=0
},
prev_collision={
up=false,
down=false,
left=false,
right=false,
},
collision={
up=false,
down=false,
left=false,
right=false,
},
animations={
idle={}
},
init=function(self)
end,
update=function(self)
end,
draw=function(self)
local sprite=self.animations[self.animation][self.frame]
if sprite~=nil then
spr(sprite, self.x, self.y, flr(self.width/8), flr(self.height/8), self.flip)
else
rectfill(self.x, self.y, self.x+self.width, self.y+self.height, self.color)
end
end,
}
local entity=self:merge({default_attributes, attributes})
add(self.entities, entity)
return entity
end,
-- private
animation={
update=function(self, entity)
if entity.speed.x < 0 then
entity.flip=true
elseif entity.speed.x > 0 then
entity.flip=false
end
end
},
physics={
update=function(self, entity)
self.physics.apply_gravity(self, entity)
self.physics.apply_friction(self, entity)
self.physics.resolve_collision(self, entity)
end,
apply_gravity=function(self, entity)
if not entity.collision.down then
local gravity = self.config.gravity
local max_gravity = self.config.max_gravity
local speed_y = entity.speed.y
entity.speed.y = min(max_gravity, speed_y + gravity)
entity.y += entity.speed.y
end
end,
apply_friction=function(self, entity)
if entity.collision.down and entity.speed.x ~= 0 then
entity.speed.x -= sgn(entity.speed.x) * min(self.config.friction, abs(entity.speed.x))
end
end,
resolve_collision=function(self, entity)
local speed = entity.speed
local steps = 1
local max_speed = max(abs(speed.x), abs(speed.y))
if max_speed >= 0.25 then
steps=ceil(max_speed / 0.25)
end
for i=1, steps do
entity.x += speed.x / steps
entity.y += speed.y / steps
self:resolve_collision_left(entity)
self:resolve_collision_right(entity)
self:resolve_collision_up(entity)
self:resolve_collision_down(entity)
end
end
},
resolve_collision_up=function(self, entity)
local flags = self.config.flags
local tilesize = self.config.tilesize
local min_x = entity.x
local min_y = entity.y
local max_x = entity.x + entity.width
local max_y = min_y
local tiles = self:get_tiles(min_x, min_y, max_x, max_y)
entity.prev_collision.up = entity.collision.up
entity.collision.up = false
for tile in all(tiles) do
if fget(tile.sprite, flags.solid) then
entity.speed.y = 0
entity.y = tile.y + tilesize
break
end
end
end,
resolve_collision_down=function(self, entity)
local min_x = entity.x
local min_y = entity.y + entity.height + 1
local max_x = entity.x + entity.width
local max_y = min_y
local tiles = self:get_tiles(min_x, min_y, max_x, max_y)
entity.prev_collision.down = entity.collision.down
entity.collision.down = false
for tile in all(tiles) do
if fget(tile.sprite, self.config.flags.solid) then
entity.speed.y = 0
entity.y = tile.y - entity.height - 1
entity.collision.down = true
break
end
end
end,
resolve_collision_left=function(self, entity)
local flags = self.config.flags
local tilesize = self.config.tilesize
local min_x = entity.x
local min_y = entity.y
local max_x = min_x
local max_y = entity.y + entity.height
local tiles = self:get_tiles(min_x, min_y, max_x, max_y)
entity.prev_collision.left = entity.collision.left
entity.collision.left = false
for tile in all(tiles) do
if fget(tile.sprite, flags.solid) then
entity.speed.x = 0
entity.x = tile.x + tilesize
break
end
end
end,
resolve_collision_right=function(self, entity)
local flags = self.config.flags
local tilesize = self.config.tilesize
local min_x = entity.x + entity.width + 1
local min_y = entity.y
local max_x = min_x
local max_y = entity.y + entity.height
local tiles = self:get_tiles(min_x, min_y, max_x, max_y)
entity.prev_collision.right = entity.collision.right
entity.collision.right = false
for tile in all(tiles) do
if fget(tile.sprite, flags.solid) then
entity.speed.x = 0
entity.x = tile.x - entity.width - 1
break
end
end
end,
merge=function(self, tables)
local result={}
for t in all(tables) do
for k,v in pairs(t) do
if (type(result[k])=="table" and type(v)=="table") then
result[k]=self:merge({result[k],v})
else
result[k]=v
end
end
end
return result
end,
log=function(self, obj, overwrite)
printh(self:tostring(obj), self.config.logfile, overwrite)
end,
tostring=function(self, any)
if type(any)=="table" then
local str="{"
local add_comma=false
for k,v in pairs(any) do
str=str..(add_comma and ", " or " ")
str=str..k.."="..self:tostring(v)
add_comma=true
end
return str.." }"
else
return tostr(any)
end
end,
get_tiles=function(self, min_x, min_y, max_x, max_y)
local tilesize=self.config.tilesize
local tiles={
self:tget(min_x, min_y),
self:tget(min_x, max_y),
self:tget(max_x, min_y),
self:tget(max_x, max_y),
}
for x = min_x + tilesize, max_x - tilesize, tilesize do
for y = min_y + tilesize, max_y - tilesize, tilesize do
add(tiles, self:tget(x, y))
end
end
return tiles
end,
tget=function(self,x,y)
local mx=flr(x/8)
local my=flr(y/8)
local sprite=mget(mx,my)
return {
x=mx*8,
y=my*8,
mx=mx,
my=my,
sprite=sprite,
flags=fget(sprite)
}
end
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment