paint-brush
运用 React 和 Three.js 堆栈新创建您自行的 3D 投弹玩游戏 — 第 1 方面 进行@varlab
868 讀數
868 讀數

使用 React 和 Three.js 堆栈创建您自己的 3D 射击游戏 — 第 1 部分

根据 Ivan Zhukov17m2023/10/21
Read on Terminal Reader

太長; 讀書

在网络技术和交互式应用程序积极发展的时代,3D 图形变得越来越重要和需求。但如何在不失去Web开发优势的情况下创建3D应用程序呢?在本文中,我们将了解如何将 Three.js 的强大功能与 React 的灵活性相结合,在浏览器中创建您自己的游戏。 本文将向您介绍 React Three Fiber 库,并教您如何创建交互式 3D 游戏。
featured image - 使用 React 和 Three.js 堆栈创建您自己的 3D 射击游戏 — 第 1 部分
Ivan Zhukov HackerNoon profile picture
0-item
1-item

在现代 Web 开发中,经典应用程序和 Web 应用程序之间的界限每天都在变得模糊。今天,我们不仅可以创建交互式网站,还可以在浏览器中创建成熟的游戏。让这成为可能的工具之一是库 - 一个使用React技术基于创建 3D 图形的强大工具。

关于 React 三纤维堆栈

React Three FiberThree.js 的包装器,它使用React的结构和原理在 Web 上创建 3D 图形。该堆栈允许开发人员将Three.js的强大功能与React的便利性和灵活性结合起来,使创建应用程序的过程更加直观和有组织。


React Three Fiber的核心理念是,您在场景中创建的所有内容都是React组件。这允许开发人员应用熟悉的模式和方法。

React Three Fiber的主要优点之一是它易于与React生态系统集成。使用此库时仍然可以轻松集成任何其他React工具。

Web-GameDev 的相关性

Web-GameDev近年来发生了重大变化,从简单的 2D 游戏发展到可与桌面应用程序相媲美的复杂 3D 项目。受欢迎程度和功能的增长使得 Web-GameDev 成为一个不容忽视的领域。


页面 传奇的主耍胜机最为是其可访问权限性。用户不同进行下载和装配所以各种系统软件 - 只需单击搜索器中的连接就能。这简单化了传奇的发布和推广营销,使鸟卵可供世界里各县市区的谋福利受众群体运用。


最后一个,网页内容手机网游研发就可成为了研发师我的第一次用了解自己的能力开始手机网游研发的好方案。充分运用可的工具软件和库,纵使找不到 3D 几何图形游戏经验,也就可有个愉快且高品产品品质的内容!

现代浏览器中的游戏性能

现代浏览器已经走过了漫长的道路,从相当简单的网络浏览工具发展到用于运行复杂应用程序和游戏的强大平台。 ChromeFirefoxEdge主流浏览器都在不断优化和开发,以确保高性能,使其成为开发复杂应用程序的理想平台。


是推动基于浏览器的游戏发展的关键工具之一。该标准允许开发人员使用硬件图形加速,从而显着提高了 3D 游戏的性能。与其他 webAPI 一起, WebGL为直接在浏览器中创建令人印象深刻的 Web 应用程序开辟了新的可能性。


然后,在为浏览访问器发掘小游戏时,要考虑几种稳定性多方面至关关健:自然资源优化系统、内存条工作管理和真对与众不同产品的匹配都会决定大型项目顺利完成的关健点。

各就各位!

但是,任何文字和学说一愿因,但活动体验这是另外愿因。要现实看法和体会公司页面涉及网游定制制作的全都能力,较好的的办法还是徜徉在定制制作历程中。往往,作公司页面涉及网游定制制作获得成功的事例,咱们将创造自已的网游。这点历程将使咱们自学定制制作的主要上,面临现实问题并找出处理解决方法,并看得见公司页面涉及网游定制制作平台网站需要这样专业和利索。


在一款型散文中,我们大家将介绍怎样安全使用该库的功能模块创立了首先称为射击类网游,并深入细致摸索让人快感的电脑网站网游的开发天下!



上的存储空间库


当前,给各位起吧!

设置项目并安装包

首先,我们需要一个React项目模板。那么让我们从安装开始吧。


 npm create vite@latest


  • 选择React库;
  • 选择JavaScript


的安装额外的的 npm 包。


 npm install three @react-three/fiber @react-three/drei @react three/rapier zustand @tweenjs/tween.js


随后从自己的投资项目中有不要要的介绍。


自定义画布显示

main.jsx文件中,添加将作为范围显示在页面上的 div 元素。插入Canvas组件并设置相机的视野。在Canvas组件内放置App组件。


main.jsx


让我们向index.css添加样式,以将UI 元素拉伸到屏幕的整个高度,并将范围显示为屏幕中心的圆圈。


索引.css


App组件中我们添加了一个Sky组件,它将以天空的形式显示在我们的游戏场景中作为背景。


应用程序.jsx


在场景中显示天空


地板表面

让我们创建一个Ground组件并将其放置在App组件中。


应用程序.jsx


Ground中,创建一个平坦的表面元素。在 Y 轴上向下移动,使该平面位于相机的视野内。并在 X 轴上翻转平面,使其水平。


地面.jsx


或许我们公司同一橘黄色充当原料的颜色,单面仍呈现为纯黑色。


现场平坦


基本照明

默认情况下,场景中没有照明,因此让我们添加一个光源ambientLight ,它从各个方向照亮对象,并且没有定向光束。作为参数设置发光强度。


应用程序.jsx


照明平面


地板表面的纹理

因为使地面外层让人觉得了不均,我门将获取肌理。以延着外层连续的单园格的方法做地面外层的的图案。

资源文件夹中添加带有纹理的 PNG 图像。


添加纹理


要在场景中加载纹理,让我们使用@react- Three/drei包中的useTexture钩子。作为钩子的参数,我们将传递导入到文件中的纹理图像。设置图像在水平轴上的重复次数。


地面.jsx


平面上的纹理


相机移动

使用@react-two/drei包中的PointerLockControls组件,将光标固定在屏幕上,这样当你移动鼠标时它不会移动,但会改变相机在场景中的位置。


应用程序.jsx


相机运动演示


让我们对Ground组件进行一些小的编辑。


地面.jsx


添加物理

方便看不清楚无误,使人们向场地获取一种十分简单的立方米体。


 <mesh position={[0, 3, -5]}> <boxGeometry /> </mesh> 


场景中的立方体


当今他只能悬在空间中。


使用@react-two/rapier包中的Physics组件将“物理”添加到场景中。作为参数,配置重力场,我们在其中设置沿轴的重力。


 <Physics gravity={[0, -20, 0]}> <Ground /> <mesh position={[0, 3, -5]}> <boxGeometry /> </mesh> </Physics>


然而,我们的立方体位于物理组件内部,但它什么也没发生。为了使立方体表现得像一个真实的物理对象,我们需要将其包装在@react- Three/rapier包中的RigidBody组件中。


应用程序.jsx


,,我们公司会可以看看每一次网页页面二次调用时,万立方体也会在摩擦力的的影响下落下来。


立方体坠落


但现如今还会有别的个神器任务 - 有用不着使地毯称为立米体可与之互动的物件,因此超出它就没掉下。


地板作为一个物理对象

让我们回到Ground组件并添加一个RigidBody组件作为地板表面的包装。


地面.jsx


现阶段,当脱落时,立米体像真人的生物学原料似得落到复合木地板上。


立方体在平面上掉落


让角色服从物理定律

让我们创建一个Player组件来控制场景中的角色。


该角色与添加的立方体是同一物理对象,因此它必须与地板表面以及场景中的立方体进行交互。这就是我们添加RigidBody组件的原因。让我们将角色制作成胶囊的形式。


播放器.jsx


Player组件放置在Physics 组件内。


应用程序.jsx


现时他们的游戏角色就已发生时现象了。


胶囊形态的角色


移动角色 - 创建钩子

角色将使用WASD键进行控制,并使用空格键进行跳跃。

经过公司的各自的react-hook,公司的达到了走动的角色的逻缉。


让我们创建一个hooks.js文件并在其中添加一个新的usePersonControls函数。


令各位以 {"keycode": "action to be Perform"} 的格试的定义的关键字。现在来,插入应用在按压和产生数字键盘按建的惨案净化治理源子程序。当净化治理源子程序被重置时,各位将确定好特定无法下达的操作流程并更行其话动方式。看作终究报告单,该钩子将回的格试为 {"action in Progress": "status"} 的关键字。


钩子.js


移动角色 - 实现钩子

实现usePersonControls钩子后,应该在控制角色时使用它。在Player组件中,我们将添加运动状态跟踪并更新角色运动方向的向量。


咱们还将概念数据存储转动领域方式的变量类型。


播放器.jsx


要更新角色的位置,让我们使用@react- Three/Fiber包提供的Frame 。该钩子的工作原理与requestAnimationFrame类似,每秒执行函数主体约 60 次。


播放器.jsx


代码说明:

1. const playerRef = useRef();为玩家对象创建链接。此链接将允许与场景中的玩家对象直接交互。

2. const { 向前、向后、向左、向右、跳跃 } = usePersonControls();当使用钩子时,会返回一个具有布尔值的对象,该布尔值指示玩家当前按下了哪些控制按钮。

3. useFrame((状态) => { ... });在动画的每一帧上都会调用该钩子。在此钩子内,玩家的位置和线速度会更新。

4. if (!playerRef.current) 返回;检查玩家对象是否存在。如果没有玩家对象,该函数将停止执行以避免错误。

5. const速度=playerRef.current.linvel();获取玩家当前的线速度。

6. frontVector.set(0, 0, 向后-向前);根据按下的按钮设置向前/向后运动矢量。

7. sideVector.set(左-右, 0, 0);设置左/右移动矢量。

8. Direction.subVectors(frontVector, sideVector).normalize().multiplyScalar(MOVE_SPEED);通过减去运动向量、对结果进行归一化(使向量长度为 1)并乘以运动速度常数来计算玩家运动的最终向量。

9.playerRef.current.wakeUp(); “唤醒”玩家对象以确保它对更改做出反应。如果不使用此方法,一段时间后对象将“休眠”并且不会对位置变化做出反应。

10.playerRef.current.setLinvel({ x: 方向.x, y: 速度.y, z: 方向.z });根据计算出的运动方向设置玩家新的线速度,并保持当前的垂直速度(以免影响跳跃或跌倒)。


结果,当按下WASD键时,角色开始在场景中移动。他还可以与立方体进行交互,因为它们都是物理对象。


人物动作


移动角色 - 跳跃

为了实现跳跃,我们使用@dimforge/rapier3d-compat@react- Three/rapier包中的功能。在此示例中,我们检查角色是否在地面上并且已按下跳跃键。在本例中,我们在 Y 轴上设置角色的方向和加速力。


对于玩家,我们将在所有轴上添加质量和块旋转,以便他在与场景中的其他物体碰撞时不会在不同方向上摔倒。


播放器.jsx


代码说明:

  1. const world = 剑杆.world;访问Rapier物理引擎场景。它包含所有物理对象并管理它们的交互。
  1. const ray = world.castRay(new RAPIER.Ray(playerRef.current.translation(), { x: 0, y: -1, z: 0 }));这就是“光线投射”(raycasting)发生的地方。创建一条从玩家当前位置开始并指向 y 轴的射线。该光线被“投射”到场景中,以确定它是否与场景中的任何对象相交。
  1. const 接地 = ray && ray.collider && Math.abs(ray.toi) <= 1.5;如果玩家在地面上,则检查条件:
  • ray -射线是否被创建;
  • ray.collider - 射线是否与场景中的任何物体发生碰撞;
  • Math.abs(ray.toi) - 光线的“曝光时间”。如果该值小于或等于给定值,则可能表明玩家距离表面足够近,可以被视为“在地面上”。


您还需要修改Ground组件,以便通过添加将与场景中其他对象交互的物理对象来确定“着陆”状态的光线追踪算法正常工作。


地面.jsx


就让们将手机举高一点儿,以便非常好地了解场景中。


main.jsx


角色跳跃


那部分源代码

将摄像机移动到角色后面

为了移动相机,我们将获取玩家的当前位置,并在每次刷新帧时更改相机的位置。为了使角色精确地沿着相机所指向的轨迹移动,我们需要添加applyEuler


播放器.jsx


代码说明:

applyEuler方法根据指定的欧拉角对向量应用旋转。在这种情况下,相机旋转应用于方向矢量。这用于匹配相对于相机方向的运动,以便玩家沿着相机旋转的方向移动。


让我们稍微调整Player的大小,使其相对于立方体更高,增加CapsuleCollider的大小并修复“跳跃”逻辑。


播放器.jsx


移动相机


部门代码是什么

立方体的生成

为了使场景不会感觉完全空虚,让我们添加立方体生成。在 json 文件中,列出每个立方体的坐标,然后将它们显示在场景上。为此,创建一个文件cubes.json ,我们将在其中列出一个坐标数组。


 [ [0, 0, -7], [2, 0, -7], [4, 0, -7], [6, 0, -7], [8, 0, -7], [10, 0, -7] ]


Cube.jsx文件中,创建一个Cubes组件,它将循环生成立方体。而Cube组件将直接生成对象。


 import {RigidBody} from "@react-three/rapier"; import cubes from "./cubes.json"; export const Cubes = () => { return cubes.map((coords, index) => <Cube key={index} position={coords} />); } const Cube = (props) => { return ( <RigidBody {...props}> <mesh castShadow receiveShadow> <meshStandardMaterial color="white" /> <boxGeometry /> </mesh> </RigidBody> ); }


让我们通过删除之前的单个立方体来将创建的立方体组件添加到应用程序组件中。


应用程序.jsx


立方体的生成


将模型导入到项目中

现代让你们向场合修改 3D 模式。让你们为较色修改法宝模式。让你们从找到 3D 模式刚刚开始。咱们因为例。


下载百度 GLTF 图片格式的模式并将存檔解压到建设项目的根索引中。

为了获得将模型导入场景所需的格式,我们需要安装gltf-pipeline附加包。


npm i -D gltf-pipeline


使用gltf-pipeline包,将模型从GLTF 格式重新转换为GLB 格式,因为在此格式中,所有模型数据都放置在一个文件中。我们指定公共文件夹作为生成文件的输出目录。


gltf-pipeline -i weapon/scene.gltf -o public/weapon.glb


然后我们需要生成一个包含该模型标记的反应组件,以将其添加到场景中。让我们使用@react- Three/Fiber开发人员的。


转到转换器将要求您加载转换后的Weapon.glb文件。


运用拖放或信息菅理器搜素,发现该信息并下载软件。


改装型号


在转换器中,我们将看到生成的反应组件,我们将其代码传输到新文件WeaponModel.jsx中的项目,将组件的名称更改为与文件相同的名称。


现场展示武器模型

现在让我们将创建的模型导入到场景中。在App.jsx文件中添加WeaponModel组件。


应用程序.jsx


导入模型演示


添加阴影

倘若,在让我们的场景设计中,没得其他对象图片也正在投到隐影。

要在场景上启用阴影,您需要将阴影属性添加到Canvas组件。


main.jsx


接下来,我们需要添加一个新的光源。尽管场景中已经有了环境光,但它无法为对象创建阴影,因为它没有定向光束。因此,让我们添加一个名为orientationLight 的新光源并对其进行配置。启用“投射”阴影模式的属性是castShadow 。正是这个参数的添加,表明这个物体可以给其他物体投射阴影。


应用程序.jsx


之后,我们给Ground组件添加另一个属性receiveShadow ,这意味着场景中的组件可以接收并显示自身的阴影。


地面.jsx


模型投下阴影


类似的属性应该添加到场景中的其他对象:立方体和玩家。对于立方体,我们将添加castShadowreceiveShadow ,因为它们都可以投射和接收阴影,而对于玩家,我们将仅添加castShadow


让我们为Player添加castShadow


播放器.jsx


Cube添加castShadowreceiveShadow


立方体.jsx


场景中的所有物体都投射阴影


添加阴影 - 修正阴影剪切

现代若你细细观察植物,你就会发现投影阴霾的外表积很大小。而当过大这些城市时,树影就被非常简单地立即切断了。


阴影裁剪


原因是默认情况下相机仅捕获来自orientationLight的显示阴影的一小部分区域。我们可以通过为orientationLight组件添加额外的属性shadow-camera-(top,bottom,left,right)来扩展这个区域的可见性。添加这些属性后,阴影会变得稍微模糊。为了提高质量,我们将添加shadow-mapSize属性。


应用程序.jsx


将武器绑定到角色

现在让我们添加第一人称武器显示。创建一个新的武器组件,其中将包含武器行为逻辑和 3D 模型本身。


 import {WeaponModel} from "./WeaponModel.jsx"; export const Weapon = (props) => { return ( <group {...props}> <WeaponModel /> </group> ); }


让我们将此组件放置在与角色的RigidBody相同的水平上,并在useFrame挂钩中,我们将根据相机值的位置设置位置和旋转角度。


播放器.jsx


第一人称武器模型展示


行走时武器摆动的动画

为了使角色的步态更加自然,我们将在移动时添加武器的轻微摆动。为了创建动画,我们将使用已安装的tween.js库。


Weapon组件将被包装在一个组标签中,以便您可以通过useRef挂钩添加对它的引用。


播放器.jsx


让我们添加一些useState来保存动画。


播放器.jsx


我就们组建一位指数函数来初期化动漫。


播放器.jsx


代码说明:

  1. const twSwayingAnimation = new TWEEN.Tween(currentPosition) ...创建对象从当前位置“摆动”到新位置的动画。
  1. const twSwayingBackAnimation = new TWEEN.Tween(currentPosition) ...创建第一个动画完成后对象返回到其起始位置的动画。
  1. twSwayingAnimation.chain(twSwayingBackAnimation);连接两个动画,以便当第一个动画完成时,第二个动画自动开始。


useEffect中我们调用动画初始化函数。


播放器.jsx


现再有用不着选定移动发生的的时间表。这行使用选定阵营目标方向的当前向量来成功。


如作用会发生手机,公司将获取h动画并在已完成后第三步使用。


播放器.jsx


代码说明:

  1. const isMoving = Direction.length() > 0;这里检查对象的运动状态。如果方向向量的长度大于0,则表示物体有运动方向。
  1. if (isMoving && isSwayingAnimationFinished) { ... }如果对象正在移动并且“摆动”动画已完成,则执行此状态。


App组件中,我们添加一个useFrame来更新补间动画。


应用程序.jsx


TWEEN.update()更新TWEEN.js库中的所有活动动画。在每个动画帧上调用此方法以确保所有动画顺利运行。


要素源代码:

反冲动画

我们需要定义射击的时刻 - 即按下鼠标按钮的时刻。让我们添加useState来存储此状态, useRef来存储对武器对象的引用,以及两个用于按下和释放鼠标按钮的事件处理程序。


武器.jsx


武器.jsx


武器.jsx


让我们在单击鼠标按钮时实现反冲动画。为此,我们将使用tween.js库。


令你们理解反冲力和动画作品维持日子的常量。


武器.jsx


与手段偏移视频差不多,.我为反冲和重返启始的位置视频修改了两个人 useState 情况,甚至一款更具视频结尾情况的情况。


武器.jsx


让我们创建函数来获取反冲动画的随机向量-generateRecoilOffsetgenerateNewPositionOfRecoil


武器.jsx


创建一个函数来初始化反冲动画。我们还将添加useEffect ,其中我们将指定“镜头”状态作为依赖项,以便在每次镜头时再次初始化动画并生成新的结束坐标。


武器.jsx


武器.jsx


useFrame中,我们添加一个检查“按住”鼠标键以进行射击,以便在释放按键之前射击动画不会停止。


武器.jsx


反冲动画


不活动期间的动画

建立阵营“不活动”的动画电影,让游戏手机并没有“挂”的感受到。


为此,我们通过useState添加一些新状态。


播放器.jsx


我不想们解决“偏移”动漫的起始化以用到的感觉中的值。一个思路是,有所区别的的感觉:前进或暂停,将用到有所区别的动漫值,还有就是每次在动漫都可以首要起始化。


播放器.jsx


空闲动画


结论

你在一个局部分中,我都大家进行了不一样导出和职业电信。我都大家还移除了重武器实体模型、速射时和充分利用时的反歇斯底里画。在下一个局部分中,我都大家将马上建全我都大家的手游,移除新模块。


也发布消息。


바카라사이트 바카라사이트 온라인바카라