添加點擊歸位事件,也就是鼠標點到導(dǎo)航盒的某一個面,讓攝像機運動到對準那個面的軸上,這涉及到以下幾個問題:
1.如何獲取點擊事件?
2.如何獲取點到的面?
3.如何獲取點到面的法線?
4.如何將攝像機移動到法線對應(yīng)的軸?我們依次解決這幾個問題,首先我們使用scene.onPointerDown來獲取檢測,因為副場景的scene是獨立的,可以直接使用這個API去獲取點擊事件,不會和主場景的檢測沖突,因為這個功能是加在副場景的scene上,我們并不需要在dispose中對這個功能進行注銷處理。
//注冊鼠標點擊事件
registPointDownEvent() {
this.scene.onPointerDown = (evt, pi) => {
console.log(1);
if (!pi.pickedMesh) return;
const pickMesh = pi.pickedMesh;
if (!pickMesh) return;
console.log(pi);
}
}
//https://playground.babylonjs.com/?#ENABP9#11
通過打印pickInfo,我們發(fā)現(xiàn)我們可以獲取到碰撞點的faceId,根據(jù)faceId,可以找到該面的三個頂點的索引號,因為創(chuàng)建網(wǎng)格的時候,拋開八個角面,同一個平面上面的點相對于其他面都是獨立存在的,點的法線就代表著所處平面的法線,這樣第二三個問題迎刃而解。
//獲取頂點索引
......
const indices = pickMesh.getIndices();
const p1Index = indices[pi.faceId * 3];
// const p2Index = indices[pi.faceId * 3+1];
// const p3Index = indices[pi.faceId * 3+2];
//獲取normal
const normals = pickMesh.getNormalsData();
const normal = new BABYLON.Vector3(normals[p1Index * 3], normals[p1Index * 3 + 1], normals[p1Index * 3 + 2]);
console.log(normal);
//https://playground.babylonjs.com/?#ENABP9#12
此時我們已經(jīng)獲取了法線,接下來我們通過法線去判定六個方向,就可以知道我們即將旋轉(zhuǎn)到的軸的方向,這里可以準備一個新的enum和function:
enum IDirection {
forward,
backward,
right,
left,
up,
down
}
......
//獲取normal
const normals = pickMesh.getNormalsData();
const normal = new BABYLON.Vector3(normals[p1Index * 3], normals[p1Index * 3 + 1], normals[p1Index * 3 + 2]);
if (normal.z === -1) {
this.moveCameraToDirection(CameraDirection.forward)
}
if (normal.z === 1) {
this.moveCameraToDirection(CameraDirection.backward)
}
if (normal.x === -1) {
this.moveCameraToDirection(CameraDirection.left);
}
if (normal.x === 1) {
this.moveCameraToDirection(CameraDirection.right);
}
if (normal.y === 1) {
this.moveCameraToDirection(CameraDirection.up);
}
if (normal.y === -1) {
this.moveCameraToDirection(CameraDirection.down);
}
......
//指向
moveCameraToDirection(direction: CameraDirection) {
}
......
//https://playground.babylonjs.com/?#ENABP9#13
前面的幾個問題都已經(jīng)解決,最后一個問題就是怎么移動?我們先寫一個簡單的動畫機ProcessAnimation,當(dāng)然也可以直接使用gsap之類的插件。對于ArcRotateCamera,最簡單的移動的方法有兩種,一種是通過插值Position,一種是通過插值A(chǔ)lpha和Beta。第一種方法簡單粗暴,第二種方法得去匹配對應(yīng)的alpha和beta,我們可以分開嘗試。先試試通過插值Position:
moveCameraToDirection(direction: CameraDirection) {
const tmpV3 = B.TmpVectors.Vector3[0];
const startPos = B.TmpVectors.Vector3[1].copyFrom(this.bindCamera.position);
switch (direction) {
case CameraDirection.forward:
tmpV3.set(0, 0, -1);
break;
case CameraDirection.backward:
tmpV3.set(0, 0, 1);
break;
case CameraDirection.right:
tmpV3.set(1, 0, 0);
break;
case CameraDirection.left:
tmpV3.set(-1, 0, 0);
break;
case CameraDirection.up:
tmpV3.set(0, 1, 0);
break;
case CameraDirection.down:
tmpV3.set(0, -1, 0);
break;
}
//最終的Position
tmpV3.scaleInPlace(this.bindCamera.radius).addInPlace(this.bindCamera.target);
this.process.play(0.5, (process) => {
B.Vector3.LerpToRef(startPos, tmpV3, process, this.bindCamera.position);
this.bindCamera.rebuildAnglesAndRadius();
})
}
//https://playground.babylonjs.com/?#ENABP9#14
編寫的過程簡單粗暴,效果好些也挺OK,但是多次嘗試會發(fā)現(xiàn),點擊頂和底的移動好像有點問題,結(jié)束的時候方向總是不對或者會瞬間旋轉(zhuǎn)。這是因為遇到類似萬向死鎖的問題,當(dāng)Beta不處于0和PI時,一對Beta和Alpha有唯一對應(yīng)的一對Position和Target,但是當(dāng)Beta處于0或PI時,相當(dāng)于一個軸旋轉(zhuǎn)了90度,再旋轉(zhuǎn)Alpha時,會發(fā)現(xiàn)position并不會改變,也就是說此時一對Position和Target對應(yīng)著無數(shù)個Alpha,計算的alpha可能是0-2Pi中的任意一個值。接下來試試使用Alpha和Beta插值的方法來進行移動:
moveCameraToDirection(direction: CameraDirection) {
let alpha = 0;
let beta = 0;
switch (direction) {
case CameraDirection.forward:
alpha = - Math.PI / 2;
beta = Math.PI / 2;
break;
case CameraDirection.backward:
alpha = Math.PI / 2;
beta = Math.PI / 2;
break;
case CameraDirection.right:
alpha = 0;
beta = Math.PI / 2;
break;
case CameraDirection.left:
alpha = - Math.PI;
beta = Math.PI / 2;
break;
case CameraDirection.up:
alpha = - Math.PI / 2;
beta = 0;
break;
case CameraDirection.down:
alpha = - Math.PI / 2;
beta = Math.PI;
break;
}
let startBeta = this.bindCamera.beta;
let startAlpha = this.bindCamera.alpha % (Math.PI * 2);
//避免繞大圓
if (startAlpha < 0) startAlpha += Math.PI * 2;
if (Math.abs(alpha - startAlpha) > Math.PI) {
alpha += Math.PI * 2;
}
this.process.play(0.5, (process) => {
this.bindCamera.alpha = BABYLON.Scalar.Lerp(startAlpha, alpha, process);
this.bindCamera.beta = BABYLON.Scalar.Lerp(startBeta, beta, process);
})
}
//https://playground.babylonjs.com/?#ENABP9#15
除了這兩種方法,還可以使用四元數(shù)插值直接計算矩陣來移動視角,有興趣的小伙伴可以去研究一下。到此為止,導(dǎo)航盒的基礎(chǔ)功能已經(jīng)完成,下一節(jié)將為盒子添加一個點擊后高亮的功能。
PS:本節(jié)結(jié)束PG(https://playground.babylonjs.com/?#ENABP9#16)
PS:后續(xù)修改了部分結(jié)構(gòu)以支持八角定位(https://playground.babylonjs.com/?#ENABP9#24)
下一節(jié):BabylonJS系列:方向?qū)Ш胶?4.點擊面高亮
