核心要点
我正在制作一款名为“变色龙魅力”的游戏。它使用Three.js、React和WebGL构建。本文介绍了这些技术如何使用react-three-renderer(缩写为R3R)协同工作。
请查看SitePoint上的《WebGL入门指南》和《React和JSX入门指南》,了解React和WebGL的介绍。本文和随附的代码使用ES6语法。
一切的开始
一段时间前,Pete Hunt在#reactjs IRC频道中开了一个玩笑,说要用React来制作游戏:
我敢打赌我们可以用React制作一款第一人称射击游戏!敌人有
几年后,我做的正是这件事。
《变色龙魅力》是一款收集增强道具的游戏,这些道具会让你缩小以解决无限分形迷宫。我已经做了几年的React开发者,我很想知道是否有办法使用React驱动Three.js。这时,R3R吸引了我的注意。
为什么选择React?
我知道你在想什么:为什么?请容我解释一下。以下是一些考虑使用React驱动3D场景的原因:
<player></player>
、<wall></wall>
、<level></level>
等等。<texture require="" src="%7B"></texture>
让我们设置一个场景来了解这一切是如何工作的。
React和WebGL
我创建了一个示例GitHub存储库来配合本文。克隆存储库并按照README中的说明运行代码并继续学习。它以SitePointy 3D机器人为主角!
警告:R3R仍在测试阶段。其API不稳定,将来可能会发生变化。目前它只处理Three.js的一个子集。我发现它足够完整,可以构建一个完整的游戏,但你的结果可能会有所不同。
使用React驱动WebGL的主要好处是我们的视图代码与游戏逻辑解耦。这意味着我们渲染的实体是小而易于理解的组件。
R3R公开了一个封装Three.js的声明式API。例如,我们可以编写:
<code><scene>></scene> <perspectivecamera> position={ new THREE.Vector3( 1, 1, 1 ) /> > </perspectivecamera></code>
现在我们有一个带有摄像机的空3D场景。向场景添加网格就像包含<mesh></mesh>
组件并赋予它<geometry></geometry>
和<material></material>
一样简单。
<code><scene>></scene> … <mesh>></mesh> <boxgeometry></boxgeometry> width={ 1 } height={ 1 } depth={ 1 } /> <meshbasicmaterial></meshbasicmaterial> color={ 0x00ff00 } /> > </code>
在幕后,这将创建一个THREE.Scene并自动添加一个带有THREE.BoxGeometry的网格。R3R处理旧场景与任何更改的差异。如果你向场景添加一个新的网格,则不会重新创建原始网格。就像使用普通的React和DOM一样,3D场景只更新差异。
因为我们在React中工作,所以我们可以将游戏实体分离到组件文件中。示例存储库中的Robot.js文件演示了如何使用纯React视图代码表示主要角色。它是一个“无状态函数”组件,这意味着它不保存任何本地状态:
<code>const Robot = ({ position, rotation }) => <group></group> position={ position } rotation={ rotation } > <mesh> rotation={ localRotation }></mesh> <geometryresource></geometryresource> resourceId="robotGeometry" /> <materialresource></materialresource> resourceId="robotTexture" /> > >; </code>
现在我们将<robot></robot>
包含在我们的3D场景中!
<code><scene>></scene> … <mesh>></mesh>…> <robot></robot> position={…} rotation={…} /> > </code>
你可以在R3R GitHub存储库上查看更多API示例,或者在随附的项目中查看完整的示例设置。
等式的另一半是处理游戏逻辑。让我们给我们的机器人SitePointy添加一些简单的动画。
传统的游戏循环是如何工作的?它们接受用户输入,分析旧的“世界状态”,并返回新的世界状态以进行渲染。为方便起见,让我们将“游戏状态”对象存储在组件状态中。在一个更成熟的项目中,你可以将游戏状态移动到Redux或Flux存储中。
我们将使用浏览器的requestAnimationFrame
API回调来驱动我们的游戏循环,并在GameContainer.js
中运行循环。为了动画化机器人,让我们根据传递给requestAnimationFrame
的时间戳计算一个新的位置,然后将新的位置存储在状态中。
<code><scene>></scene> <perspectivecamera> position={ new THREE.Vector3( 1, 1, 1 ) /> > </perspectivecamera></code>
调用setState()
将触发子组件的重新渲染,并更新3D场景。我们将状态从容器组件传递到演示性<game></game>
组件:
<code><scene>></scene> … <mesh>></mesh> <boxgeometry></boxgeometry> width={ 1 } height={ 1 } depth={ 1 } /> <meshbasicmaterial></meshbasicmaterial> color={ 0x00ff00 } /> > </code>
我们可以应用一个有用的模式来帮助组织这段代码。更新机器人位置是一个简单的基于时间的计算。将来,它还可能考虑来自先前游戏状态的先前机器人位置。一个接受一些数据、处理它并返回新数据的函数通常被称为reducer。我们可以将移动代码抽象成一个reducer函数!
现在我们可以编写一个简洁明了的游戏循环,其中只包含函数调用:
<code>const Robot = ({ position, rotation }) => <group></group> position={ position } rotation={ rotation } > <mesh> rotation={ localRotation }></mesh> <geometryresource></geometryresource> resourceId="robotGeometry" /> <materialresource></materialresource> resourceId="robotTexture" /> > >; </code>
要向游戏循环添加更多逻辑,例如处理物理,请创建另一个reducer函数并将其传递给先前reducer的结果:
<code><scene>></scene> … <mesh>></mesh>…> <robot></robot> position={…} rotation={…} /> > </code>
随着游戏引擎的增长,将游戏逻辑组织成单独的函数变得至关重要。使用reducer模式,这种组织非常简单。
这仍然是R3R的一个发展领域。对于纹理,你可以在JSX标签上指定一个url属性。使用Webpack,你可以要求本地图像路径:
<code>// … gameLoop( time ) { this.setState({ robotPosition: new THREE.Vector3( Math.sin( time * 0.01 ), 0, 0 ) }); } </code>
有了这个设置,如果你更改磁盘上的图像,你的3D场景将实时更新!这对于快速迭代游戏设计和内容非常宝贵。
对于其他资源(如3D模型),你仍然必须使用Three.js的内置加载器(如JSONLoader)来处理它们。我尝试过使用自定义Webpack加载器来加载3D模型文件,但最终工作量太大,没有好处。将模型视为二进制数据并使用文件加载器加载它们更容易。这仍然可以实现模型数据的实时重载。你可以在示例代码中看到这一点。
调试
R3R支持Chrome和Firefox的React开发者工具扩展。你可以像检查普通DOM一样检查你的场景!将鼠标悬停在检查器中的元素上会在场景中显示它们的边界框。你还可以将鼠标悬停在纹理定义上以查看场景中哪些对象使用这些纹理。
你还可以加入react-three-renderer Gitter聊天室,以获得有关调试应用程序的帮助。
性能注意事项
在构建《变色龙魅力》时,我遇到了一些此工作流程特有的性能问题。
setState()
。在分析我的游戏后,React本身是主要的瓶颈。每帧调用setState()
多次会导致双重渲染并降低性能。Chrome DevTools的时间轴功能是调试性能的绝佳工具。你可以轻松地直观地检查游戏循环,而且它比DevTools的“配置文件”功能更易于阅读。
就是这样!
查看《变色龙魅力》以了解使用此设置可以实现的功能。虽然此工具链还很年轻,但我发现使用R3R的React对于清晰地组织我的WebGL游戏代码至关重要。你还可以查看小型但不断增长的R3R示例页面,以查看一些组织良好的代码示例。
本文由Mark Brown和Kev Zettler进行同行评审。感谢所有SitePoint的同行评审人员,使SitePoint的内容达到最佳状态!
使用ReactJS和WebGL构建游戏的常见问题解答(FAQ)
要开始使用ReactJS和WebGL构建游戏,你需要对JavaScript、HTML和CSS有基本的了解。还需要了解ReactJS(一个流行的用于构建用户界面的JavaScript库)。此外,了解WebGL(Web图形库)(一个用于渲染交互式3D和2D图形的JavaScript API)至关重要。熟悉ES6语法、npm(Node包管理器)和命令行也将大有裨益。
可以使用react-unity-webgl包将Unity与ReactJS集成。此包允许你将Unity WebGL构建嵌入到ReactJS应用程序中。你可以使用npm安装它并将其导入到你的项目中。然后,你可以使用包提供的Unity组件将你的Unity游戏嵌入到你的ReactJS应用程序中。
有几种方法可以使用React创建3D应用程序。最流行的方法之一是使用Three.js,这是一个用于创建和显示动画3D计算机图形的跨浏览器JavaScript库。另一种方法是直接使用WebGL,但这可能更复杂。其他库(如react-three-fiber和react-unity-webgl)也可以用于使用React创建3D应用程序。
WebGL允许你直接在浏览器中创建交互式3D图形,无需插件。你可以使用WebGL的API创建复杂的3D图形、动画和游戏。但是,WebGL的API是低级的,直接使用可能很复杂。因此,许多开发人员更喜欢使用像Three.js这样的库,这些库为WebGL提供了更高级别的接口。
react-unity-webgl包允许你将Unity WebGL构建嵌入到ReactJS应用程序中。这意味着你可以使用Unity创建复杂的3D游戏,然后轻松地将它们集成到你的ReactJS应用程序中。如果你想创建一个基于Web的游戏或交互式3D应用程序,这将特别有用。
优化使用ReactJS和WebGL构建的游戏可能涉及多种策略。这些策略包括最小化React中的重新渲染次数,使用WebGL的内置性能功能(如requestAnimationFrame
)实现流畅的动画,以及为Web优化3D模型和纹理。
是的,你可以使用ReactJS和WebGL构建在移动设备上的Web浏览器中运行的游戏。但是,对于原生移动游戏,你可能需要考虑使用Unity或Unreal Engine等游戏开发平台,这些平台可以直接导出到iOS和Android。
可以使用标准JavaScript事件处理程序在ReactJS和WebGL游戏中处理用户输入。你可以监听键盘、鼠标和触摸事件,然后相应地更新游戏状态。ReactJS还提供合成事件,可以用来以一致的方式跨不同浏览器处理用户输入。
是的,你可以将其他JavaScript库与ReactJS和WebGL一起使用。例如,你可能会使用Three.js进行3D图形处理,使用Howler.js进行音频处理,或使用Matter.js进行物理处理。关键是确保这些库可以在你的游戏中无缝协同工作。
可以使用Web浏览器中的开发者工具调试使用ReactJS和WebGL构建的游戏。这些工具允许你检查HTML、CSS和JavaScript代码,查看控制台日志,并逐步调试代码。此外,React开发者工具是一个浏览器扩展,允许你检查React组件层次结构、道具和状态。
以上是用三分,react和webGL构建游戏的详细内容。更多信息请关注PHP中文网其他相关文章!