Swift :heart: Playdate
Swift package & plugin for building games that run on Playdate's simulator.
Some things to know:
- Requires XCode 13.3+
- Only tested on M1 Mac.
- Useful for exploring what swift development might be like, you cannot build for device (yet).
- This is highly experimental and likely to change - the swift wrapper is barely started.
- You will likely need to kill the simulator between runs, it doesn't seem to pick up on changes very well.
- The plugin isn't very stable / smart, but it can be made to be.
Usage
swift-playdate can be used via swift-package-manager. It only requires a small bit of project structure.
- Must be a package based project with product type of dynamic.
- Must have a
Sources/Resources folder with at least a valid pdxinfo file inside.
- note: other assets (images, sound, lua files) are to be added here too.
- Builds are handled via SPM:
swift package --disable-sandbox pdc: creates a pdx file located at .build/plugins/pdc/outputs/artifacts/{PACKAGE_DISPLAY_NAME}.pdx
swift package --disable-sandbox pdc --run: creates a pdx file & opens it in the simulator
--disable-sandbox is required because we are using plugins outside their typical reach.
- Ideally swizzle the
EventCallback in order to install an update handler.
Example
import Playdate
// MARK: Game Loop
func updateCallback() -> Bool {
Graphics.clear(with: .white)
processInputs()
DisplayList.updateAndDrawSprites()
return true
}
func processInputs() {
let state = System.currentButtonState
player.move(
by: .init(
x: state.contains(.left) ? -10 : state.contains(.right) ? 10 : 0,
y: state.contains(.up) ? -10 : state.contains(.down) ? 10 : 0
)
)
}
// MARK: Setup
let image = Bitmap("images/player")
let player = Sprite()
func initialize() {
setupPlayer()
setupMenu()
}
func setupPlayer() {
player.setImage(image)
player.move(to: .init(x: image.size.width, y: image.size.height))
player.addToDisplayList()
}
func setupMenu() {
Menu.addCheckmarkItem("inverted", isOn: false) { isEnabled in
Display.isInverted = isEnabled
}
}
// MARK: Event Handler
@_dynamicReplacement(for: EventCallback(event:))
func eventCallback(event: SystemEvent) {
if event == .initialize {
initialize()
System.setUpdateCallback(updateCallback)
}
}
Supported APIs
Below is a list of all the C apis & wether or not there is some sort of equivalent in Playdate.
Display
| Status |
Name |
| ✅ |
getWidth |
| ✅ |
getHeight |
| ✅ |
setRefreshRate |
| ✅ |
setInverted |
| ✅ |
setScale |
| ✅ |
setMosaic |
| ✅ |
setFlipped |
| ✅ |
setOffset |
File
| Status |
Name |
| 🚧 |
geterr |
| 🚧 |
stat |
| 🚧 |
mkdir |
| 🚧 |
unlink |
| 🚧 |
rename |
| 🚧 |
open |
| 🚧 |
read |
| 🚧 |
write |
| 🚧 |
flush |
| 🚧 |
tell |
| 🚧 |
seek |
Graphics
| Status |
Name |
| ✅ |
Clear |
| ✅ |
setBackgroundColor |
| ✅ |
setStencil |
| ✅ |
setDrawMode |
| ✅ |
setDrawOffset |
| ✅ |
setClipRect |
| ✅ |
setScreenClipRect |
| ✅ |
clearClipRect |
| ✅ |
setLineCapStyle |
| ✅ |
setFont |
| ✅ |
setTextTracking |
| ✅ |
setTextLeading |
| ✅ |
pushContext |
| ✅ |
popContext |
Drawing
| Status |
Name |
| ✅ |
drawBitmap |
| ✅ |
tileBitmap |
| ✅ |
drawLine |
| 🚧 |
fillTriangle |
| ✅ |
drawRect |
| ✅ |
fillRect |
| 🚧 |
drawEllipse |
| 🚧 |
fillEllipse |
| 🚧 |
fillPolygon |
| ✅ |
drawScaledBitmap |
| ✅ |
drawText |
Bitmap
| Status |
Name |
| ✅ |
newBitmap |
| ✅ |
freeBitmap |
| ✅ |
loadBitmap |
| ✅ |
copyBitmap |
| 🚧 |
loadIntoBitmap |
| ✅ |
getBitmapData |
| ✅ |
clearBitmap |
| 🚧 |
rotatedBitmap |
BitmapTable
| Status |
Name |
| 🚧 |
newBitmapTable |
| 🚧 |
freeBitmapTable |
| 🚧 |
loadBitmapTable |
| 🚧 |
loadIntoBitmapTable |
| 🚧 |
getBitmapTable |
Font
| Status |
Name |
| 🚧 |
loadFont |
| 🚧 |
getFontPage |
| 🚧 |
getPageGlyph |
| 🚧 |
getGlyphKerning |
| 🚧 |
getTextWidth |
Raw Framebuffer
| Status |
Name |
| 🚧 |
getFrame |
| 🚧 |
getDisplayFrame |
| 🚧 |
getDebugFrame |
| 🚧 |
copyFrameBufferBitmap |
| 🚧 |
markUpdatedRows |
| ✅ |
display |
JSON
Note: it may not be worth implementing these since we have JSONDecoder.
Lua
| Status |
Name |
| 🚧 |
addFunction |
| 🚧 |
registerClass |
| 🚧 |
pushFunction |
| 🚧 |
indexMetatable |
| 🚧 |
start |
| 🚧 |
stop |
| 🚧 |
getArgCount |
| 🚧 |
getArgCount |
| 🚧 |
argIsNil |
| 🚧 |
getArgBool |
| 🚧 |
getArgInt |
| 🚧 |
getArgFloat |
| 🚧 |
getArgString |
| 🚧 |
getArgBytes |
| 🚧 |
getArgObject |
| 🚧 |
getBitmap |
| 🚧 |
getSprite |
| 🚧 |
pushNil |
| 🚧 |
pushBool |
| 🚧 |
pushInt |
| 🚧 |
pushFloat |
| 🚧 |
pushString |
| 🚧 |
pushBytes |
| 🚧 |
pushBitmap |
| 🚧 |
pushSprite |
| 🚧 |
pushObject |
| 🚧 |
retainObject |
| 🚧 |
releaseObject |
| 🚧 |
setObjectValue |
| 🚧 |
getObjectValue |
| 🚧 |
callFunction |
Scoreboards
Note: these are not documented anywhere.
| Status |
Name |
| 🚧 |
addScore |
| 🚧 |
getPersonalBest |
| 🚧 |
freeScore |
| 🚧 |
getScoreboards |
| 🚧 |
freeBoardsList |
| 🚧 |
getScores |
| 🚧 |
freeScoresList |
Sound
TODO
Sprite
| Status |
Name |
| ✅ |
newSprite |
| ✅ |
moveTo |
| ✅ |
moveBy |
| ✅ |
getPosition |
| ✅ |
addSprite |
| ✅ |
setImage |
| ✅ |
freeSprite |
| 🚧 |
copy |
| 🚧 |
setBounds |
| 🚧 |
getBounds |
| 🚧 |
getImage |
| 🚧 |
setSize |
| 🚧 |
setZIndex |
| 🚧 |
getZIndex |
| 🚧 |
setTag |
| 🚧 |
getTag |
| 🚧 |
setDrawMode |
| 🚧 |
setImageFlip |
| 🚧 |
getImageFlip |
| 🚧 |
setStencil |
| 🚧 |
setStencilPattern |
| 🚧 |
clearStencil |
| 🚧 |
setClipRect |
| 🚧 |
clearClipRect |
| 🚧 |
setClipRectsInRange |
| 🚧 |
clearClipRectsInRange |
| 🚧 |
setUpdatesEnabled |
| 🚧 |
updatesEnabled |
| 🚧 |
setVisible |
| 🚧 |
isVisible |
| 🚧 |
setOpaque |
| 🚧 |
setAlwaysRedraw |
| 🚧 |
markDirty |
| 🚧 |
addDirtyRect |
| 🚧 |
setIgnoresDrawOffset |
| 🚧 |
setUpdateFunction |
| 🚧 |
setDrawFunction |
| 🚧 |
setUserData |
| 🚧 |
getUserData |
| 🚧 |
removeSprite |
| 🚧 |
removeSprites |
| 🚧 |
removeAllSprites |
| 🚧 |
getSpriteCount |
| 🚧 |
drawSprites |
| ✅ |
updateAndDrawSprites |
| 🚧 |
resetCollisionWorld |
| 🚧 |
setCollisionsEnabled |
| 🚧 |
collisionsEnabled |
| 🚧 |
setCollideRect |
| 🚧 |
getCollideRect |
| 🚧 |
clearCollideRect |
| 🚧 |
setCollisionResponseFunction |
| 🚧 |
checkCollisions |
| 🚧 |
moveWithCollisions |
| 🚧 |
querySpritesAtPoint |
| 🚧 |
querySpritesInRect |
| 🚧 |
querySpritesAlongLine |
| 🚧 |
querySpriteInfoAlongLine |
| 🚧 |
overlappingSprites |
| 🚧 |
allOverlappingSprites |
System
| Status |
Name |
| ✅ |
realloc |
| ✅ |
error |
| ✅ |
logToConsole |
| ✅ |
error |
Menu
| Status |
Name |
| ✅ |
addMenuItem |
| ✅ |
addCheckmarkMenuItem |
| ✅ |
addOptionsMenuItem |
| ✅ |
removeMenuItem |
| ✅ |
removeAllMenuItems |
| ❌ |
getMenuItemTitle |
| ❌ |
getMenuItemValue |
| ❌ |
setMenuItemValue |
| ❌ |
getMenuItemUserData |
| ❌ |
setMenuItemUserData |
| ✅ |
setMenuImage |
Miscellaneous
| Status |
Name |
| ✅ |
getCurrentTimeMilliseconds |
| ✅ |
getSecondsSinceEpoch |
| ✅ |
resetElapsedTime |
| ✅ |
getElapsedTime |
| ✅ |
getFlipped |
| ✅ |
getReduceFlashing |
| ❌ |
formatString |
| ✅ |
setUpdateCallback |
| ✅ |
drawFPS |
| ✅ |
getBatteryPercentage |
| ✅ |
getBatteryVoltage |
| ✅ |
getLanguage |
| ✅ |
setPeripheralsEnabled |
| ✅ |
getAccelerometer |
| ✅ |
getButtonState |
| ✅ |
getCrankAngle |
| ✅ |
getCrankChange |
| ✅ |
getCrankDocked |
| ✅ |
setAutoLockDisabled |
| ✅ |
setCrankSoundDisabled |