Webブラウザで玉転がしを作成してみる（2）

## はじめに

この記事は　[Babylon.js Advent Calendar 2024](https://qiita.com/advent-calendar/2024/babylonjs)
の、21日目の記事です。

何度かに分けて、スマホのセンサーにアクセスして玉転がしをするページを作成しています。


## 今回の内容。
[前回](https://qiita.com/Ykoba791/items/f1343e4e702fdc9c1708)は、ジャイロセンサーにアクセスしてデバイスの傾きを取得して表示するところまで作成しました。

今回は前回作成した処理をベースに土台と玉を用意して、デバイスの傾きに連動して玉が転がるようにしました。
https://playground.babylonjs.com/#2S9H94#24

### 物理エンジンを準備する
Babylon.js は、物理エンジンと連携して物理シミュレーションをサポートしています。

| 物理エンジン | 特徴 |
|-----------|------------|
| Cannon.js  |基本的な機能を持つ軽量なエンジン|
| Ammo.js     | 高機能で複雑な形状に対応するエンジン|
| Havok       | 高精度な物理エンジン        |

今回は軽量なCannon.jsを指定します。
Cannon.jsを使用する場合、Playgroundでは自動で読み込まれますがそうでない場合は下記のように別途ロードする必要があります。

``` JavaScript
<script src="https://cdn.babylonjs.com/cannon.js"></script>
```


３Ｄベクトルを表すクラス Vector3(x, y, z) で、重力に使用するベクトルの初期化をします。ここでは、Y軸方向に、-9.81でm/s² の重力を設定しています。
enablePhysicsで、重力に指定するベクトルと、使用する物理エンジンを指定します。
``` JavaScript
    //物理エンジンにCannnonを使用する
    const gravityVector = new BABYLON.Vector3(0, -9.81, 0); 
    const physicsPlugin = new BABYLON.CannonJSPlugin();
    scene.enablePhysics(gravityVector, physicsPlugin);
```

#### 物理エンジンの機能
上記の物理エンジンの有効化により、質量、形状、衝突時の反発係数を設定できます。また、重力により物体が受ける影響も処理してくれます。


Babylon.jsには、getPhysicsEngineという物理エンジンのインスタンスを取得するためのメソッドです。


### 玉と土台の追加
土台を追加を以下のように追加します。
``` JavaScript
    // 地面を作成
    const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 10, height: 10 }, scene);
    const groundMaterial = new BABYLON.StandardMaterial("groundMat", scene);
    groundMaterial.diffuseColor = new BABYLON.Color3(0.5, 0.8, 0.5);
    ground.material = groundMaterial;

    // 地面に物理特性を追加
    ground.physicsImpostor = new BABYLON.PhysicsImpostor(
        ground,
        BABYLON.PhysicsImpostor.BoxImpostor,
        { mass: 0, restitution: 0.9 }, 
        scene
    );
```
groundを通常の地面と同じように生成したのち、PhysicsImpostorで物理特性を割り当てます。
massはオブジェクトの質量で、0 を指定すると重力の影響を受けずにその場から動かなくなります。土台はその場に留まってほしいので0を指定しています。
restitution は他の物体とぶつかった場合に反発する指定で0だと衝突時に跳ね返らなくなります。

``` JavaScript
    // ボールを作成
    const ball = BABYLON.MeshBuilder.CreateSphere("ball", { diameter: 1 }, scene);
    ball.position.y = 5; // ボールを初期位置に配置

    const ballMaterial = new BABYLON.StandardMaterial("ballMat", scene);
    ballMaterial.diffuseColor = new BABYLON.Color3(1, 0.2, 0.5);
    ball.material = ballMaterial;

    // ボールに物理特性を追加
    ball.physicsImpostor = new BABYLON.PhysicsImpostor(
        ball,
        BABYLON.PhysicsImpostor.SphereImpostor,
        { mass: 1, restitution: 0.33 }, // 質量1、反発係数0.9
        scene
    );
```
転がす玉についても同様にメッシュを作成したのち、PhysicsImpostor で特性を指定します。
玉は重力の影響を受けさせたいため、massは1を指定しています。


### ジャイロセンサーの値で動くようにする
土台と玉の作成ができたので、最後に物理エンジンに重力の方向を設定する処理を記述します。
ジャイロセンサーの読み込みは前回のまま、センサーから読み取ったbetaとgammaの値を物理エンジンの重力にセットします。
初期値として、Vector3(0, -9.81, 0) をセットしましたがジャイロセンサーが傾きを検知したらその角度を反映したベクトルを、Vector3(gamma[左右の傾き], -9.81, beta[前後の傾き]) で作成し、setGravity(gravity)で物理エンジンの重力に反映します。

``` JavaScript
    //センサーの値を表示する
    const handleOrientation = (event) => {
        const alpha = (event.alpha || 0);
        const beta = (event.beta || 0) ;
        const gamma = (event.gamma || 0) ;

        // 重力ベクトルを更新（物理エンジンへ反映）
        const gravity = new BABYLON.Vector3(gamma , -9.81, -beta );
        scene.getPhysicsEngine().setGravity(gravity);
    };
```

### 動作結果
以下の画像のように、スマホを傾けると赤いボールが緑色の土台の上を転がるようになりました。
特にガードもないので、最後は緑色の物体からはみ出して落ちて行ってしまっています。

![MNUH5839.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594551/b0353105-ee73-af16-71a8-677aa50d17f4.gif)



### 最後に
ジャイロセンサーを利用してスマホで玉を転がすサイトを作成しました。

昔はこういった転がすような動きを実装する時は、自分でXYの加速度やら計算しましたが、物理エンジンの重力を弄るだけで物体に反映されるのでかなり楽になったなぁという印象です。

次回は、ガードを付けるなどもう少し機能を増やしてみようと思います。