1. はじめに
前回 #11 では、Smart Ball を Canvas 2D へ移植して「当たり判定・カップ衝突・サブステップ」など、ゲーム側の土台を固めました。
今回は、Babylon.js + Havok でミニボウリングを試作。
前回の記事はこちら:
[Babylon.js #11] 3DスマートボールをCanvas 2Dへ移植 — アーチ壁・サブステップ・カップ判定の実装
Babylon.js版スマートボールをCanvas 2Dに移植し、物理と当たり判定を自前実装。サブステップでの時間刻み、上部アーチ壁の衝突反射、ポケットのカップ形状を線分近似で判定し“乗る→落ちる”で入賞させる設計、チャージ発射や軽量演出まで、ロジック中心に解 …
https://humanxai.info/posts/babylonjs-11-smart-ball-canvas2d-port-arch-substep-cup-collision/今回のゴールは「完成度の高いボウリング」ではなく、最小コストで“ゲームとして成立”させること。
- ピンが物理で倒れる(Havok)
- レーン上を左右に移動できる(A/D または ←/→)
- スペース長押しでパワーを溜め、離した瞬間に投球する
- ピンはGLBモデルに差し替え、PMXキャラを表示して雰囲気を作る(Tポーズ回避にVPD適用)
実装のコアは「見た目」と「物理」を分離することです。ピンは 見た目はGLB、当たり判定は 単純な円柱(Cylinder) にして挙動を安定させます。
モデルデータは、sketchfabからお借りしてます。
制作者:MSerdar Tekin様
MSerdar Tekin (@teriologia)
View the profile and 3D models by MSerdar Tekin (@teriologia)
https://sketchfab.com/teriologiaポーズデータは、BowlRollからお借りしてます。
制作者:じぶん 様
じぶん - BowlRoll
じぶんのユーザーページです
https://bowlroll.net/user/200761モデルデータは、VROID STUDIOで制作してます。
VRoid Studio
3D創作を誰でも楽しめる世界へ。VRoid Studioは3Dキャラクター制作ソフトウェアです。はじめての人にはかんたんに、慣れた人はよりこだわって、3Dキャラクターを作ることができることができます。
https://vroid.com/studioスクリーンショット:
動画(Youtube):
Babylon.js×MMD ミニボウリング試作|長押し投球×Havok物理 #Shorts
Babylon.js + Havokでミニボウリング試作。A/Dで左右移動、Space長押しでパワー投球(離すと発射)。Tech:Babylon.js / Havok / TypeScript / MMD(bpmx) / VPD / glTF(GLB)Credits:- Bowling Pin 3D Mode...
https://youtube.com/shorts/c3frhY9-b6k?feature=share動画(PC):
2. 今回の完成条件
今回の“完成”は、作り込みではなく プレイ可能性の成立を基準にしました。短時間でデモとして成立させるため、達成条件は次の5つに絞っています。
-
ピンが倒れる(Havok) 見た目がボウリングでも、ピンが倒れないとゲームになりません。まずは Havok を入れて「当てれば倒れる」物理挙動を最優先にします。
-
レーン上で左右移動できる(A/D or ←/→) “狙って投げる”体験を作るために、プレイヤー位置(投球位置)の X移動だけを許可します。移動は
playerRootを作って、レーン幅の範囲でクランプするだけの最小実装です。 -
スペース長押しでパワー投球(離した瞬間に発射) クリック投球は ArcRotateCamera のドラッグ操作と干渉しやすいので、投球操作はキーボードに寄せました。スペース押下でチャージ開始、キーを離した瞬間に
applyImpulseで発射します。 -
ピンはGLB表示に差し替え 仮の円柱/箱でも挙動は確認できますが、絵として成立させるにはモデル差し替えが必要です。ここは「ピンっぽさ」が出るだけで説得力が跳ね上がります。
-
PMXキャラを表示(VPDでTポーズ回避) 操作主体(プレイヤー)の存在感を出すために PMX(bpmx)を表示し、左右移動に追従させます。Tポーズのままだと一気に雰囲気が落ちるので、VPDポーズを当てて“立っている”状態にします。
この5点を満たした時点で、細部(スコア、リセット、演出)は未実装でも「動画にできる」段階になります。今回の作業は、まずそこまで最短で到達することを狙いました。
3. ピンは「見た目」と「物理」を分ける
今回いちばん効いた設計は、ピンを 「見た目」と「物理」で分離したことです。
- 見た目:GLB(bowling_pin.glb)
- 物理:単純な円柱(Cylinder / Capsule)
ボウリングのピンは形状が細かく、GLBのメッシュをそのまま物理形状として使うと、たいてい挙動が不安定になります(接触判定が過剰に複雑になり、跳ねたり、引っかかったり、計算も重くなる)。 そこで、物理は最初から割り切って 円柱一本にしました。ピンの見た目はGLBで十分“それっぽく”なるので、ゲームとしての安定性を優先できます。
実装としては、以下のように 見えない円柱(物理)を作り、その子として GLBの複製(見た目)をぶら下げます。
phys:PhysicsAggregate(Cylinder)を持つ見えないメッシュvisRoot:ピンGLBをcloneした表示ルート(physの子)
足元合わせが地味に難所で、GLBの原点や階層の違いで yOffset を計算して合わせたくなりますが、今回は「早く仕上げる」が目的なので、確実に崩れにくい固定解に寄せました。
visRoot.position.set(0, -pinH / 2, 0);
物理円柱は phys.position.y = pinH / 2 で床上に立てているので、見た目側を -pinH/2 だけ下げると、円柱の中心にだいたい合います。完全な自動フィットではないものの、モデル差し替えで破綻しにくく、調整もしやすいので、今回のフェーズではこの方式が一番コスパが良かったです。
まとめると、今回のピン周りの方針はこうです:
- GLBを物理にしない(重い・不安定)
- 物理は単純形状(円柱/カプセル)で安定させる
- 見た目はGLBを親子付けで追従させ、足元は固定オフセットで合わせる
この分離を入れた時点で、ピンが倒れる挙動が安定し、以降の作業(投球・カメラ・演出)に集中できるようになりました。
4. レーンと壁
レーン周りも、今回は「見た目より挙動を優先」して最小構成にしました。
- レーン本体:Boxメッシュ + BOX物理
- 左右の壁:Boxメッシュ(Physics用)だけ置いて isVisible=false
レーンを本格的な溝形状にしたり、ガターを作り込んだりもできますが、まずは球が横に逃げない“遊べる状態”が先です。左右壁を不可視にしておけば、見た目は邪魔せず、物理だけ効かせられます。
摩擦と反発のざっくり方針
ここは正解があるというより、「ボウリングっぽい挙動」に寄せるための調整です。今回は次の考え方で数値を置きました。
-
レーン:摩擦は高め / 反発は低め ボールがバウンドして跳ねるより、前に転がっていく挙動が欲しいので、restitution は抑えます。
frictionは高めにして“転がってる感”を出します。 -
壁:反発は低め / 摩擦はそこそこ 壁に当たった時にピンボールみたいに跳ね返らせないため、壁も
restitutionは低め。摩擦は適度に入れて、接触時の不自然な滑りを減らします。
実際の調整は、以下の3点を触るだけでかなり変わります。
laneのfriction(転がり方)ballのrestitution(跳ねやすさ)wallのrestitution(横反射の強さ)
この段階では細かい物理再現よりも、「ボールが前に進み、ピンが倒れ、ゲームとして成立する」ことが重要なので、数値は“それっぽく”動く範囲で割り切っています。ここが固まったら、次のフェーズでガター形状やレーン材質(PBR/木目)に寄せていけば十分です。
5. 操作:左右移動+長押し投球
操作は「狙って投げる」体験を最短で作るために、入力をかなり割り切りました。ポイントは プレイヤー座標を表す playerRoot を1つ作ることです。
playerRoot:Xだけ動かす
playerRoot はレーン上の“投球位置”を表すノードで、毎フレーム Xだけ更新します。Z(投球位置)は固定にして、左右移動だけ許可することで「ボウリングっぽい狙い」を作れます。
- A/D または ←/→ で左右
- レーン幅からはみ出さないようにクランプ
- Shift押下で減速(微調整用)
この方式だと、キャラ(PMX)も playerRoot の子にぶら下げるだけで一緒に移動でき、座標管理がシンプルになります。
スペース長押し:power01 を作って投げる
投球は「押して溜める → 離して投げる」にしました。
- KEYDOWN:チャージ開始(
chargeStart = performance.now()) - KEYUP:経過時間から
power01(0..1)を計算 power01をbaseImpulse..maxImpulseにマッピングしてapplyImpulse
こうすると、操作感はほぼ“パワーショット”になり、短いデモでもゲームっぽく見せられます。投球の瞬間にボールを生成するので、毎回「現在の playerRoot のX座標」から発射できるのも都合が良いです(投球位置の管理が一箇所に集約される)。
クリック投球を避けた理由
当初はクリックで発射する案もありましたが、ArcRotateCamera を使っていると
- クリック/ドラッグが カメラ操作(回転・パン) と干渉する
- 発射のつもりがカメラ回転になったり、その逆が起きる
という問題が出やすく、操作が不安定になります。 そこで投球はキーボード(スペース)に寄せ、マウスは「カメラ操作専用」にして干渉を避けました。結果として、デモの操作がかなり安定し、動画でも意図通りの挙動を見せやすくなりました。
6. PMX(bpmx)+VPDポーズ適用
雰囲気づくりとして、レーン手前に PMX(bpmx)モデルを表示し、左右移動(playerRoot)に追従させました。ピンやボールが動くだけでもデモにはなりますが、操作主体(プレイヤー)が見えると一気に“ゲーム画面”になります。
ただし、PMXを読み込んだ直後は Tポーズになりがちで、そのままだと見た目がかなり締まりません。そこで今回は VPD(ポーズデータ)を適用して、待機姿勢を作りました。
VPD適用の流れ
実装手順はシンプルで、次の順番に処理します。
- VpdLoader で VPD を読み込む
- mmdModel.addAnimation(pose) でポーズを登録
- mmdModel.setAnimation(pose.name) でポーズに切り替える
ここまでは想定通りですが、実際にやってみると「ポーズが変わらない」ケースがあります。今回ハマったポイントはそこでした。
反映のポイント:seekAnimation(0) + playAnimation() を最後に呼ぶ
VPDを addAnimation / setAnimation しただけだと、Runtimeが更新されず 見た目がTポーズのままに見えることがあります。
そこで、最後に 0フレへ固定して更新を回すために、
seekAnimation(0)playAnimation()
を “反映処理の最後” に入れました。これで、VPDポーズが確実に画面へ反映されるようになります。
今回の実装では「ポーズを再生したい」のではなく、「姿勢を当てて固定したい」だけなので、ここは少し直感に反する部分でしたが、結果として Runtimeを一度動かして反映させるのが重要でした。
PMXの読み込みと親子付けも含めると、構成は以下のイメージになります。
-
playerRoot:左右移動の基点(操作座標)-
mmdRoot:PMXメッシュ群をまとめるTransformNode- bpmx から読み込んだメッシュ群
-
-
mmdRuntime:MMD更新用 -
VpdLoader:ポーズ読み込み用
この構造にしておくと、キャラの左右移動は playerRoot を動かすだけで済み、ポーズやアニメーションは mmdRuntime 側で独立して扱えるため、あとから「投球モーションを付ける」「待機モーションを追加する」といった拡張もしやすくなります。
7. 見た目改善(影・空・木目)
最小デモが動いた後は、物理の正しさより“画面の説得力”を上げるフェーズです。今回もやったことはシンプルで、効きが大きい順に「影 → 空 → 木目」を足しました。ここを押さえるだけで、同じBoxレーンでも一気に“それっぽい”画になります。
影:DirectionalLight + ShadowGenerator
影は最重要です。ピンが倒れる物理自体は同じでも、影が付いた瞬間に「重量感」「接地感」「距離感」が出ます。
今回は太陽光として DirectionalLight を置き、ShadowGenerator を有効化しました。
- レーン(床)は
receiveShadows = true - ピン(GLB)とボールは ShadowCaster に登録
これだけで、ピンの密集や転倒の見え方が段違いになります。動画でも“実在感”が乗るので、最初に入れて正解でした。
空:グラデ球(Sky Sphere)
背景が単色だと、画面が“実験”っぽく見えます。
そこで巨大な球体を作り、内側にグラデーションを貼った グラデ球(Sky Sphere)を追加しました。テクスチャは外部画像を使わず、DynamicTexture でグラデを描いて貼るだけでも十分です。
空が入ると、コントラストやシルエットが安定して「画面が締まる」ので、影の次にコスパが良い改善点でした。
木目:DynamicTexture / 簡易テクスチャ
レーンがBoxのままだと、色が単調で“黄色い板”になりがちです。 ここは現実的にはテクスチャが強いですが、今回は「早く形にする」方針なので、
- 簡易テクスチャを貼る
- もしくは DynamicTextureで疑似木目を生成する
どちらでもOKにしました。木目が入るだけで、レーンの存在感が出て「ボウリング場っぽさ」が増します。最終品質を狙うならPBR+normal/roughnessまで持っていく余地はありますが、デモ段階ではここまでで十分です。
まとめると、見た目改善は「細部の作り込み」よりも、画面の基本要素を揃えるのが効きます。
- 影で“接地”を作る
- 空で“背景”を作る
- 木目で“材質”を作る
この3点を入れた時点で、試作でも動画として成立する見た目になりました。
8. まとめ
今回は「ボウリングとして最低限“遊べる”」ところまでを最短で作ることを目的に、Havok物理・GLBピン差し替え・左右移動・長押し投球・PMX+VPDポーズ適用までを一気に通しました。 特に、ピンを 見た目(GLB)と物理(円柱)で分離したことで挙動が安定し、操作や演出に作業時間を回せたのが大きいです。影・空・木目を入れた段階で、試作でも画として成立するところまで到達できました。
次は“ゲームっぽさ”を上げる小タスクを足していけば完成度が上がります。今日は遅いので、続きは明日でOK。
その他
-
Rでリセット(ピン立て直し) ピンを再生成し、飛んでいったボールも掃除できるようにすると、デモの回しが一気に楽になります。動画撮影でも「何回も投げる」が途切れなくなります。
-
スコア判定(倒れた判定) 厳密な判定は不要で、ピンの“上方向ベクトル”とワールドYの角度を見るだけでも十分です。一定以上傾いていれば倒れた扱いにして、10本中何本倒れたかを表示すれば“ゲーム”になります。
-
カメラ追従(投球中だけ lockedTarget) 投げた瞬間だけボールを追い、しばらくしたら
playerRootに戻す。これだけで映像が自然になり、プレイしている感が増します(ArcRotateCameraでも簡単に実現できます)。
この3つを入れれば、試作から「ミニゲーム」としての体裁がほぼ整います。
💬 コメント