-- Particles, 2D: fireworks bursting over the disc, a smoke trail following a moving point, and -- sparks off a bouncing 2D physics ball, all drawn with the overlay. Runs without a GPU too. local font = fontLoad("Singe/FreeSansBold.ttf", 28) local frames = 0 local width = overlayGetWidth() local height = overlayGetHeight() fontSelect(font) discPlay() -- Fireworks: a burst of bright additive sparks that fall and fade. local fireworks = emitterNew() emitterSetBlend(fireworks, PARTICLE_ADD) emitterSetLife(fireworks, 0.8, 1.6) emitterSetSpeed(fireworks, 120, 320) emitterSetSpread(fireworks, 180) emitterSetGravity(fireworks, 0, 220) emitterSetDrag(fireworks, 0.8) emitterSetSize(fireworks, 14, 3, 0.3) emitterSetColor(fireworks, 255, 230, 120, 255, 255, 60, 20, 0) emitterSetMax(fireworks, 2000) -- Smoke: grey puffs drifting up from a moving point, drawn under the overlay text. local smoke = emitterNew() emitterSetLayer(smoke, PARTICLE_UNDER) emitterSetRate(smoke, 90) emitterSetLife(smoke, 1.2, 2.0) emitterSetSpeed(smoke, 30, 60) emitterSetDirection(smoke, 0, -1) emitterSetSpread(smoke, 25) emitterSetGravity(smoke, 0, -20) emitterSetSize(smoke, 12, 48, 0.2) emitterSetColor(smoke, 200, 200, 210, 160, 90, 90, 100, 0) emitterSetSpin(smoke, -60, 60) emitterSetRadius(smoke, 6) emitterStart(smoke) -- Sparks: short hot streaks thrown off a ball bouncing in a 2D physics world. physicsSet2D(true) physicsSetGravity(0, 600, 0) local floor = nodeNew() nodeSetPosition(floor, width / 2, height - 10, 0) bodyNew(floor, BODY_STATIC, SHAPE_BOX, width, 20, 50) local ball = nodeNew() nodeSetPosition(ball, width * 0.25, 80, 0) bodyNew(ball, BODY_DYNAMIC, SHAPE_SPHERE, 18) bodySetBounce(ball, 0.8) bodySetVelocity(ball, 140, 0, 0) local sparks = emitterNew() emitterSetBlend(sparks, PARTICLE_ADD) emitterSetRate(sparks, 160) emitterSetLife(sparks, 0.3, 0.7) emitterSetSpeed(sparks, 80, 200) emitterSetDirection(sparks, 0, -1) emitterSetSpread(sparks, 70) emitterSetGravity(sparks, 0, 500) emitterSetSize(sparks, 5, 1) emitterSetColor(sparks, 255, 255, 200, 255, 255, 120, 0, 0) emitterStart(sparks) local ballSprite = spriteLoad("testScripts/crate.png") function onOverlayUpdate() local t = frames / 60 local bx = nodeGetPosition(ball) local by = select(2, nodeGetPosition(ball)) frames = frames + 1 overlayClear() -- The smoke source circles slowly. emitterSetPosition(smoke, width / 2 + 160 * math.cos(t), height / 2 + 60 * math.sin(t * 1.3)) -- Sparks come from wherever the ball is. emitterSetPosition(sparks, bx, by + 14) if frames % 45 == 1 then emitterSetPosition(fireworks, 120 + (frames * 7) % (width - 240), 80 + (frames * 13) % 160) emitterBurst(fireworks, 260) end emitterDraw(smoke) emitterDraw(fireworks) emitterDraw(sparks) spriteDraw(ballSprite, bx - 18, by - 18, bx + 18, by + 18) fontPrint(20, 20, "Particles 2D, frame " .. frames .. " live " .. emitterGetCount(fireworks) + emitterGetCount(smoke) + emitterGetCount(sparks)) if frames == 40 or frames == 110 or frames == 170 then singeScreenshot() end if frames == 180 then singeQuit() end end