SPACE
to play / pauseARROW RIGHT
orL
to go forwardARROW LEFT
orJ
to go backwardARROW UP
to increase volumeARROW DOWN
to decrease volumeF
to toggle fullscreenM
to toggle mute0 to 9
to go to the corresponding part of the videoSHIFT
+,
to decrease playback speedSHIFT
+.
or;
to increase playback speed
Others
- Leva
- @react-three/rapier
- KeyboardControls (Github drei)
- Bebas Neue (Google Fonts)
- Zustand (Github)
- Zustand (website)
Rapier
Three.js documentation
Shortcuts ⌨️
⚠️ Update
Use version 1.4
:
npm install @react-three/rapier@1.4
⚠️ Update
In the latest versions of @react-three/rapier
, the debug mode is activated using the debug
attribute (not <Debug>
):
<Physics debug>
⚠️ Update
In the latest version of @react-three/rapier
and rapier
, a RigidBody
falls asleep after a few seconds of inaction.
This would result in the sphere not moving even though the player presses the arrow keys.
To fix it, set the canSleep
attribute to false
on the <RigidBody>
:
<RigidBody canSleep={ false } colliders="ball" restitution={ 0.2 } friction={ 1 } position={ [ 0, 1, 0 ] }>
{/* ... */}
</RigidBody>
⚠️ Update
In the latest versions of @react-three/rapier
, useRapier
returns the actual Rapier world.
You don’t need to use world.raw()
and you can use world
directly.
⚠️ Update
In the latest versions of @react-three/rapier
, toi
has been renamed timeOfImpact
.
console.log(hit.timeOfImpact)
⚠️ Update
In the latest versions of @react-three/rapier
, the debug mode is activated and deactivated using the debug
attribute (not <Debug>
):
<Physics debug={ false }>
⚠️ Update
Since we are now using Vite.js
, the index.html
is now located in the src/
folder.
⚠️ Update
Use version 4.5
:
npm install zustand@4.5
⚠️ Update
In the latest versions of zustand
you need to import create
like this:
import { create } from 'zustand'
⚠️ Update
In the latest versions of @react-three/drei
, the default size of <Text>
is bigger.
Set the scale
to 0.5
instead of 4
:
<Text scale={ 0.5 }></Text>
⚠️ Update
In the latest versions of @react-three/drei
, the default size of <Text>
is bigger.
Set the scale
to 1
instead of 8
:
<Text scale={ 1 }></Text>
Unlock content 🔓
To get access to 93 hours of video, a members-only Discord server, subtitles, lesson resources, future updates and much more join us for only $95!
Want to learn more? 👋
That's the end of the free part 😔
To get access to 93 hours of video, a members-only Discord server and future updates, join us for only $95!
Create a game
Difficulty Hard
Introduction 00:00
In this lesson, we are going to learn how to create a mini-game.
We play a marble and we have to go through a level filled with various moving obstacles in order to reach the destination.
Here are some of the mechanics:
- The player can move the marble and make it jump with the keyboard.
- As soon as the marble is moving, a timer starts running and it indicates to the player how long it took to finish the obstacle course.
- At the end of the race, a “restart” button appears and by clicking on it, it will reset the marble to the initial position, reset the timer and create a new set of obstacles so that the level is never the same.
This lesson is a good opportunity to put into practice the knowledge we gathered like physics, interface & components but also new concepts like a global state or keyboard controls.
And obviously, we get to create and play a game!
Setup 00:49
In the starter, we have the classic orange sphere, purple cube, and green floor.
Both the directional light source and an ambient light source are in a <Lights>
component because we are going to make some tweaks related to lights a bit later and we want to keep things organised.
Shadows are already enabled and the directional light is set to cast the shadows within a pretty large area:
<directionalLight
castShadow
position={ [ 4, 4, 1 ] }
intensity={ 1.5 }
shadow-mapSize={ [ 1024, 1024 ] }
shadow-camera-near={ 1 }
shadow-camera-far={ 10 }
shadow-camera-top={ 10 }
shadow-camera-right={ 10 }
shadow-camera-bottom={ - 10 }
shadow-camera-left={ - 10 }
/>
The @react-three/drei
dependency is already installed within the project.
We are using the OrbitControls
helper to be able to move the camera around, but we are going to remove it later so that we can have the camera follow the marble.
We haven’t added <Perf />
from r3f-perf
but you should definitely use it if you create your own game. Having a good frame rate is very important and monitoring performance will help you.
No debug UI has been added because all the various values and colors have already been carefully chosen, but you should definitely add one (like Leva) if you were to create your own game or if you want to improve this game once you are done with the lesson.
Level 01:53
When creating a game, it’s good to have things on screen as quickly as possible. Even if the mechanics don’t work yet, it helps to get an idea of what’s coming up and it’s much more interesting.
That’s why we are going to start by creating the level and its various traps.
Our level will be composed of what we are going to call blocks.
The first block will be composed of a simple floor without anything else. It’s where the player will start.
The last block will be the finish line.
In between, there will be a bunch of trap blocks with moving obstacles.
We are going to create 3 different types of traps and then populate the blocks between the start and the end with a random set composed of those trap blocks.
Component
In /src/
, create a Level.jsx
file, export a Level
function component and put the 3 meshes from Experience
in it:
export default function Level()
{
return <>
<mesh castShadow position-x={ - 2 }>
<sphereGeometry />
<meshStandardMaterial color="orange" />
</mesh>
<mesh castShadow position-x={ 2 } scale={ 1.5 }>
<boxGeometry />
<meshStandardMaterial color="mediumpurple" />
</mesh>
<mesh receiveShadow position-y={ - 1 } rotation-x={ - Math.PI * 0.5 } scale={ 10 }>
<planeGeometry />
<meshStandardMaterial color="greenyellow" />
</mesh>
</>
}
In Experience
, import Level
:
import Level from './Level.jsx'
Instantiate it after the <Lights>
:
export default function Experience()
{
return <>
<OrbitControls makeDefault />
<Lights />
<Level />
</>
}
We have our little scene back, but now it’s set in the <Level>
.
Add Physics
The game will rely heavily on physics and we are going to use Rapier with @react-three/rapier as seen in the previous lesson.
How to use it 🤔
- Download the Starter pack or Final project
- Unzip it
- Open your terminal and go to the unzip folder
-
Run
npm install
to install dependencies
(if your terminal warns you about vulnerabilities, ignore it) -
Run
npm run dev
to launch the local server
(project should open on your default browser automatically) - Start coding
- The JS is located in src/script.js
- The HTML is located in src/index.html
- The CSS is located in src/style.css