Ok, here's a dump of what I've been working on with this.
First, my work on this doesn't represent Panic. This is just something I’ve been doing in my free time for fun.
As a quick summary, I'm developing for Playdate in TypeScript using TypeScriptToLua. TSTL transpiles TypeScript to Lua, and given a TypeScript declaration file that defines a Lua API it can produce Lua that calls that API.
TSTL also supports plugins, which allows for custom code transformations. This is useful for weird specifics of some Lua runtimes. @orta wrote a great, concise plugin for using import
rather than require
, and I’ve expanded on that to write an apparently functional plugin for Playdate’s custom OOP stuff. As a caveat, my plugin doesn't yet handle class definitions as exports (export default class Blah {}
). If you want to export a class from a module, define it first then export it separately. Also, the code is a mess.
After all that, here’s an example of some TypeScript and the Lua it produces.
require(“CoreLibs/graphics”);
require(“CoreLibs/sprites”);
require(“CoreLibs/object”);
class Ball extends playdate.graphics.sprite {
radius = 10;
pos = {
x: 40,
y: 20,
};
velocity = {
// initial horizontal velocity
dx: 8,
dy: 0,
};
acceleration = {
dx: 0,
// gravity
dy: 2,
};
constructor() {
super();
this.setBounds(0, 0, 20, 20);
this.setCenter(0.5, 0.5);
}
update() {
this.velocity.dx += this.acceleration.dx;
this.velocity.dy += this.acceleration.dy;
const x = this.pos.x + this.velocity.dx;
const y = this.pos.y + this.velocity.dy;
if (x - this.radius < 0 || x + this.radius > playdate.display.getWidth()) {
this.velocity.dx *= -1;
} else {
this.pos.x = x;
}
if (y - this.radius < 0 || y + this.radius > playdate.display.getHeight()) {
this.velocity.dy *= -1;
} else {
this.pos.y = y;
}
this.moveTo(this.pos.x, this.pos.y);
}
draw(x: number, y: number, width: number, height: number) {
playdate.graphics.drawCircleAtPoint(this.radius, this.radius, this.radius);
}
}
const ball = new Ball();
ball.add();
playdate.update = () => {
playdate.graphics.sprite.update();
};
becomes...
-- a bunch of bundling stuff and library code
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
import(“CoreLibs/graphics”)
import(“CoreLibs/sprites”)
import(“CoreLibs/object”)
class(“Ball”).extends(playdate.graphics.sprite)
Ball.init = function(self)
Ball.super.init(self)
self.radius = 10
self.pos = {x = 40, y = 20}
self.velocity = {dx = 8, dy = 0}
self.acceleration = {dx = 0, dy = 2}
self:setBounds(0, 0, 20, 20)
self:setCenter(0.5, 0.5)
end
function Ball.update(self)
local ____self_velocity_0, ____dx_1 = self.velocity, “dx”
____self_velocity_0[____dx_1] = ____self_velocity_0[____dx_1] + self.acceleration.dx
local ____self_velocity_2, ____dy_3 = self.velocity, “dy”
____self_velocity_2[____dy_3] = ____self_velocity_2[____dy_3] + self.acceleration.dy
local x = self.pos.x + self.velocity.dx
local y = self.pos.y + self.velocity.dy
if x - self.radius < 0 or x + self.radius > playdate.display.getWidth() then
local ____self_velocity_4, ____dx_5 = self.velocity, “dx”
____self_velocity_4[____dx_5] = ____self_velocity_4[____dx_5] * -1
else
self.pos.x = x
end
if y - self.radius < 0 or y + self.radius > playdate.display.getHeight() then
local ____self_velocity_6, ____dy_7 = self.velocity, “dy”
____self_velocity_6[____dy_7] = ____self_velocity_6[____dy_7] * -1
else
self.pos.y = y
end
self:moveTo(self.pos.x, self.pos.y)
end
function Ball.draw(self, x, y, width, height)
playdate.graphics.drawCircleAtPoint(self.radius, self.radius, self.radius)
end
ball = Ball()
ball:add()
playdate.update = function()
playdate.graphics.sprite.update()
end
end,
}
and the program running:
Here’s a zip of the project, including the TSTL plugin and TS declaration file for the Playdate Lua API. Assuming you have node and npm installed, you should be able to run npm install
then npm run build
to generate a Source.pdx you can run in the simulator or on device.
tstlexport.zip (23.2 KB)
In my local development I have my TSTL plugin and .d.ts type declaration as separate packages that I symlink with npm link
, but I’ve just tossed them in here to share. I haven’t had the momentum to publish either on npm, but I welcome anyone to use that code however they like, including publishing it as a package if you want.
The TypeScript declaration was just made from me copying the Lua API while watching TV or whatever, and is not based on any proprietary information. Some of it is likely wrong; I make corrections to it when I find something broken, but I haven’t written enough TypeScript to cover the entire giant Playdate API.
I’m working on a larger game in TypeScript (again, personal project, doesn't represent Panic) which is making me think about the performance of the transpiled Lua. The first half of this post talks about loops: Fewer milliseconds.md · GitHub
Hopefully this is enough to get anyone started developing in TypeScript.