🔥

Three.js Cannon.es 調査資料 - ギア(MT)の導入/トルク曲線(エンジン性能曲線)にそった挙動

2024/11/09に公開

この記事のスナップショット

マニュアル車での画面

ソース

https://github.com/fnamuoo/webgl/blob/main/032

  • ソース一式を WEB サーバ上に配置してください
  • 車の操作法
    • カーソル上 .. アクセル
    • カーソル下 .. バック
    • カーソル左、カーソル右 .. ハンドル
    • 'b' .. ブレーキ
    • 'c' .. カメラ視点の変更
    • 'r' .. 姿勢を戻す
    • 'x' .. シフトアップ
    • 'z' .. シフトダウン
    • マウス操作 .. カメラ位置の変更
    • '1' .. 車変更:FR低出力(デフォルト)3速
    • '2' .. 車変更:FR高出力 5速(クロスギア)

概要

今までの車の挙動が EngineForce 一定でEV車のような挙動?でしたが、ホイールの回転に応じて EngineForce を変化、つまりトルク曲線(エンジン性能曲線)を模倣したエンジンの動きをさせてガソリン車(マニュアル車)のような動きをさせます。
トルク曲線は疑似的に上に凸の二次関数を使います。
さらに複数のギア(シフト)を用意して、マニュアル車に仕上げます。

やったこと

物理モデル RaycastVehicle のサンプルでもそうなのですが、アクセルボタン押下(矢印キーの上)したときに一定の EngineForce を割り当てる書き方をしています。この場合、常に一定の加速をしているような動きになります。

いままでのコード
    keydown_ArrowUp() {
        this.moVehicle_.applyEngineForce(-this.maxForce_, 2)
        this.moVehicle_.applyEngineForce(-this.maxForce_, 3)
    }

今回、ホイールの回転(deltaRotation)に応じた EngineForce(トルク?)をトルク曲線から算出することで一定の速度以上にならない、ある範囲のみスピードが伸びて、その前後では伸びにくい挙動、ガソリンエンジンのような挙動になります。

今回のコード
    // アクセル時の applyEngineForce を func(ホイール回転数 deltaRotation) に比例させる
    deltaRot2force() {
        var x = Math.max(this.moVehicle_.wheelInfos[2].deltaRotation,
                         this.moVehicle_.wheelInfos[3].deltaRotation);
        x = Math.abs(x);
        // トルク曲線(エンジン性能曲線)を模倣した2次関数に当てはめる
        var x2 = x - this.gearfx_;
        var y = this.gearfa_*x2*x2 + this.gearfb_;
        // エンジンブレーキを有効にするため -0.5倍まで許容する
        y = Math.max(-0.5, y);
        return y * this.maxForce_;
    }

    keydown_ArrowUp() {
        var vforce = this.deltaRot2force();
        this.moVehicle_.applyEngineForce(-vforce, 2)
        this.moVehicle_.applyEngineForce(-vforce, 3)
    }

実際のトルク曲線を用意するのは面倒なので、上に凸の二次関数で代用します。
また、トルク曲線1つ(1速)だけでは限界があるので、複数の曲線を用意します。
ここで曲線の傾きや係数はかなり適当に決めたものですが、それっぽい挙動になっているといいな(願望)。

トルク曲線(3速)

トルク曲線を模倣した二次関数の係数(3速)
    // トルク曲線/エンジン性能曲線を模した二次関数の係数 y = a*(x-s)^2 + b
    //                        a    b     s    y=0となるx(大きい方)
    shiftleverFuncFactor = [[-4  , 1   , 0.2, 0.7],
                            [-2  , 0.85, 0.7, 1.352],
                            [-0.6, 0.8 , 1.2, 2.355]
                           ];

トルク曲線(5速)

トルク曲線を模倣した二次関数の係数(5速)
    // トルク曲線/エンジン性能曲線を模した二次関数の係数 y = a*(x-s)^2 + b
    //                        a    b     s    y=0となるx(大きい方)
    shiftleverFuncFactor = [[-15 , 1.5 , 0.1, 0.417],
                            [-4  , 1   , 0.3, 0.8  ],
                            [-2  , 0.85, 0.5, 1.152],
                            [-1.2, 0.82, 0.8, 1.627],
                            [-0.6, 0.8 , 1.2, 2.355]
                           ];

あと、シフト(ギア数)表示を画面の中央上部に配置してます。
やり方は前回の記事「スピードメーターとコース地図の表示」と同様なので割愛します。

シフト表示

シフトの右下に現れる%の値は、回転数とそのギアの最大回転数の割合をしめしており、50付近なら最大出力を出せて、100に近いなら上のギアにシフトアップ、逆に低いときは下のギアにシフトダウンする目安をしめしています。可能ならタコメーターとかでかっこよくみせたいところですが、今はこれで我慢。

感想

今回の記事は、下記で紹介されているコードに触発されて。

https://forum.babylonjs.com/t/havok-raycastvehicle/40314

babylon7 で vehicle モデルがまだ実装されてなくて、だれか作ってないかなと探していたところで上記に巡り合いました。cannonjs の RaycastVehicle と WheelInfo をコピペして使っている模様。摩擦係数をスピードで変化させたり、内輪差を考慮した舵角の付け方はなるほどと思いつつも、「加速を制御するためにスピードに応じて決めている」の発想は面白いのですが、試走してみると違和感が。

加速を制御するという着想が面白かったので、そのアイデアを拝借しつつ、もっとよりリアルっぽく、「スピード」ではなく「ホイールの回転」でやってみました。

試走してみるといい感じです。
ただ3速のほうはギアが開きすぎて加速がぎこちない。5速の方は「良くなったけど、こんなもん?」な感じです。いやー、奥が深いです。
シフトチェンジに割り当てたキーが 'z' と 'x' で、使いにくかったら申し訳ないです。正直キーボードだとつらいので、ゲームコントローラーにひかれる今日この頃。
というか、レースゲームではオートマ派なのでシフト操作がむずい。

最後に、今回あれこれいじっていて、複数キー押下時のイベントリスナーについて個人的な発見があったので次の記事で紹介します。

Discussion