21 Babylonjs入門進階 自定義相機輸入事件

相機默認會在我們我們將其綁定(attachControl)到畫布上面時,給我們自動處理操作輸入事件。你還可以使用detachControl函數來解除事件的綁定。大多數Babylon.js的專家都使用兩步:

//第一步,設置相機的activeCamera爲你創建的相機
scene.activeCamera = myCamera;
//第二步,將相機綁定到畫布
//配置項:畫布對象canvas,不阻止默認事件noPreventDefault
scene.activeCamera.attachControl(canvas, true);

還有更簡單的版本:

myCamera.attachControl(canvas);

默認情況下,noPreventDefault值爲false。這意味着綁定到畫布的所有的相機操作都將自動阻止默認事件。
在Babylon.js v2.4版本引入了一種不同的方式來管理相機的操作,提供了一種面向輸入可組合性的方法。你可以使用輸入管理器,並且每個輸入都可以被視爲此攝像機系列的插件,以及給定的輸入類型(鼠標,鍵盤,遊戲手柄,陀螺儀等)。
使用輸入管理器( input manager),你可以添加,刪除,啓用或者禁用相機可用的任何輸入。你可以非常輕鬆的實現自己的輸入機制或覆蓋現有的輸入機制。
例如,輸入管理器( input manager)可以用過相機的inputs屬性來獲取:

var camera = new BABYLON.FreeCamera("sceneCamera", new BABYLON.Vector3(0, 1, -15), scene);
var inputManager = camera.inputs;

配置你的輸入

大多數輸入提供設置自定義敏感度使相機適應你的場景。
每個輸入在管理器上提供了一個簡短的名稱。目的是爲了方便設置。

var camera = new BABYLON.FreeCamera("sceneCamera", new BABYLON.Vector3(0, 1, -15), scene);
camera.inputs.add(new BABYLON.FreeCameraGamepadInput());
camera.inputs.attached.gamepad.gamepadAngularSensibility = 250;

添加現有的輸入

ArcRotateCamera和FreeCamera的輸入管理器都公開了用於添加內置輸入的簡寫函數。

var camera = new BABYLON.FreeCamera("sceneCamera", new BABYLON.Vector3(0, 1, -15), scene);
camera.inputs.addGamepad();

等同於

var camera = new BABYLON.FreeCamera("sceneCamera", new BABYLON.Vector3(0, 1, -15), scene);
camera.inputs.add(new BABYLON.FreeCameraGamepadInput());

如果需要,你還可以自定義自己的輸入方式(本節末尾處將實現自定義輸入)。

啓用或禁用輸入

當你調用相機的attachControl時,會自動激活input manager的所有的輸入。同樣的,調用相機的detachControl時,將關閉所有的輸入。
如果想要暫時禁用輸入,可以直接在輸入上調用detachControl來禁止某個操作輸入:

var camera = new BABYLON.FreeCamera("sceneCamera", new BABYLON.Vector3(0, 1, -15), scene);
camera.inputs.attached.mouse.detachControl();
camera.inputs.addGamepad();

如果你想再打開它,可以調用attachInput

camera.inputs.attachInput(camera.inputs.attached.mouse);

刪除輸入

有時,你需要一個非常具體的輸入機制。在這種情況下,最好的辦法可能是清除當前所有的默認輸入並添加需要的輸入。

var camera = new BABYLON.FreeCamera("sceneCamera", new BABYLON.Vector3(0, 1, -15), scene);
camera.inputs.clear();
camera.inputs.addMouse();

你還可以從input Manager裏面刪除掉某個輸入。你可以按實例或者類型名稱刪除:

var camera = new BABYLON.FreeCamera("sceneCamera", new BABYLON.Vector3(0, 1, -15), scene);
// 按照實例刪除輸入
camera.inputs.remove(camera.inputs.attached.mouse);
// 按照類型名稱刪除輸入
camera.inputs.removeByType("FreeCameraKeyboardMoveInput");

實現自己的輸入

我們實現的自己的輸入必須是一個類(構造函數)。而且還必須爲類添加多個所需的方法,這些方法將會由輸入函數對象調用:

//這個方法返回相機的類型名稱,它可用於序列化場景
getTypeName();

//這個函數返回添加到輸入管理器中的輸入的簡稱
//比如camera.inputs.attached.mouse將返回 "mouse" 
getSimpleName();

//這個函數用於綁定設備事件,即使你的輸入不需要DOM元素。
// element 和noPreventDefault 爲必填項
//返回空
attachControl(element, noPreventDefault);

//解綁控制方法必須能夠停止輸入事件並解綁所有的指針,閉包和事件監聽。
//element 爲必填項
// 返回空
detachControl(element);        

//如果你需要輸入與渲染同步,這個函數將在每一個渲染幀中調用。
//無須使用requestAnimationFrame方法. 如果需要這是一個需要計算的好地方。
// 返回空
checkInputs();

使用JavaScript實現

我們將改變FreeCamera的鍵盤操作,將移動操作改爲旋轉操作。
首先刪除掉默認的鍵盤輸入:

camera.inputs.removeByType("FreeCameraKeyboardMoveInput");

創建新的構造函數FreeCameraKeyboardRotateInput

var FreeCameraKeyboardRotateInput = function () {
    this._keys = [];
    this.keysLeft = [37];
    this.keysRight = [39];
    this.sensibility = 0.01;
}

添加獲取名稱方法

FreeCameraKeyboardRotateInput.prototype.getTypeName = function () {
    return "FreeCameraKeyboardRotateInput";
};
FreeCameraKeyboardRotateInput.prototype.getSimpleName = function () {
    return "keyboardRotate";
};

然後再添加綁定事件和解綁事件的方法:

FreeCameraKeyboardRotateInput.prototype.attachControl = function (element, noPreventDefault) {
    var _this = this;
    if (!this._onKeyDown) {
        element.tabIndex = 1;
        this._onKeyDown = function (evt) {
            if (_this.keysLeft.indexOf(evt.keyCode) !== -1 ||
                _this.keysRight.indexOf(evt.keyCode) !== -1) {
                var index = _this._keys.indexOf(evt.keyCode);
                if (index === -1) {
                    _this._keys.push(evt.keyCode);
                }
                if (!noPreventDefault) {
                    evt.preventDefault();
                }
            }
        };
        this._onKeyUp = function (evt) {
            if (_this.keysLeft.indexOf(evt.keyCode) !== -1 ||
                 _this.keysRight.indexOf(evt.keyCode) !== -1) {
                var index = _this._keys.indexOf(evt.keyCode);
                if (index >= 0) {
                    _this._keys.splice(index, 1);
                }
                if (!noPreventDefault) {
                    evt.preventDefault();
                }
            }
        };

        element.addEventListener("keydown", this._onKeyDown, false);
        element.addEventListener("keyup", this._onKeyUp, false);
        BABYLON.Tools.RegisterTopRootEvents([
            { name: "blur", handler: this._onLostFocus }
        ]);
    }
};


FreeCameraKeyboardRotateInput.prototype.detachControl = function (element) {
    if (this._onKeyDown) {
        element.removeEventListener("keydown", this._onKeyDown);
        element.removeEventListener("keyup", this._onKeyUp);
        BABYLON.Tools.UnregisterTopRootEvents([
            { name: "blur", handler: this._onLostFocus }
        ]);
        this._keys = [];
        this._onKeyDown = null;
        this._onKeyUp = null;
    }
};

添加每一幀的處理檢測:

FreeCameraKeyboardRotateInput.prototype.checkInputs = function () {
    if (this._onKeyDown) {
        var camera = this.camera;
        // Keyboard
        for (var index = 0; index < this._keys.length; index++) {
            var keyCode = this._keys[index];
            if (this.keysLeft.indexOf(keyCode) !== -1) {
                camera.cameraRotation.y += this.sensibility;
            }
            else if (this.keysRight.indexOf(keyCode) !== -1) {
                camera.cameraRotation.y -= this.sensibility;
            }
        }
    }
};

最後將新的輸入方法添加到相機的input Manager

camera.inputs.add(new FreeCameraKeyboardRotateInput());

使用TypeScript

使用TypeScript,您可以實現接口ICameraInput。

interface ICameraInput<TCamera extends BABYLON.Camera> {       
    // 填充輸入管理的父相機
    camera: TCamera;        

    //這個方法返回相機的類型名稱,它可用於序列化場景
    getTypeName(): string;

  	//這個函數返回添加到輸入管理器中的輸入的簡稱
	//比如camera.inputs.attached.mouse將返回 "mouse" 
    getSimpleName(): string;

   //這個函數用於綁定設備事件,即使你的輸入不需要DOM元素。
    attachControl: (element: HTMLElement, noPreventDefault?: boolean) => void;

    //解綁控制方法必須能夠停止輸入事件並解綁所有的指針,閉包和事件監聽。
    detachControl: (element: HTMLElement) => void;        

    //如果你需要輸入與渲染同步,這個函數將在每一個渲染幀中調用。
	//無須使用requestAnimationFrame方法. 如果需要這是一個需要計算的好地方。
    checkInputs?: () => void;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章