name: viverse-r3f-foundation
description: Build browser 3D games with React Three Fiber and @react-three/viverse. Use for initial scene setup, character controls, physics body setup, and VIVERSE-ready project foundations.
prerequisites: [React, three, @react-three/fiber, VIVERSE app ID]
tags: [viverse, r3f, threejs, react, game-foundation]
VIVERSE R3F Foundation
Foundation skill for browser mini-games using @react-three/viverse.
When To Use This Skill
Use when you need:
- a fast R3F + VIVERSE starter scene
- character movement and simple platform/arena gameplay
- physics-ready level geometry for browser mini-games
Read Order
- This file
- Viverse simple game tutorial
- Viverse publish tutorial
../viverse-auth/SKILL.md
../viverse-world-publishing/SKILL.md
Preflight Checklist
Implementation Workflow
- Set up
<Canvas> and wrap game content with <Viverse>.
- Add environment lights and sky.
- Add controllable player with
<SimpleCharacter />.
- Add collision-ready geometry inside
<BvhPhysicsBody>.
- Add respawn/reset logic in frame update for fail-safe gameplay.
- Wire auth/profile and publish pipeline from existing VIVERSE core skills.
Full-path controls without avatar (tested pattern)
For games that do not use SimpleCharacter (for example tank/driving games), you can still adopt pmndrs input framework directly:
- Create an
InputSystem on the canvas DOM element (onCreated -> gl.domElement).
- Register built-in inputs:
ScreenJoystickInput (mobile movement joystick)
LocomotionKeyboardInput (desktop movement keys)
- Add one custom input plugin for game-specific actions (for example turret left/right + fire).
- Define custom
InputFields for game-specific actions and map them into your game control state each frame.
- Keep gameplay/controller logic separate from input providers; swap providers without changing gameplay.
This allows framework-standard mobile joystick UX while preserving non-avatar gameplay.
Input action "one-shot" pattern (required for non-continuous actions)
For actions like weapon switch, interact, or mode-toggle:
- Define a dedicated custom
InputField (for example WeaponSwitchField).
- Store pending action in custom input plugin state (for example
pendingWeaponChoice).
- Return action once from
get(field).
- Immediately clear pending action after read.
Why:
- Prevents repeated triggers while key/button is held.
- Keeps input deterministic and easy to reason about in frame update loops.
Minimal scaffold:
<Canvas shadows>
<Viverse>
<Sky />
<directionalLight intensity={1.2} position={[5, 10, 10]} castShadow />
<ambientLight intensity={1} />
<SimpleCharacter />
<BvhPhysicsBody>
<PrototypeBox scale={[10, 1, 15]} position={[0, -0.5, 0]} />
</BvhPhysicsBody>
</Viverse>
</Canvas>
Verification Checklist
Critical Gotchas
- Keep scope small for browser mini-games; avoid large maps and heavy shaders.
- Keep auth and App ID config in sync with publish target app.
- Rebuild after env changes before publishing (
import.meta.env is build-time).
- Do not duplicate auth state hooks across multiple top-level UI components.
ScreenJoystickInput alone does not cover custom combat actions; add a custom input plugin for action fields.
SimpleCharacter is optional; pmndrs input framework can be used directly for vehicle/tank controllers.
- Keep gameplay state deterministic and authoritative; treat visual effects (shot/hit/explosion) as render-layer events with short TTL.
- Cap transient FX collections (for example
slice(-N)) to avoid memory/perf drift during long playtests.
- Timestamp-based effects/buffs (
until fields) are more robust than many ad-hoc timers spread across components.
References