Core points
- Using React to drive 3D scenes in game development has many advantages, including clear separation of scene rendering from game logic, easy-to-understand components, real-time reloading of game resources, and the ability to use native browser tools to 3D scenes Check and debug as markers.
- react-three-renderer (R3R) provides a declarative API that encapsulates Three.js, allows decoupling view code from game logic, and creates small, easy-to-understand components.
- As game engines grow, it is crucial to use reducer mode to organize game logic into separate functions. This mode allows creating a clean and clear game loop and easily add more logic to the game loop.
- Draining and performance considerations are unique in building games using Three.js, React and WebGL. Using Chrome DevTools' timeline functionality is invaluable for debugging performance, and implementing strategies such as minimizing the number of re-renders in React can help optimize performance.
I am making a game called "Chameleon Charm". It is built using Three.js, React and WebGL. This article describes how these technologies work together using react-three-renderer (abbreviated as R3R).
Please check out the WebGL Getting Started Guide and the React and JSX Getting Started Guide on SitePoint for an introduction to React and WebGL. This article and the accompanying code use ES6 syntax.
The beginning of everything
A while ago, Pete Hunt made a joke in the #reactjs IRC channel, saying that he would use React to create a game:
I bet we can make a first-person shooter with React! The enemy has
A few years later, I did exactly this.
Chameleon Charm is a game that collects enhanced props that will allow you to narrow down to solve the infinite fractal maze. I've been working as a React developer for a few years and I'm curious to know if there is a way to use React to drive Three.js. At this time, R3R attracted my attention.
Why choose React?
I know what you are thinking: Why? Please let me explain. Here are some reasons to consider using React to drive 3D scenes:
- The "Declarative" view allows you to clearly separate scene rendering from game logic.
- Design easy-to-understand components such as
<player></player>
,<wall></wall>
,<level></level>
, and more. - "hot" (real-time) reload of game resources. Change textures and models and see their updates in the scene in real time!
- Check and debug 3D scenes as tags using native browser tools such as Chrome Inspector.
- Manage game resources in dependency graphs using Webpack, e.g.
<texture require="" src="%7B"></texture>
Let's set up a scenario to see how this all works.
React and WebGL
I created a sample GitHub repository to work with this article. Clone the repository and run the code as in the README and continue learning. It features SitePointy 3D robot as the main character!
Warning: R3R is still in the testing phase. Its API is unstable and may change in the future. Currently it only processes a subset of Three.js. I found it complete enough to build a complete game, but your results may vary.
Organization view code
The main benefit of using React to drive WebGL is that our view code is decoupled from the game logic. This means that the entities we render are small and easy to understand components.
R3R exposes a declarative API that encapsulates Three.js. For example, we can write:
<code><scene>></scene> <perspectivecamera> position={ new THREE.Vector3( 1, 1, 1 ) /> > </perspectivecamera></code>
Now we have an empty 3D scene with a camera. Adding a mesh to a scene is as simple as including a <mesh></mesh>
component and giving it <geometry></geometry>
is as easy as <material></material>
.
<code><scene>></scene> … <mesh>></mesh> <boxgeometry></boxgeometry> width={ 1 } height={ 1 } depth={ 1 } /> <meshbasicmaterial></meshbasicmaterial> color={ 0x00ff00 } /> > </code>
Behind the scenes, this will create a THREE.Scene and automatically add a grid with THREE.BoxGeometry. R3R handles the differences between old scenes and any changes. If you add a new mesh to the scene, the original mesh will not be recreated. Just like using normal React and DOM, 3D scenes only update the differences.
Because we work in React, we can detach the game entity into the component file. The Robot.js file in the sample repository demonstrates how to represent a major role using pure React view code. It is a "stateless function" component, which means it does not save any local state:
<code>const Robot = ({ position, rotation }) => <group></group> position={ position } rotation={ rotation } > <mesh> rotation={ localRotation }></mesh> <geometryresource></geometryresource> resourceId="robotGeometry" /> <materialresource></materialresource> resourceId="robotTexture" /> > >; </code>
Now we will include <robot></robot>
in our 3D scene!
<code><scene>></scene> … <mesh>></mesh>…> <robot></robot> position={…} rotation={…} /> > </code>
You can view more API examples on the R3R GitHub repository, or see the full sample settings in the included project.
Organizing game logic
The other half of the equation is to deal with game logic. Let's add some simple animations to our robot SitePointy.
How does traditional game loop work? They accept user input, analyze the old "world state", and return to the new world state for rendering. For convenience, let's store the "Game State" object in the component state. In a more mature project, you can move game state to Redux or Flux storage.
We will use the browser's requestAnimationFrame
API callback to drive our game loop and run the loop in GameContainer.js
. To animation the robot, let's calculate a new location based on the timestamp passed to requestAnimationFrame
and then store the new location in the state.
<code><scene>></scene> <perspectivecamera> position={ new THREE.Vector3( 1, 1, 1 ) /> > </perspectivecamera></code>
Calling setState()
will trigger the re-render of the child component and update the 3D scene. We pass state from container component to demo component: <game></game>
<code><scene>></scene> … <mesh>></mesh> <boxgeometry></boxgeometry> width={ 1 } height={ 1 } depth={ 1 } /> <meshbasicmaterial></meshbasicmaterial> color={ 0x00ff00 } /> > </code>We can apply a useful pattern to help organize this code. Updating the robot location is a simple time-based calculation. In the future, it may also consider previous robot locations from previous game states. A function that accepts some data, processes it, and returns new data is often called a reducer. We can abstract the moving code into a reducer function!
Now we can write a concise and clear game loop that contains only function calls:
<code>const Robot = ({ position, rotation }) => <group></group> position={ position } rotation={ rotation } > <mesh> rotation={ localRotation }></mesh> <geometryresource></geometryresource> resourceId="robotGeometry" /> <materialresource></materialresource> resourceId="robotTexture" /> > >; </code>To add more logic to the game loop, such as handling physics, create another reducer function and pass it to the result of the previous reducer:
<code><scene>></scene> … <mesh>></mesh>…> <robot></robot> position={…} rotation={…} /> > </code>As the growth of game engines, it becomes crucial to organize game logic into separate functions. Using reducer mode, this organization is very simple.
Resource Management
This is still an area of ??development for R3R. For textures, you can specify a url property on the JSX tag. Using Webpack, you can ask for the local image path:
<code>// … gameLoop( time ) { this.setState({ robotPosition: new THREE.Vector3( Math.sin( time * 0.01 ), 0, 0 ) }); } </code>With this setting, if you change the image on disk, your 3D scene will be updated in real time! This is invaluable for fast iterating game design and content.
For other resources (such as 3D models), you still have to use Three.js' built-in loader (such as JSONLoader) to handle them. I've tried using a custom webpack loader to load 3D model files, but ended up with too much work and no benefit. It is easier to think of models as binary data and use a file loader to load them. This can still implement real-time overloading of model data. You can see this in the sample code.
Debug
R3R supports React developer tool extensions for Chrome and Firefox. You can check your scene like you would check a normal DOM! Hovering over elements in the inspector displays their bounding boxes in the scene. You can also hover over the texture definition to see which objects in the scene use these textures.
Performance Precautions
When building Chameleon Charm, I encountered some performance issues that are unique to this workflow.
- I use Webpack for a hot reload time of up to 30 seconds! This is because each overload has to write large resources to the package. The solution is to implement Webpack's DLLPlugin, which reduces the reload time to less than 5 seconds.
- Ideally, your scene should be called only once per frame of rendering. After analyzing my game, React itself is the main bottleneck. Calling
setState()
multiple times per frame will result in double rendering and degrade performance.setState()
After exceeding a certain number of objects, R3R's performance will be worse than ordinary Three.js code. To me, it's about 1000 objects. You can compare R3R with Three.js in the "Benchmark" in the example.
That's it!
Check out Chameleon Charm to learn what you can do with this setting. While this toolchain is still young, I found that using R3R's React is crucial to clearly organize my WebGL game code. You can also check out the small but growing R3R sample page to see some well-organized code examples.
This article was peer-reviewed by Mark Brown and Kev Zettler. Thanks to all the peer reviewers at SitePoint for getting SitePoint content to its best!
FAQ for Building Games with ReactJS and WebGLWhat are the prerequisites for building games with ReactJS and WebGL?
To start building games with ReactJS and WebGL, you need a basic understanding of JavaScript, HTML, and CSS. Also need to know ReactJS (a popular JavaScript library for building user interfaces). In addition, it is crucial to understand WebGL (Web Graphics Library), a JavaScript API for rendering interactive 3D and 2D graphics. It will also be of great benefit to be familiar with ES6 syntax, npm (Node package manager) and command line.
How to integrate Unity with ReactJS?
Unity can be integrated with ReactJS using the react-unity-webgl package. This package allows you to embed Unity WebGL builds into ReactJS applications. You can install it using npm and import it into your project. You can then use the Unity component provided by the package to embed your Unity game into your ReactJS application.
What are the different ways to create 3D applications using React?
There are several ways to create a 3D application using React. One of the most popular methods is to use Three.js, a cross-browser JavaScript library for creating and displaying animated 3D computer graphics. Another approach is to use WebGL directly, but this may be more complicated. Other libraries such as react-three-fiber and react-unity-webgl can also be used to create 3D applications using React.
How to create interactive 3D graphics using WebGL?
WebGL allows you to create interactive 3D graphics directly in your browser, without plug-ins. You can use WebGL's API to create complex 3D graphics, animations, and games. However, WebGL's API is low-level and it can be complicated to use directly. Therefore, many developers prefer to use libraries like Three.js that provide a higher level of interface to WebGL.
What is the role of react-unity-webgl in game development?
react-unity-webgl package allows you to embed Unity WebGL builds into ReactJS applications. This means you can create complex 3D games with Unity and then easily integrate them into your ReactJS application. This is especially useful if you want to create a web-based game or interactive 3D application.
How to optimize my ReactJS and WebGL games for performance?
Optimizing games built with ReactJS and WebGL may involve multiple strategies. These strategies include minimizing the number of rerenders in React, using WebGL's built-in performance features such as requestAnimationFrame
to achieve smooth animations, and optimizing 3D models and textures for the web.
Can I build mobile games using ReactJS and WebGL?
Yes, you can use ReactJS and WebGL to build games running in a web browser on your mobile device. However, for native mobile games, you may want to consider using game development platforms like Unity or Unreal Engine, which can be exported directly to iOS and Android.
How to process user input in my ReactJS and WebGL games?
User input can be processed in ReactJS and WebGL games using standard JavaScript event handlers. You can listen to keyboard, mouse, and touch events and then update the game status accordingly. ReactJS also provides synthesis events that can be used to process user input across different browsers in a consistent manner.
Can I use other JavaScript libraries with ReactJS and WebGL?
Yes, you can use other JavaScript libraries with ReactJS and WebGL. For example, you might use Three.js for 3D graphics, Howler.js for audio, or Matter.js for physical processing. The key is to make sure these libraries work seamlessly in your game.
How to debug my ReactJS and WebGL games?
Games built using ReactJS and WebGL can be debugged using developer tools in a web browser. These tools allow you to check HTML, CSS, and JavaScript code, view console logs, and debug the code step by step. Additionally, React Developer Tools is a browser extension that allows you to check React component hierarchy, props, and state.
The above is the detailed content of Building a Game with Three.js, React and WebGL. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

There are three common ways to initiate HTTP requests in Node.js: use built-in modules, axios, and node-fetch. 1. Use the built-in http/https module without dependencies, which is suitable for basic scenarios, but requires manual processing of data stitching and error monitoring, such as using https.get() to obtain data or send POST requests through .write(); 2.axios is a third-party library based on Promise. It has concise syntax and powerful functions, supports async/await, automatic JSON conversion, interceptor, etc. It is recommended to simplify asynchronous request operations; 3.node-fetch provides a style similar to browser fetch, based on Promise and simple syntax

JavaScript data types are divided into primitive types and reference types. Primitive types include string, number, boolean, null, undefined, and symbol. The values are immutable and copies are copied when assigning values, so they do not affect each other; reference types such as objects, arrays and functions store memory addresses, and variables pointing to the same object will affect each other. Typeof and instanceof can be used to determine types, but pay attention to the historical issues of typeofnull. Understanding these two types of differences can help write more stable and reliable code.

Hello, JavaScript developers! Welcome to this week's JavaScript news! This week we will focus on: Oracle's trademark dispute with Deno, new JavaScript time objects are supported by browsers, Google Chrome updates, and some powerful developer tools. Let's get started! Oracle's trademark dispute with Deno Oracle's attempt to register a "JavaScript" trademark has caused controversy. Ryan Dahl, the creator of Node.js and Deno, has filed a petition to cancel the trademark, and he believes that JavaScript is an open standard and should not be used by Oracle

CacheAPI is a tool provided by the browser to cache network requests, which is often used in conjunction with ServiceWorker to improve website performance and offline experience. 1. It allows developers to manually store resources such as scripts, style sheets, pictures, etc.; 2. It can match cache responses according to requests; 3. It supports deleting specific caches or clearing the entire cache; 4. It can implement cache priority or network priority strategies through ServiceWorker listening to fetch events; 5. It is often used for offline support, speed up repeated access speed, preloading key resources and background update content; 6. When using it, you need to pay attention to cache version control, storage restrictions and the difference from HTTP caching mechanism.

Promise is the core mechanism for handling asynchronous operations in JavaScript. Understanding chain calls, error handling and combiners is the key to mastering their applications. 1. The chain call returns a new Promise through .then() to realize asynchronous process concatenation. Each .then() receives the previous result and can return a value or a Promise; 2. Error handling should use .catch() to catch exceptions to avoid silent failures, and can return the default value in catch to continue the process; 3. Combinators such as Promise.all() (successfully successful only after all success), Promise.race() (the first completion is returned) and Promise.allSettled() (waiting for all completions)

JavaScript array built-in methods such as .map(), .filter() and .reduce() can simplify data processing; 1) .map() is used to convert elements one to one to generate new arrays; 2) .filter() is used to filter elements by condition; 3) .reduce() is used to aggregate data as a single value; misuse should be avoided when used, resulting in side effects or performance problems.

JavaScript's event loop manages asynchronous operations by coordinating call stacks, WebAPIs, and task queues. 1. The call stack executes synchronous code, and when encountering asynchronous tasks, it is handed over to WebAPI for processing; 2. After the WebAPI completes the task in the background, it puts the callback into the corresponding queue (macro task or micro task); 3. The event loop checks whether the call stack is empty. If it is empty, the callback is taken out from the queue and pushed into the call stack for execution; 4. Micro tasks (such as Promise.then) take precedence over macro tasks (such as setTimeout); 5. Understanding the event loop helps to avoid blocking the main thread and optimize the code execution order.

Event bubbles propagate from the target element outward to the ancestor node, while event capture propagates from the outer layer inward to the target element. 1. Event bubbles: After clicking the child element, the event triggers the listener of the parent element upwards in turn. For example, after clicking the button, it outputs Childclicked first, and then Parentclicked. 2. Event capture: Set the third parameter to true, so that the listener is executed in the capture stage, such as triggering the capture listener of the parent element before clicking the button. 3. Practical uses include unified management of child element events, interception preprocessing and performance optimization. 4. The DOM event stream is divided into three stages: capture, target and bubble, and the default listener is executed in the bubble stage.
