欢迎光临散文网 会员登陆 & 注册

Three.js 进阶之旅:物理效果-3D乒乓球小游戏

2023-05-16 19:55 作者:unlexx  | 我要投稿

声明:本文涉及图文和模型素材仅用于个人学习、研究和欣赏,请勿二次修改、非法传播、转载、出版、商用、及进行其他获利行为。


摘要

本文在专栏上一篇内容《Three.js 进阶之旅:物理效果-碰撞和声音》的基础上,将使用新的技术栈 React Three Fiber 和 Cannon.js 来实现一个具有物理特性的小游戏,通过本文的阅读,你将学习到的知识点包括:了解什么是 React Three Fiber 及它的相关生态、使用 React Three Fiber 搭建基础三维场景、如何使用新技术栈给场景中对象的添加物理特性等,最后利用上述知识点,将开发一个简单的乒乓球小游戏。

效果

在正式学习之前,我们先来看看本文示例最终实现效果:页面主体内容是一个手握乒乓球拍的模型和一个乒乓球 🏓,对球拍像现实生活中一样进行颠球施力操作,乒乓球可以在球拍上弹起,乒乓球弹起的高度随着施加在球拍上的力的大小的变化而变化,球拍中央显示的是连续颠球次数 5️⃣,当乒乓球从球拍掉落时一局游戏结束,球拍上的数字归零 0️⃣ 。快来试试你一次可以颠多少个球吧 😏

打开以下链接,在线预览效果,大屏访问效果更佳。

  • 👁‍🗨 在线预览地址:https://dragonir.github.io/physics-pingpong/

本专栏系列代码托管在 Github 仓库【threejs-odessey】,后续所有目录也都将在此仓库中更新

🔗 代码仓库地址:git@github.com:dragonir/threejs-odessey.git

原理

React-Three-Fiber

React Three Fiber 是一个基于 Three.js 的 React 渲染器,简称 R3F。它像是一个配置器,把 Three.js 的对象映射为 R3F 中的组件。以下是一些相关链接:

  • 仓库: https://github.com/pmndrs/react-three-fiber

  • 官网: https://docs.pmnd.rs/react-three-fiber/getting-started/introduction

  • 示例: https://docs.pmnd.rs/react-three-fiber/getting-started/examples

特点

  • 使用可重用的组件以声明方式构建动态场景图,使 Three.js 的处理变得更加轻松,并使代码库更加整洁。这些组件对状态变化做出反应,具有开箱即用的交互性。

  • Three.js 中所有内容都能在这里运行。它不针对特定的 Three.js 版本,也不需要更新以修改,添加或删除上游功能。

  • 渲染性能与 Three.js 和 GPU 相仿。组件参与 React 之外的 render loop 时,没有任何额外开销。

写 React Three Fiber 比较繁琐,我们可以写成 R3F 或简称为 Fiber。让我们从现在开始使用 R3F 吧。

生态系统

R3F 有充满活力的生态系统,包括各种库、辅助工具以及抽象方法:

  • @react-three/drei – 有用的辅助工具,自身就有丰富的生态

  • @react-three/gltfjsx – 将 GLTFs 转换为 JSX 组件

  • @react-three/postprocessing – 后期处理效果

  • @react-three/test-renderer – 用于在 Node 中进行单元测试

  • @react-three/flex – react-three-fiber 的 flex 盒子布局

  • @react-three/xr – VR/AR 控制器和事件

  • @react-three/csg – 构造实体几何

  • @react-three/rapier – 使用 Rapier 的 3D 物理引擎

  • @react-three/cannon – 使用 Cannon 的 3D 物理引擎

  • @react-three/p2 – 使用 P2 的 2D 物理引擎

  • @react-three/a11y – 可访问工具

  • @react-three/gpu-pathtracer – 真实的路径追踪

  • create-r3f-app next – nextjs 启动器

  • lamina – 基于 shader materials 的图层

  • zustand – 基于 flux 的状态管理

  • jotai – 基于 atoms 的状态管理

  • valtio – 基于 proxy 的状态管理

  • react-spring – 一个 spring-physics-based 的动画库

  • framer-motion-3d – framer motion,一个很受欢迎的动画库

  • use-gesture – 鼠标/触摸手势

  • leva – 创建 GUI 控制器

  • maath – 数学辅助工具

  • miniplex – ECS 实体管理系统

  • composer-suite – 合成着色器、粒子、特效和游戏机制、

安装

第一个场景

在一个新建的 React 项目中,我们通过以下的步骤使用 R3F 来创建第一个场景。

初始化Canvas

首先,我们从 @react-three/fiber 引入 Canvas 元素,将其放到 React 树中:

Canvas 组件在幕后做了一些重要的初始化工作:

  • 它初始化了一个场景 Scene 和一个相机 Camera,它们都是渲染所需的基本模块。

  • 它在页面每一帧更新中都渲染场景,我们不需要再到页面重绘方法中循环调用渲染方法。

🚩 Canvas 大小响应式自适应于父节点,我们可以通过改变父节点的宽度和高度来控制渲染场景的尺寸大小。

添加一个Mesh组件

为了真正能够在场景中看到一些物体,现在我们添加一个小写的 <mesh /> 元素,它直接等效于 new THREE.Mesh()

🚩 可以看到我们没有特地去额外引入mesh组件,我们不需要引入任何元素,所有Three.js中的对象都将被当作原生的JSX元素,就像在 ReactDom 中写 <div /> 及 <span /> 元素一样。R3F Fiber组件的通用规则是将Three.js中的它们的名字写成驼峰式的DOM元素即可。

一个 Mesh 是 Three.js 中的基础场景对象,需要给它提供一个几何对象 geometry 以及一个材质 material 来代表一个三维空间的几何形状,我们将使用一个 BoxGeometry 和 MeshStandardMaterial 来创建一个新的网格 Mesh,它们会自动关联到它们的父节点。

上述代码和以下 Three.js 代码是等价的:

构造函数参数

根据 BoxGeometry 的文档,我们可以选择给它传递三个参数:widthlength 及 depth

为了实现相同的功能,我们可以在 R3F 中使用 args 属性,它总是接受一个数组,其项目表示构造函数参数:

添加光源

接着,我们通过像下面这样添加光源组件来为我们的场景添加一些光线。

属性

这里介绍关于 R3F 的最后一个概念,即 React 属性是如何在 Three.js 对象中工作的。当你给一个 Fiber 组件设置任意属性时,它将对 Three.js 设置一个相同名字的属性。我们关注到 ambientLight 上,由它的文档可知,我们可以选择 color 和 intensity 属性来初始化它:

等价于

快捷方法

在 Three.js 中对于很多属性的设置如 colorsvectors 等都可以使用 set() 方法进行快捷设置:

在 JSX 中也是相同的:

结果

查看React Three Fiber完整API文档

实现

到这里,我们已经掌握了 R3F 的基本知识,我们再结合专栏上篇关于物理特性的内容,来实现如文章开头介绍的乒乓球 🏓 小游戏。

🚩 本文乒乓球小游戏基础版及乒乓球三维模型资源来源于R3F官网示例。

〇 搭建页面基本结构

首先,我们创建一个 Experience 文件作为渲染三维场景的组件,并在其中添加 Canvas 组件搭建基本页面结构。

① 场景初始化

接着我们开启 Canvas 的阴影并设置相机参数,然后添加环境光 ambientLight 和点光源 pointLight 两种光源:

如果需要修改 Canvas 的背景色,可以在其中添加一个 color 标签并设置参数 attach 为 background,在 args 参数中设置颜色即可。

② 添加辅助工具

接着,我们在页面顶部引入 Perf,它是 R3F 生态中查看页面性能的组件,它的功能和 Three.js 中 stats.js 是类似的,像下面这样添加到代码中设置它的显示位置,页面对应区域就会出现可视化的查看工具,在上面可以查看 GPUCPUFPS 等性能参数。

如果想使用网格作为辅助线或用作装饰,可以使用 gridHelper 组件,它支持配置 positionrotationargs 等参数。

③ 创建乒乓球和球拍

我们创建一个名为 PingPong.jsx 的乒乓球组件文件,然后在文件顶部引入以下依赖,其中 PhysicsuseBoxusePlaneuseSphere 用于创建物理世界;useFrame 是用来进行页面动画更新的 hook,它将在页面每帧重绘时执行,我们可以在它里面执行一些动画函数和更新控制器,相当于 Three.js 中用原生实现的 requestAnimationFrameuseLoader 用于加载器的管理,使用它更方便进行加载错误管理和回调方法执行;lerp 是一个插值运算函数,它可以计算某一数值到另一数值的百分比,从而得出一个新的数值,常用于移动物体、修改透明度、颜色、大小、模拟动画等。

创建物理世界

然后创建一个 PingPong 类,在其中添加 <Physics> 组件来创建物理世界,像直接使用 Cannon.js 一样,可以给它设置 iterationstolerancegravityallowSleep 等参数来分别设置物理世界的迭代次数、容错性、引力以及是否支持进入休眠状态等,然后在其中添加一个平面几何体和一个平面刚体 ContactGround

创建乒乓球

接着,我们创建一个球体类 Ball,在其中添加球体 🟡 ,可以使用前面介绍的 useLoader 来管理它的贴图加载,为了方便观察到乒乓球的转动情况,贴图中央加了一个十字交叉图案 。然后将其放在 <Physics> 标签下。

创建球拍

球拍 🏓 采用的是一个 glb 格式的模型,在 Blender 中我们可以看到模型的样式和详细的骨骼结构,对于模型的加载,我们同样使用 useLoader 来管理,此时的加载器需要使用 GLTFLoader

我们创建一个 Paddle 类并将其添加到 <Physics> 标签中,在这个类中我们实现模型加载,模型加载完成后绑定骨骼,并在 useFrame 页面重绘方法中,根据鼠标所在位置更新乒乓球拍模型的位置 position,并根据是否一开始游戏状态以及鼠标的位置来更新球拍的 x轴 和 y轴 方向的 rotation 值。

到这里,我们已经实现乒乓球颠球的基本功能了 🤩

颠球计数

为了显示每次游戏可以颠球的次数,现在我们在乒乓球拍中央加上数字显示 5️⃣ 。我们可以像下面这样创建一个 Text 类,在文件顶部引入 TextGeometryFontLoaderfontJson 作为字体几何体、字体加载器以及字体文件,添加一个 geom 作为创建字体几何体的方法,当 count 状态值发生变化时,实时更新创建字体几何体模型。

然后将 Text 字体类放入球拍几何体中,其中 count 字段需要在物理世界中刚体发生碰撞时进行更新,该方法加载下节内容添加碰撞音效时一起实现。

④ 页面装饰

到这里,整个小游戏的全部流程都开发完毕了,现在我们来加一些页面提示语、颠球时的碰撞音效,页面的光照效果等,使 3D 场景看起来更加真实。

音效

实现音效 🔈 前,我们先像下面这样添加一个状态管理器 📦 ,来进行页面全局状态的管理。zustand 是一个轻量级的状态管理库;_.clamp(number, [lower], upper) 用于返回限制在 lower 和 upper 之间的值;pingSound 是需要播放的音频文件。我们在其中添加一个 pong 方法用来更新音效颠球计数,添加一个 reset 方法重置颠球数字。count 字段表示每次的颠球次数,welcome 表示是否在欢迎界面。

然后我们可以在上述 Paddle 乒乓球拍类中像这样在物体发生碰撞时触发 pong 方法:

光照

为了是场景更加真实,我们可以开启 Canvas 的阴影,然后添加多种光源 💡 来优化场景,如 spotLight 就能起到视觉聚焦的作用。

提示语

为了提升小游戏的用户体验,我们可以添加一些页面文字提示来指引使用者和提升页面视觉效果,需要注意的是,这些额外的元素不能添加到 <Canvas /> 标签内哦 😄

🔗 源码地址: https://github.com/dragonir/threejs-odessey

总结

本文中主要包含的知识点包括:

  • 了解什么是 React Three Fiber 及相关生态。

  • React Three Fiber 基础入门。

  • 使用 React Three Fiber 开发一个乒乓球小游戏,学会如何场景构建、模型加载、物理世界关联、全局状态管理等。

想了解其他前端知识或其他未在本文中详细描述的Web 3D开发技术相关知识,可阅读我往期的文章。如果有疑问可以在评论中留言,如果觉得文章对你有帮助,不要忘了一键三连哦 👍

附录

  • [1]. 🌴 Three.js 打造缤纷夏日3D梦中情岛

  • [2]. 🔥 Three.js 实现炫酷的赛博朋克风格3D数字地球大屏

  • [3]. 🐼 Three.js 实现2022冬奥主题3D趣味页面,含冰墩墩

  • [4]. 🦊 Three.js 实现3D开放世界小游戏:阿狸的多元宇宙

  • [5]. 🏆 掘金1000粉!使用Three.js实现一个创意纪念页面

  • 【Three.js 进阶之旅】系列专栏访问 👈

  • 更多往期【3D】专栏访问 👈

  • 更多往期【前端】专栏访问 👈

参考

  • [1]. React Three Fiber

  • [2]. threejs.org


Three.js 进阶之旅:物理效果-3D乒乓球小游戏的评论 (共 条)

分享到微博请遵守国家法律