Your first window
A project becomes a window app when its thicket.json has a window section. Without one it is a console app.
Make the project
sh
thicket new mygame window
cd mygameOpen thicket.json5 and give the window a title and a size. List the willow system, which is the 2D module, and turn on the fs permission so the game can load images from disk:
json5
{
entry: "app.tks",
systems: ["willow"],
window: { title: "My Game", width: 800, height: 600 },
permissions: { fs: true },
}window: {} also works and uses every default.
Show a sprite
Put an image at assets/player.png, then replace app.tks with this:
ts
import { Sprite } from "willow";
class Game {
static main(): void {
const player: Sprite = new Sprite("assets/player.png");
player.setAnchor(0.5, 0.5);
player.x = 400;
player.y = 300;
engine.root2d.add(player);
const SPEED: float = 200;
App.onFrame((dt: float): void => {
if (Keyboard.isDown(Key.RIGHT)) {
player.x += SPEED * dt;
}
if (Keyboard.isDown(Key.LEFT)) {
player.x -= SPEED * dt;
}
if (Keyboard.isDown(Key.DOWN)) {
player.y += SPEED * dt;
}
if (Keyboard.isDown(Key.UP)) {
player.y -= SPEED * dt;
}
player.rotation += Math.PI * dt;
});
}
}Run it:
sh
thicket runThe window opens with the sprite in the middle, spinning half a turn per second. The arrow keys move it.
What is going on
new Sprite(path)loads the image and makes a display node that draws it.setAnchor(0.5, 0.5)puts the sprite's origin at its center, soxandyplace the middle of the image and rotation turns it around its center.engine.root2dis the root of the 2D display tree. Anything youaddto it is drawn every frame.App.onFrameruns your function once per drawn frame.dtis the time since the last frame, in seconds, soSPEED * dtmoves the same distance per second at any frame rate.KeyboardandKeyare global. You do not import them.Keyboard.isDownis true while a key is held.Keyboard.isPressedis true only on the frame the key goes down.rotationis in radians.
Avoid naming your class App
App is the built-in lifecycle class. A class of your own called App hides it, and App.onFrame stops working in that file. Name your entry class after your game instead.
Game logic on fixed ticks
App.onFrame runs once per drawn frame, so it runs more often on a faster screen. For game logic that should run at a steady rate, use App.onTick. Ticks run 60 times a second by default (App.tps). Draw-only work, like smoothing an animation, stays in onFrame.
Learn more about the 2D module in willow.
Next: checking your code.