Short, copyable solutions for everyday game tasks.
Queue per-frame moves on world.transforms from onUpdate; they apply in one native call when GameView steps the world. Each recipe returning a function is meant to be called from onUpdate.
Slide a box
React and React Native · shared engine API
import type { World } from "@nayan-ui/engine";export function addSlider(world: World) {const box = world.spawn({ mesh: "cube", position: [-4, 1, 0], color: "#33B3FF" });let x = -4;return (dt: number) => {x = x > 4 ? -4 : x + 2 * dt; // 2 units per secondworld.transforms.position(box, x, 1, 0);};}
A fixed floor and a dynamic ball: physics handles gravity and the bounce.
Bouncing ball
React and React Native · shared engine API
import { GameView, useWorld } from "@nayan-ui/engine";export function Bounce() {const world = useWorld(16, (w) => {w.spawn({ mesh: "plane", scale: [20, 1, 20], color: "#4C9A4C", physics: "fixed" });w.spawn({ mesh: "sphere", position: [0, 5, 0], color: "#FF801A", physics: { type: "dynamic", bounce: 0.7 } });});return world && <GameView source={world} camera={{ eye: [0, 4, 8], target: [0, 0, 0], fov: 1 }} style={{ flex: 1 }} />;}
useJoystick returns a mutable state: read it in onUpdate, never mirror it into React state. groundVelocity sets X/Z speed and keeps gravity.
Joystick player
React and React Native · shared engine API
import { GameView, Joystick, useJoystick, type Entity, type World } from "@nayan-ui/engine";export function Steer({ world, player }: { world: World; player: Entity }) {const stick = useJoystick();return (<><GameView source={world} style={{ flex: 1 }}onUpdate={() => world.set(player, { groundVelocity: [stick.x * 5, -stick.y * 5] })} /><Joystick state={stick} /></>);}
Cast a short ray down against the floor's layer and skip the jump while already rising.
Grounded jump
React and React Native · shared engine API
import type { Entity, World } from "@nayan-ui/engine";const at: [number, number, number] = [0, 0, 0];const vel: [number, number, number] = [0, 0, 0];// Player: a 1.8-unit capsule; floor on layer 1.export function jump(world: World, player: Entity) {if (!world.position(player, at) || !world.velocity(player, vel) || vel[1] > 0.1) return false;if (!world.raycast(at, [0, -1, 0], 1.05, 1)) return false;world.impulse(player, [0, 5, 0]);return true;}
A sensor reports contact without pushing. forEachCollision in onUpdate reads the last step's contacts, each once.
Coin pickup
React and React Native · shared engine API
import type { Entity, World } from "@nayan-ui/engine";export function addCoin(world: World, player: Entity) {const coin = world.spawn({ mesh: "cylinder", position: [3, 1, 0], scale: [0.6, 0.1, 0.6],color: "#FFCC1A", spin: [0, 2, 0], physics: { type: "fixed", sensor: true } });return () =>world.forEachCollision((a, b, { started }) => {if (started && ((a === coin && b === player) || (a === player && b === coin))) {world.despawn(coin);world.scoreboard.add(100);}});}
world.pick finds the entity under a tap; no physics body needed.
Tap to paint
React and React Native · shared engine API
import { GameView, type World } from "@nayan-ui/engine";export function TapToPaint({ world }: { world: World }) {return (<GameView source={world} style={{ flex: 1 }}onTap={(x, y) => {const hit = world.pick(x, y);if (hit) world.set(hit.entity, { color: "#FF4D66" });}} />);}
Set follow once and the engine steers the chaser along the ground; follow: null stops it.
Chaser
React and React Native · shared engine API
import type { Entity, World } from "@nayan-ui/engine";export function addChaser(world: World, player: Entity) {const enemy = world.spawn({ mesh: "sphere", position: [5, 1, 5], color: "red", follow: { target: player, speed: 2 } });return () => world.set(enemy, { follow: null }); // stop chasing}
effects.pop swells, flashes and shrinks a piece away; despawn when it resolves. No disposal guard is needed: despawn is a no-op on a disposed world.
Clear a piece
React and React Native · shared engine API
import { effects, type Entity, type World } from "@nayan-ui/engine";export async function clearPiece(world: World, piece: Entity) {await effects.pop(world, piece, { scale: 1, color: "#FF4D4D" });world.despawn(piece);}
scoreboard.listen adds a UI observer beside the game's handlers and returns its unlisten, so it fits a useEffect. It fires only when the value changes.
useScore
React and React Native · shared engine API
import { useEffect, useState } from "react";import type { World } from "@nayan-ui/engine";export function useScore(world: World) {const [score, setScore] = useState(world.scoreboard.score);useEffect(() => world.scoreboard.listen({ onScore: setScore }), [world]);return score;}
world.after counts game time, so it pauses with the game. Call cancelTimers on reset so an old round can't act in the new one.
Delayed bonus
React and React Native · shared engine API
import type { World } from "@nayan-ui/engine";export function scheduleBonus(world: World) {const id = world.after(3, () => world.spawn({ mesh: "torus", position: [0, 2, 0], color: "#FFCC1A" }));return () => world.cancelTimer(id);}
Mesh ids are never freed: build each model once per app (after warm-up, e.g. in useWorld's setup) and reuse it.
Reusable star
React and React Native · shared engine API
import { createShape, extrude, Path, type Model, type World } from "@nayan-ui/engine";let star: Model | undefined;const starMesh = () => (star ??= createShape(extrude(new Path().addStar(0, 0, 0.5, 0.22, 5), { depth: 0.15 }), { shine: 0.4 }));export function addStars(world: World) {for (let i = 0; i < 5; i++) world.spawn({ mesh: starMesh(), position: [i - 2, 1, 0], color: "#FFCC33" });}
Load the bank once, outside the frame loop (a URL on the web, require(...) on native). Mirror the player's settings onto audio.enabled and haptics.enabled, then play freely.
Pickup feedback
React and React Native · shared engine API
import { audio, haptics } from "@nayan-ui/engine";const sfx = audio.load({ pickup: "/sounds/pickup.wav" }); // native: require("./pickup.wav")export function applySettings(sounds: boolean, vibration: boolean) {audio.enabled = sounds;haptics.enabled = vibration;}export function onPickup() {sfx.play("pickup", { volume: 0.7 });haptics.selection();}
One call that cleans itself up. Leave room in the world's capacity for the particles (about count).
Confetti
React and React Native · shared engine API
import { effects, type World } from "@nayan-ui/engine";export function celebrate(world: World) {effects.confetti(world, { position: [0, 3, 0], colors: ["#FF4D4D", "#FFD933", "#4DB3FF"],size: 0.12, speed: 6, count: 40, lifetime: 1.4, gravity: -6 });}
quat.y builds the heading and quat.slerp eases toward it, both into reused arrays. For a dynamic body, rotate a visual child (parent: player) so physics keeps the body.
Turn toward the stick
React and React Native · shared engine API
import { quat, type Entity, type JoystickState, type MutableQuat, type World } from "@nayan-ui/engine";export function makeFacing(world: World, model: Entity, stick: JoystickState) {const facing: MutableQuat = [0, 0, 0, 1];const goal: MutableQuat = [0, 0, 0, 1];return (dt: number) => {if (stick.x || stick.y) quat.y(Math.atan2(stick.x, -stick.y), goal); // front faces +zquat.slerp(facing, goal, Math.min(1, dt * 12), facing);world.transforms.rotation(model, facing[0], facing[1], facing[2], facing[3]);};}