はじめに
この記事はElixirアドベントカレンダー2025のシリーズ2、11日目の記事です
今回はBabylon.jsのチュートリアルの2章をLiveView+Babylon.jsでやっていく記事になります
10日目の続きから進めていきます
下準備
下準備としてLiveViewとHooksの雛形を作っておきます
Homeにリンクを追加
lib/babylon_web/live/home_live.ex
defmodule BabylonWeb.HomeLive do
use BabylonWeb, :live_view
def render(assigns) do
~H"""
<Layouts.app flash={@flash}>
<div class="flex flex-col w-1/2 gap-4">
<.button navigate={~p"/ch101"}>Chapter1-01</.button>
<.button navigate={~p"/ch103"}>Chapter1-03</.button>
+ <.button navigate={~p"/ch201"}>Chapter2</.button>
</div>
</Layouts.app>
"""
end
end
2章のLiveViewをルーティングに追加
lib/babylon_web/router.ex
scope "/", BabylonWeb do
pipe_through :browser
live "/", HomeLive
live "/ch101", Ch101Live
live "/ch103", Ch103Live
+ live "/ch201", Ch201Live
end
描画するLiveViewを作成
lib/babylon_web/live/ch201_live.ex
defmodule BabylonWeb.Ch201Live do
use BabylonWeb, :live_view
def render(assigns) do
~H"""
<div class="p-4">
<.header>
Chapter 2-01
<:actions><.button navigate={~p"/"}>Back</.button></:actions>
</.header>
</div>
<canvas class="w-screen h-[88vh]" id="renderCanvas" phx-hook="BabylonHook201" phx-update="ignore">
</canvas>
"""
end
def mount(_params, _session, socket) do
{:ok, socket}
end
end
hooks
カメラとライトは毎回使うのでutil化
assets/js/util.js
const createAttachment = ({ scene, canvas }) => {
const camera = new BABYLON.ArcRotateCamera(
"camera",
-Math.PI / 2,
Math.PI / 2.5,
15,
new BABYLON.Vector3(0, 0, 0),
scene
);
camera.attachControl(canvas, true);
new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0), scene);
};
export { createAttachment };
assets/js/BabylonHook201.js
import { createAttachment } from "./util";
const BabylonHook201 = {
mounted() {
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const scene = new BABYLON.Scene(engine);
createAttachment(scene,canvas)
engine.runRenderLoop(() => {
scene.render();
});
}
}
export default BabylonHook201
assets/js/hooks.js
import BabylonHook101 from "./BabylonHook101";
import BabylonHook103 from "./BabylonHook103";
+ import BabylonHook201 from "./BabylonHook201";
const Hooks = {
BabylonHook101: BabylonHook101,
BabylonHook103: BabylonHook103,
+ BabylonHook201: BabylonHook201,
};
export default Hooks;
chapter 2-01 地面 (Grounding the World)
地面と箱を追加します
箱が [0, 0, 0]で表示すると貫通するので [0, 0.5, 0]にします
assets/js/BabylonHook201.js
const BabylonHook201 = {
mounted() {
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const scene = new BABYLON.Scene(engine);
createAttachment(scene, canvas);
+ const box = BABYLON.MeshBuilder.CreateBox("box", {});
+ box.position.y = 0.5;
+ const ground = BABYLON.MeshBuilder.CreateGround("ground", {
+ width: 10,
+ height: 10,
+ });
engine.runRenderLoop(() => {
scene.render();
});
},
};
2-02はスキップします
chapter 2-03: メッシュを設置 (Place and Scale a Mesh)
box1は初期値で大きさを設定
box2はscalingで個別に大きさを設定、位置をVectorでまとめて設定
box3はscalingをVectorでまとめて設定し、位置は個別に設定しています
assets/js/BabylonHook203.js
import { createAttachment } from "./util";
const BabylonHook203 = {
mounted() {
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const scene = new BABYLON.Scene(engine);
createAttachment(scene, canvas);
const ground = BABYLON.MeshBuilder.CreateGround("ground", {
width: 10,
height: 10,
});
const box1 = BABYLON.MeshBuilder.CreateBox("box1", {
width: 2,
height: 1.5,
depth: 3,
});
box1.position.y = 0.75;
const box2 = BABYLON.MeshBuilder.CreateBox("box2", {});
box2.scaling.x = 2;
box2.scaling.y = 1.5;
box2.scaling.z = 3;
box2.position = new BABYLON.Vector3(-3, 0.75, 0);
const box3 = BABYLON.MeshBuilder.CreateBox("box3", {});
box3.scaling = new BABYLON.Vector3(2, 1.5, 3);
box3.position.x = 3;
box3.position.y = 0.75;
box3.position.z = 0;
engine.runRenderLoop(() => {
scene.render();
});
},
};
export default BabylonHook203;
ch2-01に以下を追加して回転を試します
box.position.y = 0.5;
box.rotation.y = Math.PI / 4;
2-04: 基本的な家 (A Basic House)
シリンダーメッシュをtessellation: 3で作成して
z軸に回転させて、屋根っぽく載せます
assets/js/BabylonHook201.js
import { createAttachment } from "./util";
const BabylonHook201 = {
mounted() {
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const scene = new BABYLON.Scene(engine);
createAttachment(scene, canvas);
const ground = BABYLON.MeshBuilder.CreateGround("ground", {
width: 10,
height: 10,
});
const box = BABYLON.MeshBuilder.CreateBox("box", {});
box.position.y = 0.5;
+ const roof = BABYLON.MeshBuilder.CreateCylinder("roof", {
+ diameter: 1.3,
+ height: 1.2,
+ tessellation: 3,
+ });
+ roof.scaling.x = 0.75;
+ roof.rotation.z = Math.PI / 2;
+ roof.position.y = 1.22;
engine.runRenderLoop(() => {
scene.render();
});
},
};
export default BabylonHook201;
最後に
長くなってきたので一旦ここまで続きはその2で



