Skip to content

第一人称射击

loading

游戏开发对地图框架的性能是一种考验,也是最好的测试。游戏具有极高的实时性,任何卡顿在游戏将会暴露无遗。

  • three-tile 的地图支持全球海量地形,所以它不能像常规游戏那样在启动时进行数据预加载和预处理,所有地图数据都是实时加载的。在鼠标的移动中,地图视角会以极快速度切换,大量地图瓦片实时下载、解析、渲染,十分考验框架性能,如果游戏能流畅地在地图中运行,那框架性能就足以应付绝大多数应用了。

  • three-tile 最初的目标其实是开发游戏,地图只能算是 three-tile 的一个扩展,但目前地图的应用场景远多于游戏,所以现在框架的重点已经偏向于地图开发了。

1. 更换控制器

three-tile 的地图模型不依赖 camera、controls,所以可以任意更换控制器,第一人称视角,只需要将控制器更换为 FirstPersonControls:

https://threejs.org/docs/?q=controls#examples/zh/controls/FirstPersonControls

修改 GLViewer 将控制器换成 FirstPersonControls,可参考:

https://threejs.org/examples/?q=controls#misc_controls_pointerlock

TIP

计划下个版本增加使用 FirstPersonControls 控制器的 Viewer (V0.11.4 已内置)。

2. 射击

监听地图容器的 click 事件,当鼠标单击地图时:

1. 创建一个球体作为炮弹加入地图
2. 球的起点为摄像机坐标
3. 球的终点为屏幕中点(准星处)的地面坐标
4. 调用 Tween 动画,将球从起点动画移动到终点
ts
const fireInit = (viewer: PLViewer, map: tt.TileMap) => {
  const { camera, container, cameraHeight, controls } = viewer;

  const bombGroup = new Group();
  map.add(bombGroup);

  // 创建一个球
  const ball = new Mesh(
    new SphereGeometry(),
    new MeshPhongMaterial({
      map: new TextureLoader().load("../test.jpg"),
      shininess: 3,
      transparent: true,
    })
  );

  // 监听click
  container?.addEventListener("click", (evt) => {
    if (!controls.isLocked || evt.button != 0) {
      return;
    }

    // state.fireCount++;

    // clone一个球
    const aBall = ball.clone();
    aBall.scale.setScalar(cameraHeight / 40);
    bombGroup.add(aBall);

    // 球起点坐标:从摄像机位置开始
    const startPosition = map.worldToLocal(camera.getWorldPosition(new Vector3()));
    aBall.position.copy(startPosition);

    // 取得视线与地面交点
    const crossPoint = map.getLocalInfoFromScreen(camera, new Vector2(0, 0));
    // 球终点坐标:视线与地面有交点则取交点,无交点则取摄像机前方100km
    const endPostion = crossPoint ? map.worldToLocal(crossPoint.point) : map.worldToLocal(camera.localToWorld(new Vector3(0, 0, -100 * 1000)));

    // 球飞行时间
    const delay = 800;
    // 球直线飞行动画
    new Tween(aBall.position).to(endPostion, delay).easing(Easing.Quadratic.Out).start();
    // 球边飞边转
    new Tween(aBall.rotation).to({ x: Math.PI, y: Math.PI, z: Math.PI }, delay).start();

    // 球减速下落动画(模拟重力作用)
    new Tween(aBall.position)
      .to({ z: endPostion.z }, delay)
      .easing(crossPoint ? Easing.Quadratic.In : Easing.Quartic.Out)
      .start()
      .onComplete(() => {
        // 动画完成延迟销毁球
        const dispose = () => {
          aBall.removeFromParent();
          aBall.geometry.dispose();
          aBall.material.dispose();
        };
        if (crossPoint) {
          setTimeout(dispose, 10 * delay);
        } else {
          dispose();
        }
      });

    // 后坐力
    const pos = camera.position.clone();
    camera.position.sub(camera.getWorldDirection(new Vector3()).multiplyScalar(1000));
    setTimeout(() => {
      camera.position.copy(pos);
    }, 50);
  });
};


Released under the MIT License.