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;
}
发布了402 篇原创文章 · 获赞 544 · 访问量 212万+

猜你喜欢

转载自blog.csdn.net/qq_30100043/article/details/89115252
今日推荐