DEV Community

Cover image for Create a First Person Movement in React Three Fiber - Part 2

Create a First Person Movement in React Three Fiber - Part 2

Continuing with the series, in this post we are going to see the real code for this project. As always, the entire project is available in my personal repo below:

GitHub logo jgcarrillo / react-fp-movement

Basic scene with geometries and gLTF models to control a character in first person camera

React Three Fiber - First Person Movement

Table of contents πŸ‘‡

✨ Motivation

A few months ago I started to working with React Three Fiber. I was impressed with the possibilities that the library gives you to create 3D environments. I saw a wide variety of examples in R3F website so I started to explore more about the project.

There are so many good examples out there, but I get motivated with some of them, for example, a Ping Pong game or event a simple Arkanoid clone.

Since that, I have working in so many pet projects to understand the key concepts of React Three Fiber and also Three.js. I don't consider myself a R3F pro, nor even a beginner, but now I feel confident about the fact that I can create such 3D scenarios.

…

Physics 🧲

For this example we are going to use Reac Three Cannon a specific library to create physics. You can use Rapier, another cool library, but is under development right now.

To set up physics you need to import the appropriate geometry such as plane geometry, sphere geometry, etc. Then you need to establish the physics options like mass, position, etc.

In the code below you can see the floor component a 2D plane. We need that the plane has physics but no mass since in that case it would go into a downward loop.

// Floor.js

import { usePlane } from '@react-three/cannon';

const Floor = (props) => {
  const [ref] = usePlane((index) => ({ type: 'Static', mass: 0, ...props }));

  return (
    <mesh receiveShadow rotation={props.rotation} ref={ref}>
      <planeGeometry args={[1000, 1000]} />
      <meshStandardMaterial color={props.color} />
    </mesh>
  );
};
Enter fullscreen mode Exit fullscreen mode

From now, every of our components will have physics as they would otherwise go into an infinite drop and behave strangely.

Important components πŸ“š

We are not going to see all the code of the components but it's important to know some key concepts. The next component is ThreeModel.js which is the model of the three. To do this I have used a model from internet and this utility to create a React component from a gLTF.

glTF (derivative short form of Graphics Language Transmission Format or GL Transmission Format) is a standard file format for three-dimensional scenes and models. Wikipedia

The point is that you need to set up the physics as following. The important thing to know is that you need to set the size of the hit box that corresponds to the model.

In the following image from here you can see the concept.

Hit box of a character

So, with this code I have created a hit box with the same size as the three. I know it's difficult at first. We are receiving all these options through props to make the component more reusable.

// Physics for ThreeModel.js
// Don't forget to put your models inside the public folder

const [ref] = useBox((index) => ({
  type: 'Static',
  mass: 1,
  args: props.args,
  position: props.position,

  ...props,
}));
Enter fullscreen mode Exit fullscreen mode

Probably the most tricky component is the character because we need to define physics, movement and also the position of the camera. It has a lot of code but we are going to see the most interesting.

We use the hook useThree to access to the state model such as camera, renderer, scene, etc. Then, we need to define the position of the world. Finally, we set up the character movement.

// BaseCharacter.js

// Access to the state model
const { camera } = useThree();

// Set the current World position
ref.current.getWorldPosition(camera.position);

// Set the direction of the character
direction.subVectors(frontVector, sideVector).normalize().multiplyScalar(SPEED).applyEuler(camera.rotation);
Enter fullscreen mode Exit fullscreen mode

Another important component is the Scene. In this case I have create a basic scene* one. The important thing to know is the use of React children prop to reuse the scene.

Here, you need to notice that I'm using <PointerLockControls /> a component of Drei library to use the mouse as the same way you would use in a FPS.

import { Canvas } from '@react-three/fiber';
import { Loader, PointerLockControls } from '@react-three/drei';
import { Physics } from '@react-three/cannon';

import Lights from '../components/Lights.js';
import Floor from '../components/Floor.js';

const BasicScene = ({ children }) => {
  return (
    <div>
      <Canvas shadows camera={{ fov: 50 }}>
        <Lights />

        <Physics gravity={[0, -9.8, 0]}>
          {children}

          <Floor rotation={[Math.PI / -2, 0, 0]} color="white" />
        </Physics>

        <PointerLockControls />
      </Canvas>
      <Loader />
    </div>
  );
};

export default BasicScene;
Enter fullscreen mode Exit fullscreen mode

Another importat thing is that the physics only works within Physics component.

The helper.js file contains the movement for different geometries. I'm not going to explain this because I found it on internet in one of the R3F examples and it's not difficult to set up.

Creating the scene 🎭

So, that's it! The next step is to create the scene within the App.js component. It's also important to set up the CSS styles for the canvas.

* {
  box-sizing: border-box;
}

html,
body,
canvas {
  width: 100%;
  max-height: 100vh;
  max-width: 100wh;
  height: 100%;
  margin: 0;
  padding: 0;
}
Enter fullscreen mode Exit fullscreen mode

I hope you find it useful as I did. You can see the final project here. Feel free to reach me out and start creating your own projects. πŸ’ͺπŸ’ͺ

Top comments (1)

Collapse
 
amshenshanu07 profile image
AmshenShanu

Great Explation, Plzz Don't lose motivation and continue writing blogs like this