在babylon.js中,通过键盘控制实现类似吃鸡中的汽车控制--以赛车为例

146 阅读3分钟

一.前言

我们在玩一些通过键盘和鼠标控制模型移动的游戏,例如赛车类的或者人物之类(比如吃鸡吧),没有注意到模型运动的时候有一些特征。这些都是比较简单逻辑没有涉及到物理引擎,而实际开发中会用到物理引擎,比如刚体运动,限制,软体啥等,车子的运动也是通过施加力的作用让它加速或者旋转,这里就不详细展开说了。顺便说一下babylon.js的用到的HK物理引擎真的太棒了

一、当我们往前移动的时候或者只有角速度的时候,汽车的速度是由0慢慢加速然后打到一个速度最大值后速度就不再增加;当我们停止前进,车子就会慢慢减速直到停止;

二、当汽车在在左右旋转的时候,如果车子没有移动(指的是前进),车子是没有办法旋转的,也就是绕着车子整体的Y轴(上方向)旋转,而且汽车在拐弯的时候,它旋转的角速度和线路度是呈正相关的;

三、车轮的转速和旋转的方向和当前车子速度也有关系,我们只需给车轮增加一个绕着车轮本地坐标的Z轴方向一个旋转动画即可

四、当车子前进速度很快,却想让快速停下来可以按下后退来实现刹车效果,比如前进速度很快时,可以按下后退实现刹车;当后退速度很快可以按下前进来实现反向刹车

好了,分析完毕我们开始吧

1.对于场景必要的比如,渲染引擎、相机、灯光、模型加载啥的这些基本都是必须的就不写了 补充说明:我们需要两台相机,一个是第一人称的,另一个是和车子绑定的

 // 车轮动画
 this.tireAnimationGroup = new AnimationGroup('tireGroupAnimation')
 this.tireAnimation = initTireAnimation()
 // 键盘事件
 var inputMap: any = {};
 
 // 初始值
 var carSpeed = 0;
 const acceleration = 0.002
 var carRotationSpeed = 0.02;
 const maxSpeed = 0.8
 
 const braker = (keydown: boolean) => {
 // 阻力作用下停车
  if (keydown === false && carSpeed > 0) {
        carSpeed = Math.max(0, carSpeed - acceleration * 1.5)
   } else if (keydown === false && carSpeed < 0) {
       carSpeed = Math.min(0, carSpeed + acceleration * 1.5)
     }
 }
 
    scene.actionManager = new ActionManager(scene);
 
    scene.actionManager.registerAction(new ExecuteCodeAction(ActionManager.OnKeyDownTrigger, (evt: any) => {
      inputMap[evt.sourceEvent.key.toLowerCase()] = evt.sourceEvent.type == "keydown";
    }));
 
    scene.actionManager.registerAction(new ExecuteCodeAction(ActionManager.OnKeyUpTrigger, function (evt: any) {
      inputMap[evt.sourceEvent.key.toLowerCase()] = evt.sourceEvent.type == "keydown";
    }));

braker这个函数是比较关键的,当我们车加速到一定的速度,然后放开键盘,或者是键盘只按下了A或者D也就是方向,此时车子需要在负的加速度下慢慢减速,直到车子速度为0.

这里没有给车子别的加速度,比如不同的挡(手动挡),加速度应该是不一样的,你们也可以加上其他的按键加上不同的加速度,实现换挡的效果

2.在每一帧里调用,在三维场景模型的位置改变其实就是坐标的改变

补充:当车子移动的时候,顺便把第一人称的相机位置也更新一下,下车的时候,第一人称相机也就是停车后的位置了

   scene.onBeforeRenderObservable.add(() => {
      var keydown = false;
      // 前进,给车动力
      if (inputMap["s"]) {
        carSpeed = Math.min(maxSpeed, carSpeed + acceleration * 2)
        keydown = true;
      }
      // 反向给力
      if (inputMap["w"]) {
        carSpeed = Math.max(-maxSpeed, carSpeed - acceleration)
        keydown = true;
      }
      // 向左拐
      if (inputMap["a"]) {
        this.car.rotate(Vector3.Up(), carRotationSpeed * carSpeed * 2);
        if (!inputMap["w"] && !inputMap["s"]) {
          braker(false)
        }
        keydown = true;
      }
      // 向右拐
      if (inputMap["d"]) {
        this.car.rotate(Vector3.Up(), -carRotationSpeed * carSpeed * 2);
        if (!inputMap["w"] && !inputMap["s"]) {
          braker(false)
        }
        keydown = true;
      }
 
      // 阻力作用下停车
      braker(keydown)
      // 速度改变影响车子的位移,car.forward是模型的朝向,carSpeed具有大小,这样就 
      //得到一个具有大小和方向的速度
      this.car.moveWithCollisions(this.car.forward.scale(carSpeed));
 
      // 在车子开动的时候,顺便把第一人称的相机位置也更新一下
      if (scene.activeCamera instanceof ArcRotateCamera) {
        const carPosition = this.car.position.clone()
        carPosition.y += 2
        fcamera.position = carPosition
      }
      // 车轮转起来,就是让车轮绕着其本地坐标的X轴旋转,根据速度改变speedRatio 
      this.tireAnimationGroup.play(true)
      this.tireAnimationGroup.speedRatio = -carSpeed * 14
 
    });

总结:在文中只是用到一个核心变量carSpeed ,因为车子的运动,正负决定前进和后退,再结合car.forward也就是模型的方向就可以得到一个具有大小和方向的速度,代码的作用在注释都写的比较明白了