自定義控件
回到PerspectiveCamera透視相機上來。注釋OrthographicCamera正交相機,取消注釋PerspectiveCamera透視相機,移動相機,使其面向立方體,刪掉tick中的對網(wǎng)格體旋轉(zhuǎn)的部分。
// 相機
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 1, 1000)
// const aspectRatio = sizes.width / sizes.height
// const camera = new THREE.OrthographicCamera(- 1 * aspectRatio, 1 * aspectRatio, 1, - 1, 0.1, 100)
// camera.position.x = 2
// camera.position.y = 2
camera.position.z = 3
camera.lookAt(mesh.position)
scene.add(camera)
以用鼠標控制相機為例,首先需要知道鼠標坐標??梢允褂迷鶭avaScript的偵聽事件addEventListener,來偵聽mousemove事件。
坐標將通過回調(diào)函數(shù)返回,比如event.clientX和event.clientY
// 鼠標
window.addEventListener('mousemove', (event) =>
{
? ? console.log(event.clientX, event.clientY)
})
此處的返回值可以直接使用,但是調(diào)整下值的范圍會更好,比如調(diào)整為區(qū)間為1的值。
比如,對于x:
? ? 當光標位于畫布左側(cè),輸出-0.5
? ? 當光標位于畫布中間,輸出0
? ? 當光標位于畫布右側(cè),輸出0.5
當然也可以采用其它值。
仿照size變量,這里創(chuàng)建個cursor變量,變量默認屬性包含x和y,當觸發(fā)mousemove回調(diào)時,更新屬性的值
// 鼠標
const cursor = {
? ? x: 0,
? ? y: 0
}
window.addEventListener('mousemove', (event) =>
{
? ? cursor.x = event.clientX / sizes.width - 0.5
? ? cursor.y = event.clientY / sizes.height - 0.5
? ? console.log(cursor.x, cursor.y)
})
event.clientX除以sizes.width會返回一個介于0~1之間的值,通過減去一個0.5,讓值分布在0.5~-0.5
這樣,就完成了鼠標位置的存儲,并且可以通過tick更新位置。
const tick = () =>
{
? ? // ...
? ? // 更新相機
? ? camera.position.x = cursor.x
? ? camera.position.y = cursor.y
? ? // ...
}
運行后,會發(fā)現(xiàn)沿y軸運動方向不對,這是因為Three.js中positon.y向上運動時為正,在網(wǎng)頁中clientY向下運動時為正。
通過在cursor.y上添加負號,來糾正。
window.addEventListener('mousemove', (event) =>
{
? ? cursor.x = event.clientX / sizes.width - 0.5
? ? cursor.y = - (event.clientY / sizes.height - 0.5)
})
可以通過cursor.x和cursor.y乘上一個數(shù)字,來調(diào)整幅度大小。此時仍可通過lookAt()方法調(diào)整相機朝向。
const tick = () =>
{
? ? // ...
? ? // 更新相機
? ? camera.position.x = cursor.x * 5
? ? camera.position.y = cursor.y * 5
? ? camera.lookAt(mesh.position)
? ? // ...
}
更進一步,還可以通過使用Math.sin()和Math.cos()實現(xiàn)相機繞網(wǎng)格體旋轉(zhuǎn)。
同時使用cos和sin時,可以讓運動變成圓周運動。要運動成完成的圓,角度的大小必須是Π的2倍??梢酝ㄟ^使用Math.PI,使用Π的值。
要增加圓的半徑,可以簡單的將Math.sin()和Math.cos()結(jié)果乘上一個數(shù)。
const tick = () =>
{
? ? // ...
? ? // 更新相機
? ? camera.position.x = Math.sin(cursor.x * Math.PI * 2) * 2
? ? camera.position.z = Math.cos(cursor.x * Math.PI * 2) * 2
? ? camera.position.y = cursor.y * 3
? ? camera.lookAt(mesh.position)
? ? // ...
}
tick()
這只是一個自定義控制的例子,Three.js內(nèi)置了許多控件類,用來進行許多的相似操作。
內(nèi)建控件
如果在Three.js文檔中搜索控件,會看到很多現(xiàn)成的控件。
DeviceOrientationControls設備陀螺儀控件
DeviceOrientationControls設備陀螺儀控件,就是根據(jù)權(quán)限許可,依照設備方向相應的旋轉(zhuǎn)相機。如果設備合適,可以用來創(chuàng)建全景圖或者VR效果。
FlyControls飛行控件
FlyControls飛行控件允許像飛船一樣控制相機,此時可在三個軸上任意旋轉(zhuǎn)、并可前后移動。
FirstPersonControls第一人稱控件
FirstPersonControls第一人稱控件類似上一個,但具有一個固定向上的軸,即不能向前后左右翻轉(zhuǎn),雖然名字里有第一人稱,但是相機本身不含人。
PointerLockControls指針鎖定控件
PointerLockControls指針鎖定控件使用了JavaScript的pointer lock API。通過這個,隱藏了鼠標指針,并使其居中,但仍在事件回調(diào)中繼續(xù)發(fā)送移動。使用這個API,可以創(chuàng)建FPS游戲,但是,這個API只提供了相機的旋轉(zhuǎn),相機的移動和游戲中的物理效果仍舊需要自行處理。
OrbitControls環(huán)繞控件
OrbitControls環(huán)繞控件類似于上面的自定義控件的例子??梢酝ㄟ^左鍵繞某個點旋轉(zhuǎn),使用鼠標右鍵橫向平移,使用滾輪放大縮小。
TrackballControls軌跡球控件
TrackballControls軌跡球控件類似OrbitControls環(huán)繞控件,但是可以在豎直方向上翻轉(zhuǎn)。
TransformControls變換控件
TransformControls變換控件和相機無關(guān)??梢杂盟o對象添加一個控件,用來移動對象。
這里只講OrbitControls環(huán)繞控件
OrbitControls環(huán)繞控件
注釋掉tick中更新相機的部分。
實例化
首先,使用OrbitControls類實例化變量。
由于使用了Webpack,將會以以下形式引入
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
之后就可以直接實例化OrbitControls 類,但要在創(chuàng)建相機后執(zhí)行。
要使其正常工作,需要向其傳入相機和畫布,然后實例會自己處理鼠標事件。
// 控件
const controls = new OrbitControls(camera, canvas)
這樣就可以使用鼠標左鍵或者右鍵來移動相機,使用滾輪進行縮放。
比自定義控件方便得多,且?guī)в性S多控件。然后再往更深層次研究。
target目標
默認情況下,相機看向場景中心??梢酝ㄟ^target目標屬性修改它。
這個屬性是個Vector3,意思就是通過x、y、z三個屬性定義的。
如果希望OrbitControls環(huán)繞控件默認查看立方體上方,只需要增加y值。
controls.target.y = 2
這個用處不是很大,先注釋掉它。
Damping阻尼
Damping阻尼通過加速度和摩擦力公式來平滑動畫。
要啟用阻尼,請將controls的enableDamping屬性設置為true。
為了正常運行,還需要通過在每幀調(diào)用controls.update()。可以通過tick()實現(xiàn)。
// 控件
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
// ...
const tick = () =>
{
? ? // ...
? ? // Update controls
? ? controls.update()
? ? // ...
}
這樣控件看起來更加流暢。
阻尼可以用在各種旋轉(zhuǎn)、移動、縮放、角度控制上,甚至按鍵綁定上。
何時使用內(nèi)建控件
雖然這些控件很方便,但是都有局限性。如果過于依賴它們,可能需要以意想不到的方式改變類的工作方式。
首先請確保所需的功能,然后檢查內(nèi)建控件的功能是否完全覆蓋所需的。
否則,需要自己建立控件。