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 ですので、これをコピーしてゲームを作っても構いません。作ったら教えて頂けると嬉しいです。