A collection of code patterns and examples for common game development tasks.
- Movement Patterns
- Collision Detection
- Animation
- Particle Systems
- Camera Techniques
- Map Scrolling
- Procedural Generation
- State Machines
- Object Pools
let player = { x: 64, y: 64, speed: 2 }
function _update() {
let dx = 0
let dy = 0
if (btn(0)) dx = -1 // Left
if (btn(1)) dx = 1 // Right
if (btn(2)) dy = -1 // Up
if (btn(3)) dy = 1 // Down
// Normalize diagonal movement
if (dx != 0 && dy != 0) {
dx *= 0.707 // 1/sqrt(2)
dy *= 0.707
}
player.x += dx * player.speed
player.y += dy * player.speed
// Keep on screen
player.x = mid(4, player.x, 124)
player.y = mid(4, player.y, 124)
}let player = {
x: 64,
y: 64,
vx: 0,
vy: 0,
accel: 0.5,
friction: 0.85,
maxSpeed: 4,
}
function _update() {
// Apply acceleration
if (btn(0)) player.vx -= player.accel
if (btn(1)) player.vx += player.accel
if (btn(2)) player.vy -= player.accel
if (btn(3)) player.vy += player.accel
// Apply friction
player.vx *= player.friction
player.vy *= player.friction
// Clamp speed
let speed = sqrt(player.vx * player.vx + player.vy * player.vy)
if (speed > player.maxSpeed) {
player.vx = (player.vx / speed) * player.maxSpeed
player.vy = (player.vy / speed) * player.maxSpeed
}
// Apply velocity
player.x += player.vx
player.y += player.vy
}let player = {
x: 64,
y: 100,
vx: 0,
vy: 0,
grounded: false,
}
const GRAVITY = 0.3
const JUMP_FORCE = -5
const MOVE_SPEED = 2
const FRICTION = 0.8
function _update() {
// Horizontal movement
if (btn(0)) player.vx = -MOVE_SPEED
else if (btn(1)) player.vx = MOVE_SPEED
else player.vx *= FRICTION
// Jumping
if (btnp(5) && player.grounded) {
player.vy = JUMP_FORCE
player.grounded = false
}
// Gravity
player.vy += GRAVITY
// Apply velocity
player.x += player.vx
player.y += player.vy
// Simple ground collision
if (player.y > 100) {
player.y = 100
player.vy = 0
player.grounded = true
}
}let angle = 0
let centerX = 64
let centerY = 64
let radius = 30
function _update() {
angle += 0.02
}
function _draw() {
cls(0)
// Calculate orbit position
let x = centerX + cos(angle) * radius
let y = centerY + sin(angle) * radius
circfill(centerX, centerY, 4, 5) // Center
circfill(x, y, 4, 11) // Orbiting object
}function circleCollision(x1, y1, r1, x2, y2, r2) {
let dx = x2 - x1
let dy = y2 - y1
let dist = sqrt(dx * dx + dy * dy)
return dist < r1 + r2
}
// Usage
if (circleCollision(player.x, player.y, 4, enemy.x, enemy.y, 6)) {
// Collision detected
}function boxCollision(x1, y1, w1, h1, x2, y2, w2, h2) {
return x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2
}
// Usage
if (boxCollision(player.x, player.y, 8, 8, enemy.x, enemy.y, 8, 8)) {
// Collision detected
}function pointInBox(px, py, bx, by, bw, bh) {
return px >= bx && px < bx + bw && py >= by && py < by + bh
}function getTile(x, y) {
let tileX = flr(x / 8)
let tileY = flr(y / 8)
return mget(tileX, tileY)
}
function isSolid(x, y) {
let tile = getTile(x, y)
return tile == 1 // Sprite 1 is solid
}
// Check before moving
function movePlayer(dx, dy) {
let newX = player.x + dx
let newY = player.y + dy
// Check corners
if (
!isSolid(newX, newY) &&
!isSolid(newX + 7, newY) &&
!isSolid(newX, newY + 7) &&
!isSolid(newX + 7, newY + 7)
) {
player.x = newX
player.y = newY
}
}let player = {
x: 64,
y: 64,
frame: 0,
animTimer: 0,
sprites: [0, 1, 2, 1], // Sprite indices
}
function _update() {
player.animTimer += 1
if (player.animTimer >= 8) {
// Change frame every 8 ticks
player.animTimer = 0
player.frame = (player.frame + 1) % player.sprites.length
}
}
function _draw() {
cls(0)
spr(player.sprites[player.frame], player.x, player.y)
}let player = {
x: 64,
y: 64,
state: 'idle',
frame: 0,
timer: 0,
}
let animations = {
idle: { sprites: [0, 1], speed: 15 },
walk: { sprites: [2, 3, 4, 3], speed: 6 },
jump: { sprites: [5], speed: 1 },
}
function _update() {
// Determine state
if (!player.grounded) {
player.state = 'jump'
} else if (btn(0) || btn(1)) {
player.state = 'walk'
} else {
player.state = 'idle'
}
// Animate
let anim = animations[player.state]
player.timer += 1
if (player.timer >= anim.speed) {
player.timer = 0
player.frame = (player.frame + 1) % anim.sprites.length
}
}
function _draw() {
cls(0)
let anim = animations[player.state]
spr(anim.sprites[player.frame], player.x, player.y)
}let ball = {
x: 64,
y: 64,
vy: 0,
scaleX: 1,
scaleY: 1,
}
function _update() {
ball.vy += 0.2 // Gravity
ball.y += ball.vy
// Bounce
if (ball.y > 100) {
ball.y = 100
ball.vy = -ball.vy * 0.8
// Squash on impact
ball.scaleX = 1.4
ball.scaleY = 0.6
}
// Stretch when falling fast
if (ball.vy > 2) {
ball.scaleX = 0.8
ball.scaleY = 1.3
}
// Return to normal
ball.scaleX += (1 - ball.scaleX) * 0.2
ball.scaleY += (1 - ball.scaleY) * 0.2
}
function _draw() {
cls(0)
let w = 8 * ball.scaleX
let h = 8 * ball.scaleY
ovalfill(ball.x - w / 2, ball.y - h / 2, ball.x + w / 2, ball.y + h / 2, 8)
}let particles = []
function spawnParticle(x, y) {
add(particles, {
x: x,
y: y,
vx: rnd(2) - 1,
vy: rnd(2) - 1,
life: 30,
col: flr(rnd(3)) + 8,
})
}
function _update() {
// Spawn on button press
if (btnp(5)) {
for (let i = 0; i < 10; i++) {
spawnParticle(64, 64)
}
}
// Update particles
for (let i = particles.length - 1; i >= 0; i--) {
let p = particles[i]
p.x += p.vx
p.y += p.vy
p.vy += 0.1 // Gravity
p.life -= 1
if (p.life <= 0) {
particles.splice(i, 1)
}
}
}
function _draw() {
cls(0)
for (let p of all(particles)) {
pset(p.x, p.y, p.col)
}
}function explode(x, y, count) {
for (let i = 0; i < count; i++) {
let angle = rnd(1)
let speed = rnd(3) + 1
add(particles, {
x: x,
y: y,
vx: cos(angle) * speed,
vy: sin(angle) * speed,
life: 20 + flr(rnd(20)),
col: flr(rnd(3)) + 8, // Red, orange, yellow
})
}
}let trail = []
let maxTrail = 20
function _update() {
// Player movement
if (btn(0)) player.x -= 2
if (btn(1)) player.x += 2
if (btn(2)) player.y -= 2
if (btn(3)) player.y += 2
// Add current position to trail
add(trail, { x: player.x, y: player.y }, 0)
// Remove old positions
while (trail.length > maxTrail) {
trail.shift()
}
}
function _draw() {
cls(0)
// Draw trail (older = darker)
for (let i = 0; i < trail.length; i++) {
let t = trail[i]
let col = flr((i / trail.length) * 7) + 1
circfill(t.x, t.y, 2, col)
}
// Draw player
circfill(player.x, player.y, 4, 7)
}function _draw() {
// Center camera on player
camera(player.x - 64, player.y - 64)
// Draw world
map(0, 0, 0, 0, 32, 32)
spr(0, player.x, player.y)
// Draw HUD (reset camera)
camera()
print('HP: ' + player.hp, 2, 2, 7)
}let cam = { x: 0, y: 0 }
let camSpeed = 0.1
function _update() {
// Target position (center on player)
let targetX = player.x - 64
let targetY = player.y - 64
// Smooth interpolation
cam.x += (targetX - cam.x) * camSpeed
cam.y += (targetY - cam.y) * camSpeed
}
function _draw() {
camera(cam.x, cam.y)
// ... draw world
}function updateCamera() {
let targetX = player.x - 64
let targetY = player.y - 64
// Clamp to world bounds
let worldWidth = 256 // World is 256 pixels wide
let worldHeight = 256
targetX = mid(0, targetX, worldWidth - 128)
targetY = mid(0, targetY, worldHeight - 128)
cam.x += (targetX - cam.x) * 0.1
cam.y += (targetY - cam.y) * 0.1
}let shake = 0
function doShake(intensity) {
shake = intensity
}
function _draw() {
// Apply shake
let shakeX = 0
let shakeY = 0
if (shake > 0) {
shakeX = rnd(shake * 2) - shake
shakeY = rnd(shake * 2) - shake
shake *= 0.9 // Decay
if (shake < 0.5) shake = 0
}
camera(cam.x + shakeX, cam.y + shakeY)
// ... draw world
}let scrollX = 0
let scrollSpeed = 1
function _update() {
scrollX += scrollSpeed
}
function _draw() {
cls(0)
// Draw two copies for seamless scrolling
let mapWidth = 128 * 8 // 128 tiles * 8 pixels
let offset = scrollX % mapWidth
camera(offset, 0)
map(0, 0, 0, 0, 128, 16)
camera(offset - mapWidth, 0)
map(0, 0, 0, 0, 128, 16)
camera()
}let layers = [
{ speed: 0.2, y: 80 }, // Far background
{ speed: 0.5, y: 60 }, // Mid background
{ speed: 1.0, y: 0 }, // Foreground
]
function _draw() {
cls(1)
for (let layer of all(layers)) {
let offset = flr(cam.x * layer.speed)
// Draw layer at offset
// (assumes each layer has its own map region)
}
}function generateRoom(x, y, w, h) {
// Floor
for (let ty = y; ty < y + h; ty++) {
for (let tx = x; tx < x + w; tx++) {
mset(tx, ty, 0) // Floor tile
}
}
// Walls
for (let tx = x; tx < x + w; tx++) {
mset(tx, y, 1) // Top wall
mset(tx, y + h - 1, 1) // Bottom wall
}
for (let ty = y; ty < y + h; ty++) {
mset(x, ty, 1) // Left wall
mset(x + w - 1, ty, 1) // Right wall
}
}
function _init() {
srand(12345) // Reproducible
// Clear map
for (let y = 0; y < 64; y++) {
for (let x = 0; x < 128; x++) {
mset(x, y, 1) // All walls
}
}
// Generate random rooms
for (let i = 0; i < 10; i++) {
let rx = flr(rnd(100)) + 5
let ry = flr(rnd(50)) + 5
let rw = flr(rnd(8)) + 4
let rh = flr(rnd(6)) + 4
generateRoom(rx, ry, rw, rh)
}
}// Simple 1D noise for terrain height
function noise(x) {
// Using sin for simple pseudo-noise
return sin(x * 0.1) * 0.5 + sin(x * 0.05) * 0.3 + sin(x * 0.02) * 0.2
}
function generateTerrain() {
for (let x = 0; x < 128; x++) {
let height = 32 + flr(noise(x) * 20)
for (let y = 0; y < 64; y++) {
if (y >= height) {
mset(x, y, 1) // Ground
} else {
mset(x, y, 0) // Air
}
}
}
}let state = 'title'
function _update() {
if (state == 'title') updateTitle()
else if (state == 'game') updateGame()
else if (state == 'gameover') updateGameOver()
}
function _draw() {
if (state == 'title') drawTitle()
else if (state == 'game') drawGame()
else if (state == 'gameover') drawGameOver()
}
function updateTitle() {
if (btnp(5)) {
state = 'game'
initGame()
}
}
function drawTitle() {
cls(1)
print('PRESS Z TO START', 20, 60, 7)
}
function updateGame() {
// ... game logic
if (player.hp <= 0) {
state = 'gameover'
}
}
function updateGameOver() {
if (btnp(5)) {
state = 'title'
}
}let enemy = {
x: 64,
y: 64,
state: 'patrol',
stateTimer: 0,
patrolDir: 1,
}
function updateEnemy() {
enemy.stateTimer += 1
if (enemy.state == 'patrol') {
enemy.x += enemy.patrolDir
// Turn around at edges
if (enemy.x < 20 || enemy.x > 108) {
enemy.patrolDir = -enemy.patrolDir
}
// Check for player
if (abs(player.x - enemy.x) < 30) {
enemy.state = 'chase'
enemy.stateTimer = 0
}
} else if (enemy.state == 'chase') {
// Move toward player
let dir = sgn(player.x - enemy.x)
enemy.x += dir * 1.5
// Give up after a while
if (enemy.stateTimer > 120) {
enemy.state = 'patrol'
enemy.stateTimer = 0
}
}
}let bullets = []
let maxBullets = 20
function _init() {
// Pre-create bullets
for (let i = 0; i < maxBullets; i++) {
add(bullets, { active: false, x: 0, y: 0, vx: 0, vy: 0 })
}
}
function fireBullet(x, y, vx, vy) {
// Find inactive bullet
for (let b of all(bullets)) {
if (!b.active) {
b.active = true
b.x = x
b.y = y
b.vx = vx
b.vy = vy
return
}
}
}
function _update() {
// Fire on button press
if (btnp(5)) {
fireBullet(player.x, player.y, 0, -4)
}
// Update active bullets
for (let b of all(bullets)) {
if (b.active) {
b.x += b.vx
b.y += b.vy
// Deactivate if off screen
if (b.y < -8 || b.y > 136 || b.x < -8 || b.x > 136) {
b.active = false
}
}
}
}
function _draw() {
cls(0)
// Draw active bullets
for (let b of all(bullets)) {
if (b.active) {
circfill(b.x, b.y, 2, 10)
}
}
}let flashTimer = 0
function flash() {
flashTimer = 5
}
function _draw() {
cls(0)
// ... draw game
// Flash overlay
if (flashTimer > 0) {
flashTimer -= 1
rectfill(0, 0, 128, 128, 7)
}
}let fadeLevel = 0 // 0 = normal, 15 = full black
function fadeOut() {
fadeLevel = min(15, fadeLevel + 1)
}
function fadeIn() {
fadeLevel = max(0, fadeLevel - 1)
}
function _draw() {
// Draw game normally
cls(0)
// ...
// Draw fade overlay using screen palette
if (fadeLevel > 0) {
// Darken all colors
for (let i = 0; i < 16; i++) {
let darkIndex = max(0, i - fadeLevel)
pal(i, darkIndex, 1) // Screen palette
}
}
}let timers = []
function setTimer(frames, callback) {
add(timers, { time: frames, fn: callback })
}
function updateTimers() {
for (let i = timers.length - 1; i >= 0; i--) {
timers[i].time -= 1
if (timers[i].time <= 0) {
timers[i].fn()
timers.splice(i, 1)
}
}
}
// Usage
setTimer(60, function () {
spawnEnemy()
})