+ Move · hold to drive
+ {[["Forward", 1, 0], ["Backwards", -1, 0], ["Turn left", 0, 1], ["Turn right", 0, -1]].map(([label, forward, turn]) => drive(e, forward, turn)} onPointerUp={release} onPointerCancel={release} onLostPointerCapture={release} onKeyDown={(e) => { if (e.key === " " || e.key === "Enter") { e.preventDefault(); gameApi.setDrive?.(forward, turn); } }} onKeyUp={release} onBlur={release}>{label} )}
+ Actions
+
+ action("Kick left", () => gameApi.triggerKick?.("left"))}>Kick left
+ action("Kick right", () => gameApi.triggerKick?.("right"))}>Kick right
+ action("Roll", () => gameApi.triggerRoll?.())}>Roll
+ action("Sit", () => gameApi.setMode?.("sit"))}>Sit
+ action("Stand / Walk", () => gameApi.setMode?.("walk"))}>Stand / Walk
+ action("Crouch", () => gameApi.triggerCrouch?.())}>Crouch
+ action("Ground pick", () => gameApi.triggerGroundPick?.())}>Ground pick
+ action("Microduck quack", () => gameApi.quack?.())}>Quack
+ action("Ball", () => gameApi.spawnBall?.())}>Ball
+ action("Reset Microduck", () => gameApi.resetSim?.())}>Reset
+
+ Emotions
+ {[["joy", "Happy"], ["sadness", "Sad"], ["anger", "Angry"]].map(([id, label]) => action(`Microduck ${label}`, () => gameApi.playMicroduckEmotion?.(id))}>{label} )}
+ Head
+ action("Toggle head control", () => gameApi.toggleHeadMode?.())}>Toggle gamepad head control
+ {["Neck pitch", "Head pitch", "Head yaw", "Head roll"].map((label, i) => {label} { const next = [...head]; next[i] = Number(e.target.value); setHead(next); gameApi.setHead?.(next); }} /> )}
+ action("Neutral head", () => { setHead([0, 0, 0, 0]); gameApi.setHead?.([0, 0, 0, 0]); })}>Neutral head
+ Appearance & locomotion
+
+ Camera
+ action("Chase camera", () => gameApi.toggleCamera?.())}>Chase camera action("Both robots camera", () => gameApi.sharedCamera?.())}>Both robots
+
+