vlambda博客
学习文章列表

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠

「游戏故事」

游戏讲述的是一个小女生被恶魔诅咒找不到家了,她听说收集七龙珠可以召唤神龙,神龙可是帮她实现回家的愿望,于是她开启了她的冒险故事

「使用技术」

这个游戏使用了以下技术

  1. vite + React
  2. 基于 Three.jslingo3d

以及使用了以下工具:

  1. sketchfab:3D模型下载
  2. mixamo:3D人物动作绑定及动画
  3. readyplayer:3D角色生产工具
  4. gltf.report:模型压缩
  5. polyhaven:hdr素材库(环境贴图)
  6. textures:材质贴图素材

「部分效果实现如下 👇」

20.gif
别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
22.gif
别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
24.gif
别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png
别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png
别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png
别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png
别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
25.gif

开始

其实我也不知道从哪里开始,也不知道写什么好?

但我好像需要一个角色,那就从创建角色开始

创建一个角色

下载角色

首先到 sketchfab 中下载一个角色模型

当然如果你是首次使用 sketchfab ,记得先注册和登录

注册好之后,就会看到以下界面

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

箭头指的输入框输入关键字,即可筛选出相关的模型,然后我们从中进行挑选

注意,挑选人物模型时,最好选择 T-pose 类型,比如 这样可以方便之后绑定骨骼

我这里选择了这下面这个小妹妹,我觉得还不错

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

然后点击右上角的下载按钮,进入下载界面

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

一般fbx使用的比较多,但是有的时候不是fbx的格式,我们需要使用其他工具转格式,或者处理模型

为了方便,我们这里选择第二个:glTF 格式,点击 DOWNLOAD

下载好之后是个压缩包,我们进行解压,解压后目录如下

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

处理角色模型

接着我们选择使用模型处理软件来处理我们的模型,比如我这里选择 blender

当然得先下载软件,我们进入官网,然后点击如下按钮进行下载

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

安装就直接点击下一步下一步就行,之后打开软件,看到以下界面

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

开始进来中间是有一个立方体的,我们需要移除。步骤是鼠标选中,按x删除

之后按如下步骤导入我们刚刚下载的模型

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png
别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

结果如下,表示导入成功

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

但看不到人,可以按x或者Delete将绑定的骨骼删除,即可展示人物

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
1.gif

但是人物怎么没有颜色,这时因为这时展现的是实体,我们可以按z 甩狙,选择渲染即可

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
2.gif

选择的模型比较正常,所以不用太多的处理,接着我们将它转成fbx格式,但是在转之前需要将灯光和相机移除,以防止模型导入页面时光线等问题

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

可以跟之前一样选择中,按x即可删除

下面开始将它转成fbx格式,直接按以下步骤导出即可

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

需要注意,导出时需要将路径模式改成复制,并打开内嵌纹理。否则导出的模型将没有颜色

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

制作动画

模型有了之后,我们我们的让模型动起来,这是我们需要使用动画库相关的软件,这里选择使用我选择 Mixamo,他是Adobe公司出品的免费动画库,它提供了大量的人物动画

进入www.mixamo.com/#/ 然后上传刚刚我们自己的3d模型,步骤如下

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

之后等待上传

上传成功会显示如下界面

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

然后点击 NEXT,绑定骨骼

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
3.gif

成功之后点击 NEXT,成功之后将看到如下界面

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
4.gif

这时,我们先将T-pose下载下来别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠

之后,我们可以在左边选择我们需要的动画,比如选个跳舞

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
5.gif

没错就是这么简单

之后可以 点击 DOWNLOAD 开始下载动画

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

类似步骤,我们可以下载很多我们需要的动画

准备好动画等素材之后,开始准备搭建项目,然后将相关模型渲染到我们的页面中

搭建项目

这是选择使用vite来搭建一个react项目:

yarn create vite
复制代码

创建项目之后,进入项目、安装依赖

cd [项目名称]
yarn
yarn dev
复制代码

然后安装一个lingo3d-react

yarn add lingo3d-react
复制代码

接着我们修改src\App.jsx文件,将多余的东西都去掉,同时添加<World>场景标签。代码变成如下样子:

import { World } from "lingo3d-react"

function App({

  return (
    <World>

    </World>

  )
}

export default App
复制代码

接着启动项目

yarn dev
复制代码

成功之后,显示如下

vite v2.9.5 dev server running at:

> Local: http://localhost:3000/
> Network: use `--host` to expose

ready in 359ms.
复制代码

接着就可以进入http://localhost:3000/

进去就可以看到一个默认的一个黑的场景,如下:

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

到这,说明项目已经构建成功!下面就是要加载我们的角色模型和场景模型

加载模型

加载角色

首先需要创建一个public文件夹,并将我们下载好的模型放进去

接着使用 <Model> 加载我们的模型

import { Model, World } from "lingo3d-react"

function App({

  return (
    <World>
      <Model
        src="girl.fbx"
        animations={{ idle: "Standing Idle.fbx"}}
        animation="idle"
        scale={3}
      >

      </Model>
    </World >
  )
}

export default App
复制代码

属性说明:

  • src 是我们的T-pose
  • animations 是所要用的动画
  • animation 是当前动画
  • scale 是缩放

这里有个坑,就是src中一定得是T-pose,所以在最开始时一定要导出T-pose,否则动画加载不出来。

到这,我们的角色动画就出来了

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

有了小妹妹,接下来就是给她一个家

加载房子

更上述类似的步骤,我们添加一个房子

import { Model, World } from "lingo3d-react"

function App({

  return (
    <World>
      <Model
        src="girl.fbx"
        animations={{ idle: "Standing Idle.fbx"}}
        animation="idle"
        scale={4}
      >

      </Model>

      <Model
        src="Home.glb"
        scale={7}
      >

      </Model>
    </World >
  )
}

export default App
复制代码

结果如下

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

但大小需要调整,另外视角需要调整,可以使用第一人称或者第三人称相机来切换视角

使用第三人称相机 ThirdPersonCamera

使用ThirdPersonCamera表示已第三人称的角度看到人物,代码如下

import { Model, ThirdPersonCamera, World } from "lingo3d-react"

function App({

  return (
    <World>

      <ThirdPersonCamera active mouseControl>
        <Model
          src="girl.fbx"
          animations={{ idle: "Standing Idle.fbx" }}
          animation="idle"
          scale={3}
        />

      </ThirdPersonCamera>

      <Model
        src="Home.glb"
        scale={5}
      />

    </World >
  )
}

export default App
复制代码

其中

  • active 属性表示激活
  • mouseControl 表示鼠标的控制

效果如下:

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
6.gif

但是此时小妹妹和房子捏在一起了,这是可以设置模型的物理属性

物理属性 physics

物理属性physics的值有很多,这里设置角色physics="character",设置房子physics="map"

  • character 表示主角
  • map 表示地图

代码如下:

import { Model, ThirdPersonCamera, World } from "lingo3d-react"

function App({

  return (
    <World>

      <ThirdPersonCamera active mouseControl>
        <Model
          src="girl.fbx"
          physics="character"
          animations={{ idle: "Standing Idle.fbx" }}
          animation="idle"
          scale={1}
        />

      </ThirdPersonCamera>

      <Model
        src="Home.glb"
        scale={10}
        physics="map"
      />

    </World >
  )
}

export default App
复制代码

效果如下

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

可以看到人物就和房子分离开了,但还有一个问题是:天空是黑色的

设置天空我们需要用到天空盒 Skybox

添加 Skybox

查找天空背景

查找天空背景可以google或者百度输关键字:equirectangular sky / skybox background

添加Skybox

然后再<World >中添加<Skybox texture="xxx" />,比如:

<Skybox texture="skybox.jpeg" />
复制代码

这样就有天空啦,效果如下:

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png
别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
7.gif

但我发现人物有点浮在空中,应该是模型的物理碰撞结构的原因

我们将map换成map-debug检查一下

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png
别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

发现,这个模型里好多小雪花,被计算成碰撞体积了。

所以得调好人物的初始位置

调好之后再来换个场景,比如我看到下面这个场景感觉不错

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

接着我们将场景下载并导入

import { Model, Skybox, ThirdPersonCamera, World } from "lingo3d-react"

function App({

  return (
    <World>

      <ThirdPersonCamera active mouseControl>
        <Model
          src="girl.fbx"
          physics="character"
          animations={{ idle: "idle.fbx",walking:"walking.fbx" }}
          animation="idle"
          scale={1}
        />

      </ThirdPersonCamera>

      <Model
        src="map/scene.gltf"
        scale={30}
        physics="map"
      />

      <Skybox texture="skybox.jpg" />
    </World >
  )
}

export default App
复制代码

显示结果如下:

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png
别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

接着我想让角色可以用键盘wsad控制上、下、左、右的移动

角色移动

首先得在mixamo准备好移动相关的动画,比如走路、跑步等

将人物模型的animation设置成对应的动画,比如animation="walking",这是就会用走路的动画了

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
8.gif

其他相关的动画也是这样设置

下面整理一下如何让角色移动的思路

  1. 初始是站立状态
  2. 当我们按下键盘wsad键时,触发走路动画,角色分别向前、后、左、右的移动;如果短时间连续按两下,触发跑步动画
  3. 当松开按键时,角色进入初始站立状态

实现前后移动

当按下w时,切换向前移动动画,并向前移动;当按下s键时,切换倒退动画,人物向后移动;

代码如下

import { Model, Skybox, ThirdPersonCamera, useKeyboard, useLoop, World } from "lingo3d-react"
import { createRef, useRef } from "react"

function App({
  // useKeyboard用于监控当前按键
  const key = useKeyboard()
  const characterRef = createRef()
  //声明motion,用于表示当前角色应该对应的动画,默认为站立idle
  let motion = "idle";
  // 前
  if (key === "w") {
    motion = "walking"
  }
  // 后
  if (key === "s") {
    motion = "walking_backwards"
  }

  // useLoop 帧循环勾子
  useLoop(() => {
    characterRef.current.moveForward(-3)
  }, key === "w");

  useLoop(() => {
    characterRef.current.moveForward(3)
  }, key === "s");

  return (
    <World>
      <ThirdPersonCamera active>
        <Model
          ref={characterRef}
          src="girl.fbx"
          physics="character"
          animations={{ idle: "idle.fbx", walking: "walking.fbx", walking_backwards: "walking-backwards.fbx" }}
          animation={motion}
          scale={1}
        />

      </ThirdPersonCamera>

      <Model
        src="map/scene.gltf"
        scale={40}
        physics="map"
      />

      <Skybox texture="skybox.jpg" />
    </World >
  )
}

export default App
复制代码

实现效果如下

按w时,向前 👇

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
9.gif

按a时,向后 👇

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
11.gif

左转、右转

左转右转的改变,可以通过鼠标来控制,即mouseControl属性

<ThirdPersonCamera active mouseControl>
复制代码

效果如下

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
13.gif

添加跑步动画

由于上面是用w表示走路,这里就用w加e的时候实现跑步吧,代码如下:

import { Model, Skybox, ThirdPersonCamera, useKeyboard, useLoop, World } from "lingo3d-react"
import { createRef, useRef } from "react"

function App({
  // useKeyboard用于监控当前按键
  const key = useKeyboard()
  console.log(key);
  const characterRef = createRef()
  //声明motion,用于表示当前角色应该对应的动画,默认为站立idle
  let motion = "idle";
  // 前
  if (key === "w") {
    motion = "walking"
  }
  // 后
  if (key === "s") {
    motion = "walkingBackwards"
  }
  // 按下w和e时,开始跑
  if (key === "w e") {
    motion = "running"
  }

  // useLoop 帧循环勾子
  useLoop(() => {
    characterRef.current.moveForward(-4)
  }, key === "w");
  useLoop(() => {
    characterRef.current.moveForward(1.8)
  }, key === "s");
  useLoop(() => {
    characterRef.current.moveForward(-10)
  }, key === "w e");

  return (
    <World>
      <Skybox texture="skybox.jpg" />

      <ThirdPersonCamera active mouseControl>
        <Model
          ref={characterRef}
          src="girl.fbx"
          physics="character"
          animations={{
            idle: "idle.fbx",
            walking: "walking.fbx",
            walkingBackwards: "walking-backwards.fbx",
            running: "running.fbx"
          }}
          animation={motion}
          scale={1}
        />

      </ThirdPersonCamera>

      <Model
        src="map/scene.gltf"
        scale={40}
        physics="map"
      />


      <Model
        src="dragon_ball/scene.gltf"
        scale={10}
        physics="character"
      />

    </World >
  )
}

export default App
复制代码

效果如下

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
15.gif

接着,要不找点什么东西吧,比如收集七颗龙珠

收集七龙珠

引入7龙珠

首先得下载龙珠,步骤跟之前一样,之后引入

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

移动角色和龙珠到合适的位置

下面我希望将角色和龙珠的初始位置移动到合适的地方

可以通过model身上的x、y、z来控制,但是具体多少呢?可以使用Editor组件开启编辑模式,来测

xxx
return (
    <>
      <World>    
      </World >      
      //开启编辑模式
      <Editor/> 
    </>
)
复制代码

于是就可以看到以下的编辑模式,可以用鼠标进行移动

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

移动好之后可以记下右边的数据,主要position和rotation

比如

<ThirdPersonCamera active mouseControl>
  <Model
    ref={characterRef}
    src="girl.fbx"
    physics="character"
    animations={{
      idle: "idle.fbx",
      walking: "walking.fbx",
      walkingBackwards: "walking-backwards.fbx",
      running: "running.fbx"
    }}
    animation={motion}
    scale={1}
    x={-221.30}
    y={-1300.07}
    z={-6722.07}
    rotationY={-180}
  />

</ThirdPersonCamera>

<Model
  ref={ballRef}
  src="dragon_ball.fbx"
  physics="character"
  x={516.29}
  y={-1198.63}
  z={173.60}
  scale={.5}
/
>
复制代码

寻找龙珠

我们可以通过w和s和w+e以及配合鼠标来控制角色移动来寻找龙珠,当寻找到龙珠时,即准心对准龙珠时,让龙珠有变化,比如高亮或者啥的

所以,首先弄个准心吧

准心

通过blender找到龙珠对应的名字

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

然后在龙珠模型中添加一个Find标签

<Model id="ball"
  ref={ballRef}
  src="dragon_ball.fbx"
  physics="character"
  x={516.29}
  y={-1198.63}
  z={173.60}
  scale={.5}
>
  <Find name="Two Star_02 - Default_0" outline></Find>
</Model>
复制代码

表示的是在模型中找到的东西,我们让他outline,以示区分,效果可以之后调

展示如下

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

但是默认情况,我们应该是FALSE,不显示的

这里引入状态来控制,如下代码

//...
const [mouseOver,setMouseOver] = useState(false);
//....

<Find name="Two Star_02 - Default_0"
  outline={mouseOver}
  onMouseOver={() =>
 {
    setMouseOver(true)
  }} ></Find>

复制代码

这样的话,默认就是不显示发光特效,只有鼠标准心对准过后才发光,表示找到了的

接着我们需要添加准心,来方便找,通过Reticle

<World >
</World >

<Reticle color="white" variant={1}/
>
//xxx
复制代码
  • color是准心的颜色;
  • variant是准心的形状,可以通过不同数字选择自己喜欢的形状

上述代码效果如下 👇

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

但是此时「瞄准器」在人物的屁股上,希望的是瞄准器在人物的头顶上,相当于是人物的目光

所以我的调整相机的位置,让相机升到人物的头顶上去

调整相机位置

其实可以通过相机的innerX、innerY、innerZ来设置,如下代码

<ThirdPersonCamera active mouseControl innerY={66}>
复制代码

效果如下

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

找到龙珠

当找到龙珠并点击时,龙珠亮

代码如下

<Model id="ball"
  ref={ballRef}
  src="dragon_ball.fbx"
  physics="character"
  x={516.29}
  y={-1198.63}
  z={173.60}
  scale={.5}
>
<Find name="Two Star_02 - Default_0"
  outline={mouseOver}
  onClick={() =>
 {
    setMouseOver(true)
  }} ></Find>

</Model>
复制代码

效果如下

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
16.gif

类似的方式将其他龙珠添加到合适的位置

另一种寻找方式

除了看到点击可以点亮龙珠以外,当角色和龙珠碰撞时也应点亮龙珠

那首先要检测角色碰撞

步骤如下:

  1. 先给龙珠添加一个id
  2. 给角色添加intersectID属性,值为一个数组,数组的每一项是每个龙珠的id名
  3. 再给角色添加onIntersect属性,值为一个回调函数,这个回调函数会在角色和龙珠发生碰撞时触发

所以,需要做的事情都是这个回调函数中执行。

这步之后加上吧

神龙出现

神龙出现,小龙消失

当所有龙珠都找到时,地图上某个地方就会出现龙,你需要找到它

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

找到之后点击它,小龙消失,真神龙就会现身了

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

神龙许愿

点击神龙,过一会就会送我回到家里,并且回去之后可以看到7颗龙珠

完整的游戏过程

1. 通过按键和鼠标控制角色移动

w键:向前跑

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
20.gif

s键:向后走

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
21.gif

鼠标:移动鼠标可控制方向

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
23.gif

空格:跳跃

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
22.gif

2. 寻找七龙珠

当看到龙珠时,对准并按下鼠标即可标记此龙珠已经找到,然后继续找下一颗

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
24.gif

当所有龙珠被找到时,会提示地图某处会出现龙

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

3. 寻找龙

当提示地图某处出现龙时,就去寻找龙

此龙如图所示

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

但是到这没有结束,此龙非真的神龙

4. 真神龙出现

点击小龙,小龙会消失,真的神龙出现

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png
别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

点击神龙,一会就会实现回家的愿望

5. 回到家

到这就会回到家了,如下

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

而且家附近会出现我们找到的龙珠

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
image.png

最后

最后能回到家,肯定开心啦

所以按住d键,开始跳舞吧

别卷了,快来玩 | React+Three.js 实现一个超好玩的3D游戏:美女与龙珠
25.gif

最后