別捲了,快來玩 | React+Three.js 實現一個超好玩的3D遊戲:美女與龍珠
theme: cyanosis
我正在參加掘金社區遊戲創意投稿大賽個人賽,詳情請看:遊戲創意投稿大賽
這兩天我想寫了個小遊戲,參加這個徵文活動 👆
遊戲故事
遊戲講述的是一個小女生被惡魔詛咒找不到家了,她聽説收集七龍珠可以召喚神龍,神龍可是幫她實現回家的願望,於是她開啟了她的冒險故事
使用技術
這個遊戲使用了以下技術
1. vite
+ React
2. 基於 Three.js
的 lingo3d
以及使用了以下工具:
1. sketchfab
: 3D模型下載
2. mixamo
:3D人物動作綁定及動畫
3. readyplayer
:3D角色生產工具
4. gltf.report
:模型壓縮
5. polyhaven
:hdr素材庫(環境貼圖)
6. textures
:材質貼圖素材
部分效果實現如下 👇
開始
其實我也不知道從哪裏開始,也不知道寫什麼好?
但我好像需要一個角色,那就從創建角色開始
創建一個角色
下載角色
首先到 sketchfab 中下載一個角色模型
當然如果你是首次使用 sketchfab
,記得先註冊和登錄
註冊好之後,就會看到以下界面
箭頭指的輸入框輸入關鍵字,即可篩選出相關的模型,然後我們從中進行挑選
注意,挑選人物模型時,最好選擇 T-pose 類型,比如 這樣可以方便之後綁定骨骼
我這裏選擇了這下面這個小妹妹,我覺得還不錯
然後點擊右上角的下載按鈕,進入下載界面
一般fbx使用的比較多,但是有的時候不是fbx的格式,我們需要使用其他工具轉格式,或者處理模型
為了方便,我們這裏選擇第二個:glTF 格式,點擊 DOWNLOAD
下載好之後是個壓縮包,我們進行解壓,解壓後目錄如下
處理角色模型
接着我們選擇使用模型處理軟件來處理我們的模型,比如我這裏選擇 blender
當然得先下載軟件,我們進入官網,然後點擊如下按鈕進行下載
安裝就直接點擊下一步下一步就行,之後打開軟件,看到以下界面
開始進來中間是有一個立方體的,我們需要移除。步驟是鼠標選中,按x刪除
之後按如下步驟導入我們剛剛下載的模型
結果如下,表示導入成功
但看不到人,可以按x或者Delete將綁定的骨骼刪除,即可展示人物
但是人物怎麼沒有顏色,這時因為這時展現的是實體,我們可以按z 甩狙,選擇渲染即可
選擇的模型比較正常,所以不用太多的處理,接着我們將它轉成fbx格式,但是在轉之前需要將燈光和相機移除,以防止模型導入頁面時光線等問題
可以跟之前一樣選擇中,按x即可刪除
下面開始將它轉成fbx格式,直接按以下步驟導出即可
需要注意,導出時需要將路徑模式改成複製,並打開內嵌紋理。否則導出的模型將沒有顏色
製作動畫
模型有了之後,我們我們的讓模型動起來,這是我們需要使用動畫庫相關的軟件,這裏選擇使用我選擇 Mixamo
,他是Adobe公司出品的免費動畫庫,它提供了大量的人物動畫
進入http://www.mixamo.com/#/ 然後上傳剛剛我們自己的3d模型,步驟如下
之後等待上傳
上傳成功會顯示如下界面
然後點擊 NEXT,綁定骨骼
成功之後點擊 NEXT,成功之後將看到如下界面
這時,我們先將T-pose下載下來
之後,我們可以在左邊選擇我們需要的動畫,比如選個跳舞
沒錯就是這麼簡單
之後可以 點擊 DOWNLOAD 開始下載動畫
類似步驟,我們可以下載很多我們需要的動畫
準備好動畫等素材之後,開始準備搭建項目,然後將相關模型渲染到我們的頁面中
搭建項目
這是選擇使用vite來搭建一個react項目:
yarn create vite
創建項目之後,進入項目、安裝依賴
cd [項目名稱]
yarn
yarn dev
然後安裝一個lingo3d-react
yarn add lingo3d-react
接着我們修改src\App.jsx文件,將多餘的東西都去掉,同時添加<World>
場景標籤。代碼變成如下樣子:
```jsx import { World } from "lingo3d-react"
function App() {
return (
</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/
進去就可以看到一個默認的一個黑的場景,如下:
到這,説明項目已經構建成功!下面就是要加載我們的角色模型和場景模型
加載模型
加載角色
首先需要創建一個public文件夾,並將我們下載好的模型放進去
接着使用 <Model>
加載我們的模型
```jsx import { Model, World } from "lingo3d-react"
function App() {
return (
export default App ``` 屬性説明: - src 是我們的T-pose - animations 是所要用的動畫 - animation 是當前動畫 - scale 是縮放
這裏有個坑,就是src中一定得是T-pose,所以在最開始時一定要導出T-pose,否則動畫加載不出來。
到這,我們的角色動畫就出來了
有了小妹妹,接下來就是給她一個家
加載房子
更上述類似的步驟,我們添加一個房子
```jsx import { Model, World } from "lingo3d-react"
function App() {
return (
<Model
src="Home.glb"
scale={7}
>
</Model>
</World >
) }
export default App ```
結果如下
但大小需要調整,另外視角需要調整,可以使用第一人稱或者第三人稱相機來切換視角
使用第三人稱相機 ThirdPersonCamera
使用ThirdPersonCamera表示已第三人稱的角度看到人物,代碼如下
```jsx import { Model, ThirdPersonCamera, World } from "lingo3d-react"
function App() {
return (
<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 表示鼠標的控制
效果如下:
但是此時小妹妹和房子捏在一起了,這是可以設置模型的物理屬性
物理屬性 physics
物理屬性physics的值有很多,這裏設置角色physics="character",設置房子physics="map"
- character 表示主角
- map 表示地圖
代碼如下:
```js import { Model, ThirdPersonCamera, World } from "lingo3d-react"
function App() {
return (
<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 ```
效果如下
可以看到人物就和房子分離開了,但還有一個問題是:天空是黑色的
設置天空我們需要用到天空盒 Skybox
添加 Skybox
查找天空背景
查找天空背景可以google或者百度輸關鍵字:equirectangular sky / skybox background
添加Skybox
然後再<World >
中添加<Skybox texture="xxx" />
,比如:
html
<Skybox texture="skybox.jpeg" />
這樣就有天空啦,效果如下:
但我發現人物有點浮在空中,應該是模型的物理碰撞結構的原因
我們將map換成map-debug檢查一下
發現,這個模型裏好多小雪花,被計算成碰撞體積了。
所以得調好人物的初始位置
調好之後再來換個場景,比如我看到下面這個場景感覺不錯
接着我們將場景下載並導入
```jsx import { Model, Skybox, ThirdPersonCamera, World } from "lingo3d-react"
function App() {
return (
<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 ```
顯示結果如下:
接着我想讓角色可以用鍵盤wsad控制上、下、左、右的移動
角色移動
首先得在mixamo
準備好移動相關的動畫,比如走路、跑步等
將人物模型的animation設置成對應的動畫,比如animation="walking",這是就會用走路的動畫了
其他相關的動畫也是這樣設置
下面整理一下如何讓角色移動的思路
- 初始是站立狀態
- 當我們按下鍵盤wsad鍵時,觸發走路動畫,角色分別向前、後、左、右的移動;如果短時間連續按兩下,觸發跑步動畫
- 當鬆開按鍵時,角色進入初始站立狀態
實現前後移動
當按下w時,切換向前移動動畫,並向前移動;當按下s鍵時,切換倒退動畫,人物向後移動;
代碼如下
```jsx 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 (
<Model
src="map/scene.gltf"
scale={40}
physics="map"
/>
<Skybox texture="skybox.jpg" />
</World >
) }
export default App ``` 實現效果如下
按w時,向前 👇
按a時,向後 👇
左轉、右轉
左轉右轉的改變,可以通過鼠標來控制,即mouseControl屬性
html
<ThirdPersonCamera active mouseControl>
效果如下
添加跑步動畫
由於上面是用w表示走路,這裏就用w加e的時候實現跑步吧,代碼如下: ```jsx 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 (
<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 ``` 效果如下
接着,要不找點什麼東西吧,比如收集七顆龍珠
收集七龍珠
引入7龍珠
首先得下載龍珠,步驟跟之前一樣,之後引入
移動角色和龍珠到合適的位置
下面我希望將角色和龍珠的初始位置移動到合適的地方
可以通過model身上的x、y、z來控制,但是具體多少呢?可以使用Editor組件開啟編輯模式,來測
jsx
xxx
return (
<>
<World>
</World >
//開啟編輯模式
<Editor/>
)
於是就可以看到以下的編輯模式,可以用鼠標進行移動
移動好之後可以記下右邊的數據,主要position和rotation
比如
```jsx
尋找龍珠
我們可以通過w和s和w+e以及配合鼠標來控制角色移動來尋找龍珠,當尋找到龍珠時,即準心對準龍珠時,讓龍珠有變化,比如高亮或者啥的
所以,首先弄個準心吧
準心
通過blender找到龍珠對應的名字
然後在龍珠模型中添加一個Find標籤 ```jsx <Model id="ball" ref={ballRef} src="dragon_ball.fbx" physics="character" x={516.29} y={-1198.63} z={173.60} scale={.5}
``` 表示的是在模型中找到的東西,我們讓他outline,以示區分,效果可以之後調
展示如下
但是默認情況,我們應該是FALSE,不顯示的
這裏引入狀態來控制,如下代碼 ```jsx //... const [mouseOver,setMouseOver] = useState(false); //....
接着我們需要添加準心,來方便找,通過Reticle
```jsx
上述代碼效果如下 👇
但是此時瞄準器在人物的屁股上,希望的是瞄準器在人物的頭頂上,相當於是人物的目光
所以我的調整相機的位置,讓相機升到人物的頭頂上去
調整相機位置
其實可以通過相機的innerX、innerY、innerZ來設置,如下代碼
jsx
<ThirdPersonCamera active mouseControl innerY={66}>
效果如下
找到龍珠
當找到龍珠並點擊時,龍珠亮
代碼如下
```jsx <Model id="ball" ref={ballRef} src="dragon_ball.fbx" physics="character" x={516.29} y={-1198.63} z={173.60} scale={.5}
{ setMouseOver(true) }} > ``` 效果如下
類似的方式將其他龍珠添加到合適的位置
另一種尋找方式
除了看到點擊可以點亮龍珠以外,當角色和龍珠碰撞時也應點亮龍珠
那首先要檢測角色碰撞
步驟如下: 1. 先給龍珠添加一個id 2. 給角色添加intersectID屬性,值為一個數組,數組的每一項是每個龍珠的id名 3. 再給角色添加onIntersect屬性,值為一個回調函數,這個回調函數會在角色和龍珠發生碰撞時觸發
所以,需要做的事情都是這個回調函數中執行。
這步之後加上吧
神龍出現
神龍出現,小龍消失
當所有龍珠都找到時,地圖上某個地方就會出現龍,你需要找到它
找到之後點擊它,小龍消失,真神龍就會現身了
神龍許願
點擊神龍,過一會就會送我回到家裏,並且回去之後可以看到7顆龍珠
完整的遊戲過程
1. 通過按鍵和鼠標控制角色移動
w鍵:向前跑
s鍵:向後走
鼠標:移動鼠標可控制方向
空格:跳躍
2. 尋找七龍珠
當看到龍珠時,對準並按下鼠標即可標記此龍珠已經找到,然後繼續找下一顆
當所有龍珠被找到時,會提示地圖某處會出現龍
3. 尋找龍
當提示地圖某處出現龍時,就去尋找龍
此龍如圖所示
但是到這沒有結束,此龍非真的神龍
4. 真神龍出現
點擊小龍,小龍會消失,真的神龍出現
點擊神龍,一會就會實現回家的願望
5. 回到家
到這就會回到家了,如下
而且家附近會出現我們找到的龍珠
最後
最後能回到家,肯定開心啦
所以按住d鍵,開始跳舞吧
結語
以上就是本文的所有內容啦,源碼這兩天完善後回上傳github,敬請期待~
- 別捲了,快來玩 | React Three.js 實現一個超好玩的3D遊戲:美女與龍珠
- 2022 年值得推薦的 React 庫
- 2022前端應該掌握的10個 JS 小技巧
- vue-next已正式更名為core,Vue3即將轉正迎來春天
- 分享一些2021年個人收藏的前端學習資料
- 推薦一款免費的Markdown編輯器,GitHub斬獲22.8k Star!
- 你知道TS中的三斜線指令嗎
- TS的三種JSX模式
- TS中的命名空間合併
- 在TS中最常見的聲明合併:接口合併
- TS模塊解析策略之Node
- 一分鐘瞭解Node.js的模塊解析過程
- TS模塊解析策略之Classic
- 你知道TS中的相對和非相對模塊導入有什麼不同嗎?
- TS模塊解析流程
- 規避2個TS中命名空間和模塊的陷阱
- 來看看TS中,簡化命名空間操作的方法
- TS中將單個文件分離到不同的文件中
- 如何在TS裏使用命名空間,來組織你的代碼
- TS的模塊沿用了JS模塊的概念