The Playdate-Platformer Library is a comprehensive library that simplifies the process of creating platformer games for the Playdate. It includes all the necessary components to ensure smooth and satisfying jumping mechanics.
You can add it to your Playdate project by installing toybox.py, going to your project folder in a Terminal window and typing:
toybox add pplib
toybox update
Then, if your code is in the source folder, just import the following:
import '../toyboxes/toyboxes.lua'
To install the Playdate-Platformer Library, simply copy the pp-lib.lua file into your game directory and import it into your game.
import "pp-lib"
import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "pp-lib"
local pd <const> = playdate
local gfx <const> = pd.graphics
-- Player class
class("Player").extends(DefaultPlatformer)
local player_img = gfx.image.new(32, 32)
gfx.pushContext(player_img)
gfx.fillRect(0, 0, 32, 32)
gfx.popContext(player_img)
function Player:init(x, y)
Player.super.init(self, {idle=player_img, run=player_img, jump=player_img, fall=player_img})
self:setCollideRect(0,0, player_img:getSize())
self:moveTo(x, y)
end
-- Block class
class("Block").extends(Solid)
local block_img = gfx.image.new(16,16)
gfx.pushContext(block_img)
gfx.fillRect(0, 0, 16, 16)
gfx.popContext(block_img)
function Block:init(x, y)
Block.super.init(self)
self:setImage(block_img)
self:setCollideRect(0,0, block_img:getSize())
self:moveTo(x, y)
end
-- set up game
Player(50, 50)
-- blocks
for i=1,12,1 do
Block(8+(i-1)*16, 80)
end
for i=1,12,1 do
Block(392-(i-1)*16, 160)
end
-- walls
Solid.addEmptyCollisionSprite(0, -10, 400, 10)
Solid.addEmptyCollisionSprite(0, 240, 400, 10)
Solid.addEmptyCollisionSprite(-10, 0, 10, 240)
Solid.addEmptyCollisionSprite(400, 0, 10, 240)
function playdate.update()
gfx.sprite.update()
end
The above code demonstrates how to use the Playdate-Platformer Library to create a platformer game for the Playdate. For a more detailed description of available parameters and functionalities, please refer to the API section below.
Below are the available parameters that you can customize for your platformer character:
self.has_air_control = true -- determins if player can be controlled while in the air
self.has_ground_control = true -- determins if player can be controlled while on the ground
self.run_speed_max = 200 -- maximum ground speed, in px/s
self.run_speed_acc = 20 -- acceleration speed when the player starts moving while on the ground, in px/s
self.run_speed_dcc = 40 -- deceleration speed when the player stops moving while on the ground
self.air_speed_max = 200 -- maximum horizontal air speed, in px/s
self.air_speed_acc = 20 -- horizontal acceleration speed when the player starts moving while on the air, in px/s
self.air_speed_dcc = 4 -- horizontal deceleration speed when the player stops moving while on the air, in px/s
self.jump_boost = 400 -- initial vertical speed when the player jumps, in px/s
self.jump_dcc = 20 -- the gravity applied while the player is jumping, in px/s
self.jump_max_time = 300 -- the maximum time the player can be accending, in ms
-- if, due to `jump_dcc`, the players vertical speed reaches 0
-- before this time, they will enter the fall state before this time
self.jump_min_time = 120 -- the minimum time the player can be accending, in ms
self.jump_count_max = 1 -- how many times the player can jump without touching the ground
self.jump_buffer_time = 300 -- how long should jump inputs be buffered, in ms
self.apex_boost = 10 -- a small vertical boost when the player goes from the `JumpState`
-- into the `FallState` to smooth out the arc, in px/s
self.bump_max = 6 -- the maxium distand the player can be bumped, in px
-- if the player hits the edge of a block while jumping, this allows them
-- to be pushed aside and continue the jump
self.fall_acc = 30 -- the gravity applied when the player is falling, in px/s
self.fall_hang_acc = 20 -- a reduced gravity applied at the apex of a jump to allow for more precision platforming, in px/s
self.fall_hang_time = 100 -- the time the reduced gravity is applied for, is ms
self.fall_max = 400 -- the maximum fall speed, in px/s
self.coyote_time = 120 -- the amount of time after the player walks off a platform where the jump button will still work, in ms
- Basic platformer
- Adding a pickup
- Adding an enemy
- Adding a new state
- Overriding default states
- using LDtk
- Custom input handler - todo
- Super Date Box - Large example
extends playdate.graphics.sprite
a very thin wrapper around playdate.graphics.sprite
, mostly because I prefer calling things 'actors' insted of 'sprites' 🤷
also calls self:add()
calls self:remove()
extends Actor
extend this class to create blocks/platforms that you want you player to be able to stand on.
sets its group to {Group.solid}
side
:Side.top
,Side.bottom
,Side.left
, orSide.right
. bitwise or can be used to set multiple sides at onceSide.left | Side.right
passable
: boolean, if the player is able to passthrough coming from that direction. you may also set the mask directly,solid.mask = Side.top
, this has the effect of setting all sides except that one being passable.
utility function to add an invisible solid to the game
utility function to make working with PlaydateLDtkImporter easier. Works very similar to playdate.graphics.sprite.addWallSprites
.
Will return a list of the created sprites.
local passable = Solid.addWallSprites(tilemap, LDtk.get_empty_tileIDs(levelName, "Passthrough", layerName))
for _, s in ipairs(passable) do
s.mask = Side.top
end
extends Actor
a catch all class for everything that interacts with the player but doesn't stop them, eg: pickups, coins, spikes, enemies, etc.
sets its group to {Group.trigger}
to be overridden, is called when this object overlaps with the player
actor
: the player actorcol
: the collision object
an easy to use camera that will follow the platformer character around the level.
mode
: select how the camera will follow the player around. Either provide the mode using theCameraMode
enum or a string value"lock" | "hlock" | "box" | "look_ahead"
. Defaults to"hlock"
config
: optional config to customise the follow mode.
an enum used when initalizing a FollowCamera
-
CameraMode.lock
-
CameraMode.hlock
-
CameraMode.box
-
CameraMode.look_ahead
sets the target for the camera to follow. By default the camera will instantly snap to the target, you can ignore this behavior by passing true into ignore_snap
removes the target. Remember to do this when removing the platformer from the game (dying for example) as FollowCamera
keeps an internal ref to the target which may prevent it being cleaned up by the GC.
updates the cameras position, call this in your game update function.
sets the bounds to lock the camera to. If the bounds are set the camera cannot move past those bounds even if the target has moved out of those bounds.
Adds some nice juicy screen shake. Just call this once and the screen shake will decay over the provided time, if time
is not provided amount
will be used. time
is in frames.
you can just provide a large amount
to get a burst of screen shake:
cam:shake(10)
or provide a low amount
with a high time
to get a sustained rumble
cam:shake(3, 60)
extends BasePlatformer A good starting place for most platformers, sets up sensible defaults for all values and sets up the state machine. Sub class this class to create your platformer character.
images
:{idle=[image or imagetable], run=[image or imagetable], jump=[image or imagetable], fall=[image or imagetable]}
. The images to be used for each of the different states, under the hood it uses AnimatedImage.options
:{idle=[AnimatedImage options], run=[AnimatedImage options], jump=[AnimatedImage options], fall=[AnimatedImage options]}
- options for each of the different images, as per AnimatedImage
delay
: time in milliseconds to wait before moving to next frame. (default: 100ms)paused
: start in a paused state. (default: false)loop
: loop the animation. (default: false)step
: number of frames to step. (default: 1)sequence
: an array of frame numbers in order to be used in the animation e.g.{1, 1, 3, 5, 2}
. (default: all of the frames from the specified image table)
- options for each of the different images, as per AnimatedImage
initalises the state machine like this:
self.sm:addState("idle", IdleState(self, images.idle, options.idle))
self.sm:addState("run", RunState(self, images.run, options.run))
self.sm:addState("jump", JumpState(self, images.jump, options.jump))
self.sm:addState("fall", FallState(self, images.fall, options.fall))
self.sm:addEvent({name='run', from='*', to='run'})
self.sm:addEvent({name='stop', from='run', to='idle'})
self.sm:addEvent({name='jump', from='*', to='jump'})
self.sm:addEvent({name='fall', from='*', to='fall'})
self.sm:addEvent({name='land', from='fall', to=defaultLandEvent})
self.sm:addCallback('onland', defaultOnLandCallback)
self.sm:addCallback('onbeforefall', defaultOnBeforeFallCallback)
self.sm:addCallback('onbeforejump', defaultOnBeforeJumpCallback)
buttons is a table containing the key to be used to control the player
left
: button to move left- default
playdate.kButtonLeft
- default
right
: button to move right- default
playdate.kButtonRight
- default
jump
: button to jump- default
playdate.kButtonA
- default
Heres all the different properties that can be tweaked to make your platformer
self.has_air_control = true -- determins if player can be controlled while in the air
self.has_ground_control = true -- determins if player can be controlled while on the ground
self.run_speed_max = 200 -- maximum ground speed, in px/s
self.run_speed_acc = 20 -- acceleration speed when the player starts moving while on the ground, in px/s
self.run_speed_dcc = 40 -- deceleration speed when the player stops moving while on the ground
self.air_speed_max = 200 -- maximum horizontal air speed, in px/s
self.air_speed_acc = 20 -- horizontal acceleration speed when the player starts moving while on the air, in px/s
self.air_speed_dcc = 4 -- horizontal deceleration speed when the player stops moving while on the air, in px/s
self.jump_boost = 400 -- initial vertical speed when the player jumps, in px/s
self.jump_dcc = 20 -- the gravity applied while the player is jumping, in px/s
self.jump_max_time = 300 -- the maximum time the player can be accending, in ms
-- if, due to `jump_dcc`, the players vertical speed reaches 0 before this time, they will enter the fall state before this time
self.jump_min_time = 120 -- the minimum time the player can be accending, in ms
self.jump_count_max = 1 -- how many times the player can jump without touching the ground
self.jump_buffer_time = 300 -- how long should jump inputs be buffered, in ms
self.apex_boost = 10 -- a small vertical boost when the player goes from the `JumpState` into the `FallState` to smooth out the arc, in px/s
self.bump_max = 6 -- the maxium distand the player can be bumped, in px
-- if the player hits the edge of a block while jumping, this allows them to be pushed aside and continue the jump
self.fall_acc = 30 -- the gravity applied when the player is falling, in px/s
self.fall_hang_acc = 20 -- a reduced gravity applied at the apex of a jump to allow for more precision platforming, in px/s
self.fall_hang_time = 100 -- the time the reduced gravity is applied for, is ms
self.fall_max = 400 -- the maximum fall speed, in px/s
self.coyote_time = 120 -- the amount of time after the player walks off a platform where the jump button will still work, in ms
extends Actor
A bare bone starting point for a platformer. Initalises the statemachine but does not add any states or events. Does not initalise any parameters. Sub class this class to create your platformer character.
the players state machine
the players input handler
Initalises the statemachine but does not add any states or events.
Does not initalise any parameters.
Adds the player to the {Group.actor}
group, and sets collidesWithGroups to {Group.solid, Group.trigger}
performs the following thins in order:
- calls
update
on the input handler - calls
update
on the current state - calls
move
on self - calls
updateImageFlip
on self
returns kCollisionTypeSlide
if othe is fully solid, otherwise returns kCollisionTypeOverlap
performs the following thins in order:
- moves the player with collisions
- calls
resolveCollision
on allSolid
s collided with - calls
perform
on allTrigger
s collided with - calls
aftermove
on the current state - calls
aftermove
on self
cols
: collision objectsl
: number of collisionstx
: target xty
: target y
called after all movement resolutions are done override if you want to do something here
all input management is handled her to create a seperation of concerns and to make changing the controll scheme easy
must set the following parametes
dx
: the horozontal direction, a number between -1 and 1 (usually -1, 0, or 1)jump
: boolean, jump button held downjump_pressed
: boolean, jump button just pressedjump_buffered
: boolean, jump button pressed with in the last n ms (thejump_buffer_time
set on the actor)
the state machine based on this one without the async transitions and some extra utility added.
adds a state to the state machine, if it is the first state added, will also set it as the initial state
event is a table with the values:
name
: the name of the event, a method will be added to the state maching allowing you to trigger this event. Additional arguments will be passed to theonenter
method of the state. eg:sm:run()
from
: either a string or a list of strings, the states this event can be triggered from. If this event is triggered while the current state isn't part of this list, nothing happens.'*'
may be used to allow the event from all states.to
: either a string, the name of the state to enter, or a function that returns the name of the state to enter. The function is passed the args{[Machine], [event name], [from]}
adds a callback function to be on or before a given event
name
: either'on'..[event name]
,'onbefore'..[event name]
, or'onstatechange'
callback
: function to be called, args passed{ [Machine], name, from, to, ...}
returns the current state object
returns the state object of that name
the empty state class
extends State
extend from this class to create new states for your platformer. Handles updating the spite image.
actor
: a BasePlatformerimages
: either a image or an imagetableoptions
: AnimatedImage options
sm
: the state Machinename
: the event name stringfrom
: the class name string of the state coming fromto
: the class name string of the state going to
cols
: collision objectsl
: number of collisionstx
: target xty
: target y
called every frame when this is the current state
extends BaseState
handles jumping and checking if the player should fall
extends BaseState
handles horizontal movement while in the air
extends GroundState
handles deceleration when the player stops, otherwise just sits there
extends GroundState
handles horozontal movement while on the ground
extends AirState
handles the vertical movement while in the accending portion of a jump, also handles the edge bumping behaviour
extends AirState
handles the vertical movement while in the decending portion of a jump, also handles the coyote time when running of a solid
this library uses a slightly modified version of AnimatedImage