Building a Simple Game
Learn how to create a 3D platformer game with character controls and physics.
In this tutorial, we'll build the following simple 3D platformer game using @react-three/viverse
with:
- Character movement (WASD + mouse look)
- Jumping mechanics
- Physics-based collision detection
- A simple level with platforms to jump on
- Respawn system when falling off the map
Here's a preview of what we will build in this tutorial:
import { Suspense } from "react" import { Canvas } from '@react-three/fiber' import { Viverse } from '@react-three/viverse' import { Scene } from "./Scene" export default function App() { return ( <Canvas style={{ position: "absolute", inset: "0", touchAction: "none" }} camera={{ fov: 90, position: [0, 2, 2] }} shadows > <Suspense fallback={null}> <Viverse> <Scene /> </Viverse> </Suspense> </Canvas> ) }
Step 0: Prerequisites
Make sure you have the required dependencies installed:
npm install three @react-three/fiber @react-three/viverse @react-three/drei
Step 1: Setting Up the Canvas
First, let's create the basic Canvas setup with shadows and proper camera settings:
import { Canvas } from '@react-three/fiber'
import { Viverse } from '@react-three/viverse'
import { Suspense } from 'react'
export function App() {
return (
<Canvas
style={{ position: "absolute", inset: "0", touchAction: "none" }}
camera={{ fov: 90, position: [0, 2, 2] }}
shadows
>
<Suspense fallback={null}>
<Viverse>
<Scene />
</Viverse>
</Suspense>
</Canvas>
)
}
Step 2: Adding the Scene and creating the Sky
Let's create another component called Scene
and add the sky and basic lighting. Add the Sky
import from @react-three/drei
:
import { Sky } from '@react-three/drei'
export function Scene() {
return (
<>
{/* Environment */}
<Sky />
{/* Basic lighting */}
<directionalLight
intensity={1.2}
position={[5, 10, 10]}
castShadow
/>
<ambientLight intensity={1} />
</>
)
}
At this point, you should see a beautiful sky gradient in your scene!
Step 3: Building the Level
Now add the level geometry. Import FixedBvhPhysicsBody
and PrototypeBox
from @react-three/viverse
, and expand the directional light with shadow properties:
import { Sky } from '@react-three/drei'
import { FixedBvhPhysicsBody, PrototypeBox } from '@react-three/viverse'
export function Scene() {
return (
<>
{/* Environment */}
<Sky />
{/* Lighting - expanded with shadow settings */}
<directionalLight
intensity={1.2}
position={[5, 10, 10]}
castShadow
/>
<ambientLight intensity={1} />
<FixedBvhPhysicsBody>
<PrototypeBox
color="#ffffff"
scale={[10, 0.5, 10]}
position={[0, -2, 0]}
/>
{/* Platforms */}
<PrototypeBox
color="#cccccc"
scale={[2, 1, 3]}
position={[4, 0, 0]}
/>
<PrototypeBox
color="#ffccff"
scale={[3, 1, 3]}
position={[3, 1.5, -1]}
/>
<PrototypeBox
color="#ccffff"
scale={[2, 0.5, 3]}
position={[2, 2.5, -3]}
/>
<PrototypeBox
color="#ffccff"
scale={[2, 1, 3]}
position={[-3, 0, -2]}
/>
<PrototypeBox
color="#ccffff"
scale={[1, 1, 4]}
position={[0, -1, 0]}
/>
<PrototypeBox
color="#ffffcc"
scale={[4, 1, 1]}
position={[0, 3.5, 0]}
/>
</FixedBvhPhysicsBody>
</>
)
}
Now you should see a colorful platformer level with various platforms at different heights!
Step 4: Adding the Character
Next we will add the character. Import SimpleCharacter
from @react-three/viverse
:
import { Sky } from '@react-three/drei'
import { SimpleCharacter, FixedBvhPhysicsBody, PrototypeBox } from '@react-three/viverse'
import { useRef } from 'react'
export function Scene() {
return (
<>
{/* Environment */}
<Sky />
{/* Lighting */}
<directionalLight
intensity={1.2}
position={[5, 10, 10]}
castShadow
/>
<ambientLight intensity={1} />
<SimpleCharacter/>
{/* Level Geometry */}
<FixedBvhPhysicsBody>
{/* ... platforms remain the same ... */}
</FixedBvhPhysicsBody>
</>
)
}
Great! Now you can move around with WASD keys, look around with the mouse, and jump with the spacebar. Try jumping between the platforms!
Step 5: Adding Respawn Logic
Finally, we will add the respawn system. Import useRef
from react
and useFrame
from @react-three/fiber
and add the respawn logic:
import { Sky } from '@react-three/drei'
import { SimpleCharacter, FixedBvhPhysicsBody, PrototypeBox } from '@react-three/viverse'
import { useRef } from 'react'
import { Group } from 'three'
import { useFrame } from '@react-three/fiber' // NEW
export function Scene() {
const characterRef = useRef<Group>(null)
// Respawn logic - NEW
useFrame(() => {
if (characterRef.current == null) {
return
}
if (characterRef.current.position.y < -10) {
characterRef.current.position.set(0, 0, 0)
}
})
return (
<>
<SimpleCharacter ref={characterRef}/>
{/* ... rest remains the same ... */}
</>
)
}
Perfect! Now if you fall off the map (below y = -10), you'll automatically respawn at the starting position (0, 0, 0).