BabylonJS系列:方向?qū)Ш胶?3.添加點擊歸位事件

添加點擊歸位事件,也就是鼠標點到導(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.點擊面高亮

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容