本書を手に取っていただき、誠にありがとうございます。
本書は、Babylon.js勉強会の活動の一環として、Babylon.jsをもっと広めたいという有志の熱意から生まれ、2023年11月の「技術書典15」で発売した「Babylon.js レシピ集 Vol.3」をもとに、加筆修正されました。Babylon.js v6.0の新機能であるHavokの活用例から、プレゼンテーションへの活用、PLATEAUデータとの連携方法など、さらなる広がりをご覧いただけます。Babylon.jsのコミュニティもあるので、ぜひBabylon.js勉強会1にも参加してみてください。
Babylon.js レシピ集 Vol.3 著者代表 / 藤原貴之
本書に記載された内容は、情報の提供のみを目的としています。したがって、本書を用いた開発、製作、運用は、必ずご自身の責任と判断によって行ってください。これらの情報による開発、製作、運用の結果について、著者はいかなる責任も負いません。
本書では、Babylon.jsの基本的な説明について一部省略することがあります。公式ドキュメント2や「Babylon.js レシピ集 Vol.1」3をご覧いただければ幸いです。
本書では、3次元形状をもつ図形について、以下のように定義しています。
・メッシュ:テクスチャや色はついておらず、単に形状がわかるもの(例:直方体、球)
・3Dモデル:メッシュにテクスチャや色がついていて、客観的に何かを認識できる:
また、Playgroundを既知の情報として扱います。Playgroundとはブラウザで表示されるサイトです。
Babylon.jsのコードを記述すると動作確認ができます。
Playground上で保存された情報は公開されるので、誰でも確認できます。
本書に記載されている会社名、製品名などは、一般に各社の登録商標または商標、商品名です。会社名、製品名については、本文中では©、®、™マークなどは表示していません。
本章では、Babylon.jsとHavokの基礎的な内容をご紹介します。
本章では概要をかいつまんで解説し、実際にシンプルなコードを書くことでHavokの理解を促します。実装に際してはBabylon.js Playgroundを利用するため、開発環境をお持ちでない方や、環境構築の時間をとられたくない方にもとっつきやすい内容になっています。まだBabylon.jsに慣れていない方やHavokに触れたことのない方の最初の1歩となる、そんな内容を目指しました。
Babylon.jsで提供されるようになった物理エンジンのひとつです。もともとはHavok社1という会社をMicrosoft社が買収したことから、Babylon.js 6.0より機能が提供されるようになりました。Microsoft公式2ブログには、以下のように記述されています。
Since its inception in 1998, Havok has created products that power some of the biggest games in the market. It is one of the most widely used, well-known physics engine in AAA game development and for the first time many of their most advanced features are now available in Babylon.js 6.0!
(日本語訳)1998年の創業以来、Havok社は市場最大級のゲームソフトを生み出す製品を開発してきました。HavokはAAAゲーム開発において最も広く使用され、よく知られている物理エンジンのひとつです。そして初めて、彼らの最も先進的な機能の多くがBabylon.js 6.0で利用可能になりました!
Babylon.jsに搭載されたのは、2023年4月21日3です。搭載からまだ1年未満ですが、すでに多くの作品が生み出されています。
物理エンジンを取り込むことで実際の動きを意識することなく、現実に近い挙動を実現させることができます。これはより現実的なゲームの作成だけでなく、科学実験や現実空間の動作検証を実証するのにも役立てられる可能性があります。
実装をはじめていきます。インターネットで『Babylon.js Playground』と調べてPlayground4にアクセスするか、以下のリンクから直接アクセスしてください。
このPlayground上でコードを書いていきます。最初に球体が表示されていますが、今回はまったく新しい状態からコードを書くので、最初に表示されているコードをすべて削除します。なお、ブラウザサイズが小さすぎるとコードが表示されない仕様のため、コードが表示されない場合はサイズを変えてみてください。
Babylon.jsにはシーンとカメラが必須となります。最もシンプルなコードを実装し、骨子を理解しましょう。完成状態はこちら5となります。
シーンとは、3D空間の全体を指す内容となります。シーンの上にモデルやカメラ、ライトといったさまざまな物をおいていく土台をイメージしていただけるといいかもしれません。これがないと何も配置できないので、シーンは必須となります。もう少し詳細に理解されたい方は、以下の公式ドキュメント6を参照してみてください。
カメラは見えている方向や機能を指します。これがあることによって、表現されたものを見ることができます。一人称視点で固定の物もあれば、回り込んで見られるカメラもあります。さまざまな種類がありますが、今回は基本的なFreeCamera7を用います。
1: const createScene = function () {
2: const scene = new BABYLON.Scene(engine);
3:
4: // カメラ(Camera)
5: const camera = new BABYLON.FreeCamera(
6: "camera1", new BABYLON.Vector3(0, 5, -10), scene);
7: camera.setTarget(BABYLON.Vector3.Zero());
8: camera.attachControl(canvas, true);
9:
10: return scene;
11: };
これでBabylon.jsの準備が整いました。実行ボタンをクリックすると、特にエラーなく実行ができるでしょう。しかしあくまで最小構成であって、背景以外には何も表示されない状態となっています。そこで、次は球体(Sphere)、ライト(Light)を用意して、目の前の空間に球体を表示させます。
球体を表示させるには、BABYLON.MeshBuilder.CreateSphereを用います。変数を宣言し、引数には名前とsceneを入れます。
const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", scene);
これにより球体の情報が定義されました。
sphere.position.y = 1;
で表示させてみましょう。
これらのコードをcameraとreturnの間に記述します。おそらく、真っ黒な球体が表示されたことでしょう。
球体のモデルが真っ黒になった理由としては、光が当たっていないからです。現実の世界では意識することは少ないですが、プログラムの世界ではキチンと光の情報も定義する必要があります。一度定義しておけば空間全体に適応されるので、cameraの下に書くのがいいと思われます。lightはHemisphericLightクラスのインスタンスです。intensityは強度を表します。これは0ほど暗く、大きくなるほど明るくなります。今回は0.5ほどに設定します。以下のコードを先ほど記述したconst sphereとcameraの間に記述します。
1: const light = new BABYLON.HemisphericLight(
2: "light", new BABYLON.Vector3(0, 1, 0), scene);
3: light.intensity = 0.5;
1: const createScene = function () {
2: const scene = new BABYLON.Scene(engine);
3:
4: // カメラ(Camera)
5: const camera = new BABYLON.FreeCamera(
6: "camera1", new BABYLON.Vector3(0, 5, -10), scene);
7: camera.setTarget(BABYLON.Vector3.Zero());
8: camera.attachControl(canvas, true);
9:
10: // ライト(Light)
11: const light = new BABYLON.HemisphericLight(
12: "light", new BABYLON.Vector3(0, 1, 0), scene);
13:
14: light.intensity = 0.5;
15:
16: // 球体(Sphere)
17: const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", scene);
18:
19: return scene;
20: };
また、Sphere以外にもBox, Cylinder, Planeなど、さまざまな形状(メッシュ)が存在します。組み合わせることで、多様なモデルを作ることが可能となっています。
次にHavokの理解として、ボールを落下させて別のボールにぶつけるコードを書きます。実行結果はこちら8です。これを理解することにより、Havokの物理的な挙動を実装するためのコードのイメージをつかむことを目指します。
1: const createScene = function () {
2: const scene = new BABYLON.Scene(engine);
3:
4: // Havokの基本設定
5: const gravityVector = new BABYLON.Vector3(0, -9.81, 0);
6: const physicsPlugin = new BABYLON.HavokPlugin();
7: scene.enablePhysics(gravityVector, physicsPlugin);
8:
9: // カメラ(Camera)
10: const camera = new BABYLON.FreeCamera(
11: "camera", new BABYLON.Vector3(0, 5, -10), scene);
12: camera.setTarget(BABYLON.Vector3.Zero());
13: camera.attachControl(canvas, true);
14:
15: // ライト(Light)
16: const light = new BABYLON.HemisphericLight(
17: "light", new BABYLON.Vector3(0, 1, 0), scene);
18: light.intensity = 0.7;
19:
20: // 地面(Ground)および物理設定
21: const ground = BABYLON.MeshBuilder.CreateGround(
22: "ground", { width: 10, height: 10 }, scene);
23: new BABYLON.PhysicsAggregate(ground, BABYLON.PhysicsShapeType.BOX,
24: { mass: 0, friction: 0.4, restitution: 0.6 }, scene);
25:
26: // 衝突する側のボール(Collided Sphere)および物理設定
27: const collided_sphere = BABYLON.MeshBuilder.CreateSphere(
28: "collided_sphere", { diameter: 2, segments: 32 }, scene);
29: collided_sphere.position.y = 1;
30: new BABYLON.PhysicsAggregate(collided_sphere,
31: BABYLON.PhysicsShapeType.SPHERE,
32: { mass: 1, friction: 0.4, restitution: 1 }, scene);
33:
34: // 落下する側のボール(Drop Sphere)および物理設定
35: const drop_sphere = BABYLON.MeshBuilder.CreateSphere(
36: "drop_sphere", { diameter: 2, segments: 32 }, scene);
37: drop_sphere.position.x = 0.1;
38: drop_sphere.position.y = 5;
39: new BABYLON.PhysicsAggregate(drop_sphere,
40: BABYLON.PhysicsShapeType.SPHERE,
41: { mass: 1, friction: 0.4, restitution: 1 }, scene);
42:
43: return scene;
44: }
今回増えたのは「Havokの基本設定」「地面(Ground)および物理設定」「衝突する側のボール(Collided Sphere)および物理設定」「落下する側のボール(Drop Sphere)および物理設定」になります。順番に理解していきましょう。
まずは重力の情報を定義します。
const gravityVector = new BABYLON.Vector3(0, -9.81, 0);
がその該当箇所になります。-9.81という数字は重力加速度の値を指しています。これがY軸のマイナスになることで、物体は地球上と同様に下へと落下します。次にHavokプラグインのインスタンスを生成し、変数に入れます。
1: const physicsPlugin = new BABYLON.HavokPlugin();
最後に、このふたつの要素をscene全体に反映させるために以下を記述します。
1: scene.enablePhysics(gravityVector, physicsPlugin);
enablePhysicsは物理エンジンの有効化を意味します。これでscene内に物理的な挙動ができるようになりました。次は、ボールが落ちないように地面を作りましょう。
21~22行目は「1.2 最小構成のBabylon.js」で実装したコードの応用例になります。MeshBuilderの後がCreateGroundになっていること、第二引数に{width:10, height:10}という要素があるくらいの違いです。23~24行目はPhysicsAggregateクラスのインスタンスを作成、引数には物理演算を適用する対象のメッシュ、メッシュの形状、サイズなどの情報、sceneを入れます。これを書くことで、Groundはすり抜けてしまうことなくボールの衝突に対してリバウンドします。
最後はふたつのボール(Sphere)の記述を書いていきます。文量は多くなりましたが、書いている中身はGroundのそれとほとんど同じです。CreateSphereになっていることと、positionの情報があることに注意しましょう。
実装したら、実行ボタンがあるので押して動作を確認してみましょう。ボールが自由落下して下のボールと衝突、そのボールが転がり地面から落下するのがわかるかと思います。これで落下、衝突、物理的な動きの反映が実装できるようになりました。
今回、positionの位置が真上からでなく少しズレた位置からの衝突になっているのにはお気付きになられたでしょうか?これは、真上からボールを落とすとゲームエンジンの仕様上、ボールが転がっていかないという現象が引き起こされるためです。
試しに、drop_sphere.position.x = 0.1;の記述をなくしてみてください。
下のボールの真上に、ピタッとボールが停止することが確認できます。あくまでもゲームエンジンの挙動はプログラム的な挙動であり、必ずしも現実のそれを完全に再現しているわけではないようです。
数値をいじってみたりモデルを別の物に変えたりして遊んでみると、よりそれぞれのコードの理解が深まります。単純に言葉で読む以上に、ご自身で動かすことでよりしっかりとした理解につながります。
また、今回のHavokを利用することで、Babylon.jsで基礎的な物理運動が実装できるようになったと思います。利用することでさまざまな応用例が考えられます。たとえば、現実的には難しい素粒子の衝突を実装し、動画にするのも面白いかもしれません。Havokでどんなことができるかは、Babylon.js DocumentationのPlayground search9から検索窓に『Havok』と入れてみると、他の方が作成したコードを見ることができます。他の方の実装を見ることで、さまざまな着想が得られるかと思います。