本系列文已改編成書「甚麼?網頁也可以做派對遊戲?使用 Vue 和 babylon.js 打造 3D 派對遊戲吧!」
書中不只重構了程式架構、改善了介面設計,還新增了 2 個新遊戲呦!ˋ( ° ▽、° )
新遊戲分別使用了陀螺儀與震動回饋,趕快買書來研究研究吧!ლ(╹∀╹ლ)
在此感謝深智數位的協助,歡迎大家前往購書,鱈魚感謝大家 (。・∀・)。
助教:「所以到底差在哪啊?沒圖沒真相,被你坑了都不知道。(´。_。`)」
鱈魚:「你對我是不是有甚麼很深的偏見啊 (っ °Д °;)っ,來人啊,上連結!」
讓我們利用 babylon.js 打造 3D 遊戲吧!◝( •ω• )◟
babylon.js 產生遊戲畫面需要以下幾個步驟。
這樣才能產生一個看的到的畫面。
讓我們引入 babylon.js 並依序新增建立場景所需的 function 吧。
import { ArcRotateCamera, Engine, Scene, Vector3 } from '@babylonjs/core';
...
const canvas = ref<HTMLCanvasElement>();
let engine: Engine;
let scene: Scene;
function createEngine(canvas: HTMLCanvasElement) {
const engine = new Engine(canvas, true);
return engine;
}
function createScene(engine: Engine) {
const scene = new Scene(engine);
/** 使用預設光源 */
scene.createDefaultLight();
return scene;
}
function createCamera(scene: Scene) {
const camera = new ArcRotateCamera(
'camera',
-Math.PI / 2,
Math.PI / 4,
34,
new Vector3(0, 0, -2),
scene
);
return camera;
}
參考資料:
接著增加 init() 內容,開始建立場景。
function init() {
if (!canvas.value) {
console.error('無法取得 canvas DOM');
return;
}
engine = createEngine(canvas.value);
scene = createScene(engine);
createCamera(scene);
/** 反覆渲染場景,這樣畫面才會持續變化 */
engine.runRenderLoop(() => {
scene.render();
});
loading.hide();
}
現在讓我們在 onMounted 中呼叫 init() 吧。
<template>
...
</template>
<script setup lang="ts">
import { ArcRotateCamera, Engine, Scene, Vector3 } from '@babylonjs/core';
import { onMounted, ref } from 'vue';
...
function createEngine(canvas: HTMLCanvasElement) { ... }
function createScene(engine: Engine) { ... }
function createCamera(scene: Scene) { ... }
function init() { ... }
onMounted(() => {
init();
});
</script>
熟悉 Vue 的讀者們一定都知道會在 onMounted 中才初始化,是因為這樣才取得到 canvas DOM。
接著是當組件銷毀前,需要關閉引擎。
onBeforeUnmount(() => {
engine.dispose();
});
再加入考慮畫面隨視窗自動縮放的功能。
onMounted(() => {
init();
window.addEventListener('resize', handleResize);
});
onBeforeUnmount(() => {
engine.dispose();
window.removeEventListener('resize', handleResize);
});
function handleResize() {
engine.resize();
}
最後暫時取消 game-console 自動跳轉回首頁的限制,以免每次需要重新整理網頁的時候一直跳回首頁。
src\views\game-console.vue
...
<script setup lang="ts">
...
function init() {
// 房間 ID 不存在,跳回首頁
// if (!gameConsoleStore.roomId) {
// router.push({
// name: RouteName.HOME
// });
// loading.hide();
// return;
// }
...
// 跳轉至遊戲大通
// router.push({
// name: RouteName.GAME_CONSOLE_LOBBY
// });
}
init();
</script>
現在讓我們按下「開始遊戲」產生 3D 場景吧!
鱈魚:「出現了!ᕕ( ゚ ∀。)ᕗ」
助教:「出個毛線!這坨深藍色我用 CSS 都能產生!⎝(° Д °⎝)」
鱈魚:「那是背景預設的顏色啦,讓我們慢慢來嘛。(´,,•ω•,,)」
組成一個完整 3D 物件最基本構成是 Mesh 與 Material,前者負責組成「骨架」,後者負責「皮膚」。
現在讓我們建立一片海洋(其實就是一片地板),過程如下:
建立海洋要用的網格
這裡使用 MeshBuilder.CreateGround,可以產生一個不具厚度的網格,因為地板不需要厚度。
建立材質
將顏色設定為藍色後附加於網格上。
以上步驟便可以完成海洋地板了,現在建立新增海洋的 function。
...
<script setup lang="ts">
import { ArcRotateCamera, BackgroundMaterial, Color3, Engine, MeshBuilder, Scene, Vector3 } from '@babylonjs/core';
...
function createEngine(canvas: HTMLCanvasElement) { ... }
function createScene(engine: Engine) { ... }
function createCamera(scene: Scene) { ... }
function createSea(scene: Scene) {
const sea = MeshBuilder.CreateGround('sea', { height: 1000, width: 1000 });
const material = new BackgroundMaterial("seaMaterial", scene);
material.useRGBColor = false;
material.primaryColor = new Color3(0.57, 0.70, 0.83);
sea.material = material;
return sea;
}
function init() {
...
engine = createEngine(canvas.value);
scene = createScene(engine);
createCamera(scene);
createSea(scene);
...
}
...
</script>
鱈魚:「現在是一片淺藍色了!◝( •ω• )◟」
助教:「所以我說那個 3D 呢?(○´・д・)ノ」
鱈魚:「那就讓我們建立浮冰吧!( •̀ ω •́ )✧」
...
<script setup lang="ts">
import {
ArcRotateCamera, BackgroundMaterial, Color3,
Engine, MeshBuilder, Scene,
StandardMaterial, Vector3
} from '@babylonjs/core';
...
function createSea(scene: Scene) { ... }
function createIce(scene: Scene) {
const ice = MeshBuilder.CreateBox('ice', {
width: 30,
depth: 30,
height: 4,
});
ice.material = new StandardMaterial('iceMaterial', scene);
return ice;
}
function init() {
...
createSea(scene);
createIce(scene);
...
}
...
</script>
冰塊出現了!
鱈魚:「是不是和吃冰一樣簡單啊!♪( ◜ω◝و(و」
助教:「所以我說企鵝哩?(。_。)」
鱈魚:「…( ◜ω◝و(و」
以上程式碼已同步至 GitLab,大家可以前往下載: