LoginSignup
10
6

More than 3 years have passed since last update.

babylon.js x ブラウザで VRM モデルを歩かせてみた

Posted at

babylon.js は書いたコードをブラウザ上ですぐに再生出来るプレイグラウンドが用意されています。 TypeScript で型付きで書くことも可能です。この環境で VRM キャラクターを歩かせてみます。

元のソースコードはプレイグラウンドの方に記載されていますが、それぞれ何をしているか紹介します。

const vrmLoaderScript = document.createElement('script');
vrmLoaderScript.src = 'https://cdn.jsdelivr.net/npm/babylon-vrm-loader@v1.4.13/dist/index.js';
document.head.appendChild(vrmLoaderScript);

これは babylon-vrm-loader というライブラリを追加で読み込む処理です。これで babylon.js 上で VRM を読み込むことが可能になります。

※このライブラリ、私が書きましたが、揺れもの(SecondaryAnimation)の処理がバグっています。 help wanted です。


class Playground {
    /**
     * Entry point
     */
    public static CreateScene(engine: BABYLON.Engine, canvas: HTMLCanvasElement): BABYLON.Scene {
        const scene = new BABYLON.Scene(engine);
        const camera = createCamera(scene);
        camera.attachControl(canvas, true);

        vrmLoaderScript.onload = () => {
            BABYLON.SceneLoader.Append(
                'https://raw.githubusercontent.com/vrm-c/UniVRM/master/Tests/Models/Alicia_vrm-0.51/',
                'AliciaSolid_vrm-0.51.vrm',
                scene,
                () => {
                    // create scene after loading VRM
                    Playground.constructScene(scene, camera);
            });
        };

        return scene;
    }
}

プレイグラウンドでは、 GitHub に上がっている アリシアソリッドちゃん を読み込んでいます。

        scene.onBeforeRenderObservable.add(() => {
            // Update every frame
            vrmManager.update(scene.getEngine().getDeltaTime());
            camera.setTarget(new BABYLON.Vector3(vrmMesh.position.x, vrmMesh.position.y + 1, vrmMesh.position.z));
        });

シーンの onBeforeRenderObservable に毎フレーム処理したいものを記述していきます。 vrmManager.update は揺れものの更新、それとカメラを追従するようにターゲット設定しています。

vrmMesh.movePOV(0, 0, delta / 1300);

これで前方に進むことが出来ます。

vrmMesh.rotate(BABYLON.Vector3.Up(), -delta / 1000);

これで左回転しています。

    const inputMap = {
        65: false, // A
        83: false, // S
        68: false, // D
        87: false, // W
    };
    scene.actionManager = new BABYLON.ActionManager(scene);
    scene.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnKeyDownTrigger, function (evt) {
        inputMap[evt.sourceEvent.keyCode] = evt.sourceEvent.type == "keydown";
    }));
    scene.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnKeyUpTrigger, function (evt) {
        inputMap[evt.sourceEvent.keyCode] = evt.sourceEvent.type == "keydown";
    }));

    scene.onBeforeRenderObservable.add(() => {
        const speed = scene.getEngine().getDeltaTime();
        let moveCount = 0;
        if (inputMap[65]) {
            onLeft(speed);
            moveCount++;
        }
        if (inputMap[83]) {
            onBackward(speed);
            moveCount++;
        }
        if (inputMap[68]) {
            onRight(speed);
            moveCount++;
        }
        if (inputMap[87]) {
            onForward(speed);
            moveCount++;
        }
        if (moveCount == 0) {
            onStop();
        }
    });

scene.onKeyboardObservable というものがありますが、これは「押しっぱなしでずっと呼ばれる」のではなく、文字が入力された時(例えば k を押し続けると、 k と入力されてから少し間をおいて kkk... と入力されます)に発火するので、押しっぱなしの処理をするには不向きでした。代わりにこの ActionManager を用いています。ブラウザネイティブの機能を使っても実現出来ると思います。

    const leftUpperLegAnim = {
        name: 'leftUpperLegAnim',
        keys: [
            [0, 0, 0, 0],
            [15, 0, Math.PI / 10, 0],
            [45, 0, -Math.PI / 15, 0],
            [60, 0, 0, 0],
        ],
    };

vrmManager は humanoidBone を持っているので、どんなモデルでも共通のボーンに対して処理を行うことが出来ます。

アニメーションをするために、キーフレームを登録して、さらに AnimationGroup を使って複数のアニメーションをひとまとめにしています。


歩かせるだけですが結構な量のコードになりますね。ここからもっと応用を重ねればなんとかゲームを作れそうです。

このソースコードは Apache License 2.0 ですので、これをコピーしてゲームを作っても構いません。作ったら教えて頂けると嬉しいです。

10
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6