-- Character controller in 2D: a platformer drawn on the overlay with sprites and boxes, no GPU -- needed. The player runs right, hops a gap, climbs a step, is stopped by a wall and jumps onto a -- ledge, on a scripted route; gravity points down the screen (+Y) like every 2D game here. local font = fontLoad("Singe/FreeSansBold.ttf", 24) local crate = spriteLoad("testScripts/crate.png") local frames = 0 overlaySetResolution(720, 480) local width, height = overlayGetWidth(), overlayGetHeight() fontSelect(font) physicsSet2D(true) physicsSetGravity(0, 900, 0) local blocks = {} local function block(x, y, w, h) local node = nodeNew() nodeSetPosition(node, x, y, 0) bodyNew(node, BODY_STATIC, SHAPE_BOX, w, h, 50) blocks[#blocks + 1] = { x = x, y = y, w = w, h = h } return node end -- Ground with a gap, a low step, a wall, and a ledge to jump onto. local ground = height - 20 block(width * 0.2, ground, width * 0.4, 20) block(width * 0.72, ground, width * 0.56, 20) block(width * 0.55, ground - 22, 60, 24) block(width * 0.85, ground - 60, 20, 100) block(width * 0.92, ground - 140, 140, 20) local size = spriteGetWidth(crate) local player = nodeNew() nodeSetPosition(player, 40, ground - 10, 0) playerNew(player, size * 0.45, size * 1.4) playerSetStep(player, 26) local hops = 0 function onOverlayUpdate() local px, py = nodeGetPosition(player) frames = frames + 1 playerMove(player, (px < width * 0.9) and 220 or 0) -- Hop the gap, and later the wall, when the ground runs out ahead or the wall is near. if playerIsOnGround(player) and ((px > width * 0.36 and px < width * 0.42) or (px > width * 0.70 and px < width * 0.75)) then if playerJump(player, 560) then hops = hops + 1 end end overlayClear() colorForeground(90, 90, 110) for _, b in ipairs(blocks) do overlayBox(b.x - b.w / 2, b.y - b.h / 2, b.x + b.w / 2, b.y + b.h / 2) end spriteDraw(crate, px - size / 2, py - size * 1.4, px + size / 2, py) colorForeground(255, 255, 255) fontPrint(20, 20, string.format("Platformer, frame %d x %.0f y %.0f ground %s hops %d", frames, px, py, tostring(playerIsOnGround(player)), hops)) if frames % 10 == 0 then local vx, vy = playerGetVelocity(player) debugPrint(string.format("frame %d player %.0f %.0f v %.0f %.0f ground %s hops %d", frames, px, py, vx, vy, tostring(playerIsOnGround(player)), hops)) end if frames == 40 or frames == 90 or frames == 140 then singeScreenshot() end if frames == 180 then singeQuit() end return OVERLAY_UPDATED end