昨天了解了整個專案的架構,首先就從基本的人物模型開始動手吧!
Photo by Jeremy Thomas on Unsplash
這是本系列第 05 篇,如果還沒看過第 04 篇可以點以下連結前往:
用 Three.js 來當個創世神 (04):專案實作#1 - 專案介紹及初步規劃
今天要來完成 Creeper(苦力怕)模型:
程式碼部分跟前天的概念差不多,直觀地從成果圖來看,可以發現就是用六個不同大小及位置的長方體就可以組成一隻苦力怕,非常的簡單,就像是小時候在玩樂高積木一樣,以下我們針對其中比較不一樣的細節做解說。
class Creeper {
constructor() {
// 宣告頭、身體、腳幾何體大小
const headGeo = new THREE.BoxGeometry(4, 4, 4)
const bodyGeo = new THREE.BoxGeometry(4, 8, 2)
const footGeo = new THREE.BoxGeometry(2, 3, 2)
// 馮氏材質設為綠色
const creeperMat = new THREE.MeshPhongMaterial({ color: 0x00ff00 })
// 頭
this.head = new THREE.Mesh(headGeo, creeperMat)
this.head.position.set(0, 6, 0)
// 身體
this.body = new THREE.Mesh(bodyGeo, creeperMat)
this.body.position.set(0, 0, 0)
// 四隻腳
this.foot1 = new THREE.Mesh(footGeo, creeperMat)
this.foot1.position.set(-1, -5.5, 2)
this.foot2 = this.foot1.clone() // 剩下三隻腳都複製第一隻的 Mesh
this.foot2.position.set(-1, -5.5, -2)
this.foot3 = this.foot1.clone()
this.foot3.position.set(1, -5.5, 2)
this.foot4 = this.foot1.clone()
this.foot4.position.set(1, -5.5, -2)
// 將四隻腳組合為一個 group
this.feet = new THREE.Group()
this.feet.add(this.foot1)
this.feet.add(this.foot2)
this.feet.add(this.foot3)
this.feet.add(this.foot4)
// 將頭、身體、腳組合為一個 group
this.creeper = new THREE.Group()
this.creeper.add(this.head)
this.creeper.add(this.body)
this.creeper.add(this.feet)
}
}
從完成圖來看可以發現,一隻苦力怕包含了一個大頭、一個長身體、四隻短腳,而這邊試著將整隻苦力怕的組件寫成一個 class
物件,在其中將四隻腳與整個身體用 THREE.Group()
層層組合起來,這樣在後續針對每一個物件下的屬性做操作會更方便。
材質的部分這裡暫時先使用基本的馮氏材質,並套上最基本的綠色做為皮膚。
// 生成苦力怕並加到場景
function createCreeper() {
const creeperObj = new Creeper()
scene.add(creeperObj.creeper)
}
function init(){
...
// 產生苦力怕
createCreeper()
...
}
這邊承接上述的苦力怕物件,做成一個只要一在 init()
中呼叫就能產生一隻苦力怕的 function,這樣之後若要產生很多隻苦力怕就很方便了!
// 簡單的地板
const planeGeometry = new THREE.PlaneGeometry(60, 60)
const planeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff })
let plane = new THREE.Mesh(planeGeometry, planeMaterial)
plane.rotation.x = -0.5 * Math.PI // 使平面與 y 軸垂直,並讓正面朝上
plane.position.set(0, -7, 0)
scene.add(plane)
這邊加入一個簡單的平面物體當作地板,目前就只是一個看起來可以立足的地方,但實際上的用途在之後的光影效果會看到。
這邊其中有一行 plane.rotation.x = -0.5 * Math.PI
,是指將平面「沿著 x 軸正方向逆時針轉 90 度」。
原因是 PlaneGeometry
預設是在 z = 0
的 x-y 平面
上,又在材質屬性中的 side
預設是 THREE.FrontSide
時只有平面體的前側會反射光線,也就是朝向 z 軸正向的方向,因此為了達到預期的讓此平面呈現為 y = 0
的 x-z 平面
且可以反射光線,需要將平面體「沿著 x 軸正方向逆時針旋轉 90 度」。
let axes = new THREE.AxesHelper(20) // 參數為座標軸長度
scene.add(axes)
最後提一下,一個簡單好用的輔助小幫手,就是在之前有提過的 THREE.AxesHelper
,有了這個就不怕分不清楚每一項元素的三軸座標該怎麼設。
今天先完成了一個苦力怕的模型,相信大家好像也開始覺得自己又往遊戲工程師的路上前進了一些(握拳),程式碼跟前天的概念沒差太多,場景、渲染器、燈光等等都一樣,所以只做了新東西的分析,若有任何不懂的地方歡迎在下面留言區發文。
目前只先完成了形狀的部分,明天會先來幫這個場景加上一些小工具,方便後面修改材質及光影效果,我們明天見!
[幫助補充]plane.rotation.x = -0.5 * Math.PI
是沿著X軸順時針轉90度。
PlaneGeometry默認在Z=0的XY平面上,沿著X軸順時針轉90度,便把它改為在Y=0的XZ平面上。
PlaneGeometry有正、反2面,正面會反射光線,反面就不會。在默認的狀態下,向著Z正數方向的那面是正面。
感謝 marlin12 大大糾正與補充,學到了 PlaneGeometry
預設的狀態,不過想跟您確認一下,是「順」時針轉嗎?
我的理解是沿著 x 軸正方向旋轉,所以也才會是 -0.5
。
我弄錯[負數值]是[順時針旋轉],原因是我從[旋轉軸]的正面(正數至負數的方向)看進去。如果是從[旋轉軸]的背面(負數至正數的方向)看進去,那麽[負數值]便是[逆時針旋轉]。
這裏也很感謝Dez大大的糾正。