6. 自定义相机输入

本教程翻译自官方教程Customize Camera Inputs,各位也可以进入本站镜像站点查看

本文目录

  1. 配置您的输入
  2. 添加现有输入
  3. 启用或禁用输入
  4. 删除输入
  5. 实施自己的输入
    1. 使用Javascript
    2. 使用Typescript
    3. 如何在场景中漫步并环顾相机

如何自定义相机输入

    一旦调用相机的attachControl函数,每个Babylon.js相机都会自动为您处理输入。您可以使用detachControl函数来撤销该控件。大多数Babylon.js专家使用两步过程来激活和附着相机

// 首先, 设置场景的 activeCamera 成为你的相机
scene.activeCamera = myCamera;
// 然后附着 activeCamera 到 canvas 上
// 参数: canvas, noPreventDefault
scene.activeCamera.attachControl(canvas, true);

    一个更简单的版本可能如下所示:

myCamera.attachControl(canvas);

    noPreventDefault默认值为false,这意味着在所有画布上鼠标的单击和触摸事件会自动调用 preventDefault()。

    Babylon.js v2.4引入了另一种方法来管理摄像机输入,从而提供了一种针对组合性输入的方法。现在,您可以使用输入管理器,并且每个输入都可以看作是特定于此相机系列以及给定输入类型(鼠标,键盘,游戏手柄,设备方向等)的插件。

    使用输入管理器,您可以添加,删除,启用或禁用任何对于相机可用的输入。您也可以轻松实现自己的输入机制或覆盖现有的输入机制。

    输入管理器可通过称为输入的属性来使用,例如

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;

添加现有输入

    ArcRotateCameraFreeCamera的输入管理器都提供了用于添加内置输入的简写功能。

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时,您正在激活连接到输入管理器的所有输入。同样,您可以通过调用相机上的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();

    您也可以从输入管理器中删除单个输入。您可以按实例或按类型名称删除它们

var camera = new BABYLON.FreeCamera(
  "sceneCamera",
  new BABYLON.Vector3(0, 1, -15),
  scene
);
//remove by instance
camera.inputs.remove(camera.inputs.attached.mouse);
//remove by type
camera.inputs.removeByType("FreeCameraKeyboardMoveInput");

实施自己的输入

    您的输入函数作为一个功能对象被创建。您必须为使用输入函数对象调用的几种具有所需名称的方法编写代码。方法名称和目的是

// 此函数必须返回摄影机的类型名称,它可以用于序列化场景
getTypeName();

// 此函数必须返回一个简单名称, 这个名称将作为简写形式注入输入管理器中
// 例如 "mouse" 会变成  camera.inputs.attached.mouse
getSimpleName();

// 此函数必须激活您的输入事件。即使您的输入不需要DOM元素
// 参数 element 和 noPreventDefault 必须存在
// Return void.
attachControl(element, noPreventDefault);

// 分离控件必须停用输入并释放所有指针、闭包或事件侦听器
// 参数 element 必须存在
// Return void.
detachControl(element);

// 如果要输入同步渲染,将为每个渲染帧调用此可选函数,
// 不需要使用 requestAnimationFrame。如果有必要的话,这是一个应用计算的好地方。
// Return void.
checkInputs();

使用Javascript

    这会改变按键的正常使用方式, 从左右前后移动摄像机旋转到当前位置。

    首先, 删除输入的默认键

camera.inputs.removeByType("FreeCameraKeyboardMoveInput");

     现在,创建一个新的输入函数FreeCameraKeyboardRotateInput

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

添加获取名称方法

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

以及附着和分离方法

FreeCameraKeyboardRotateInput.prototype.attachControl = function(
  noPreventDefault
) {
  var _this = this;
  var engine = this.camera.getEngine();
  var element = engine.getInputElement();
  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(canvas, [
      { name: "blur", handler: this._onLostFocus }
    ]);
  }
};

FreeCameraKeyboardRotateInput.prototype.detachControl = function() {
  var engine = this.camera.getEngine();
  var element = engine.getInputElement();
  if (this._onKeyDown) {
    element.removeEventListener("keydown", this._onKeyDown);
    element.removeEventListener("keyup", this._onKeyUp);
    BABYLON.Tools.UnregisterTopRootEvents(canvas, [
      { 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;
      }
    }
  }
};

最后,将此新输入法添加到摄像机输入中

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

演示广场 - 旋转自由相机

使用Typescript

使用TypeScript,您可以实现ICameraInput接口。

interface ICameraInput<TCamera extends BABYLON.Camera> {
    // 输入管理器将填充父摄影机
    camera: TCamera;

    //此函数必须返回输入类的类名,它可以用于序列化场景
    getClassName(): string;

    // 此函数必须返回将作为简写形式注入输入管理器中的简单名称
    //例如 "mouse" 将转变为 camera.inputs.attached.mouse
    getSimpleName(): string;

    //此函数必须激活您的输入事件。即使您的输入不需要DOM元素
    attachControl: (element: HTMLElement, noPreventDefault?: boolean) => void;

    // 分离控件必须停用输入并释放所有指针、闭包或事件侦听器
    detachControl: (element: HTMLElement) => void;

    //如果要将输入同步到渲染,将为每个渲染帧调用此可选函数,
    //无需使用requestAnimationFrame。如果你必须要计算,那是一个很好的应用计算的地方
    checkInputs?: () => void;
}

如何在场景中漫步并环顾相机

    以下示例自定义了通用摄像机的键盘和鼠标输入。进行此更改后,您可以使用箭头键在场景中前后移动并旋转以向左和向右看。使用鼠标,您可以环顾四周,上下左右。

    在此示例中,有两个视口,上方的视口在您移动和环顾四周时提供第一人称视角。下图显示了相机及其周围的碰撞体积。

    请记住在使用箭头键之前单击场景。

演示示例-漫步并且旋转相机视角

贡献者

翻译: Alshat