AIゲーム制作ラボ

Unity牽引ゲームの作り方:カプセルとケーブルの衝突判定

救助ゲーム「灰都の救難艇」のC#実装から、艇とは別に動くカプセル、ケーブルの接触、切り離し後の再接続、港での回収条件を解説。実行できるCoreサンプルと、横風で失敗する経路の比較を掲載します。

Libra

公開 2026年10月1日

この記事の要点3項目

艇が通れても牽引物は通れるとは限らない。 艇・カプセル・ケーブルを別々に検査し、角への接触と伸びすぎを防ぐ。

残り2つの要点を読む
  • 艇だけの帰港では回収しない。 カプセルの位置、艇の速度、港内の滞在時間をそろえて一度だけ救助を数える。
  • 同じ横風の経路でも、予告に合わせる操縦は全員無傷、待たない操縦は必須カプセルを喪失した。 C#サンプルで比較を再現できる。
目次8章
検証した環境最終確認 2026年10月1日
  • Unity 6000.6.0f1 / WebGL / Codex制作のv1.0.1
  • 配布Core: Windows / .NET SDK 10.0.401 / 追加NuGetパッケージなし

書いてあるのは、この環境で実際に試した結果です。ツールの仕様や料金は変わるので、重要な判断の前には公式の情報も確認してください。

牽引するゲームでは、自機が壁を避けるだけでは足りません。 私がCodexで制作している「灰都の救難艇」では、艇の後ろを動く救命カプセルと、その間のケーブルにも接触の判定を置きました。角を曲がるとき、艇は通れてもカプセルが建物へ引っかかるためです。

この記事は、v1で検証した牽引・接触・回収のCoreの作り方です。完成ゲームの画面や音を一から組む手順は扱いません。ゲームを直接遊ぶか、操作と制作意図を見ると、牽引の動きを確認できます。

灰都の救難艇v1。崩れた運河の救命1へ接続し、牽引耐久3/3と切り離すボタンを表示
375px幅で接続した実画面。艇とカプセルは別々に移動する。生成素材は本作のために作成し、建物の固体判定と画面の配置を照合した。

動かせるC#サンプルで経路を比較する

.NET 10 SDKを用意し、Coreと検査操縦のZIPを展開します。追加NuGetパッケージは使いません。次をHaitoSample.csprojがある場所で実行します。

dotnet run --project HaitoSample.csproj -c Release
ZIPを展開して実行した出力から抜粋成功
Wind route Won health 5 seconds 85.42043 damage 0 capsuleHealth 3,3,3
Wind no-wait comparison Lost health 5 rescued 0 seconds 9.600025 damage 3 capsuleHealth 0,3,3
Checks 69

出力のLostは検査の失敗ではありません。同じ任務へ、予告を待たない操縦を持ち込むと必須カプセルを失うことを確認しています。Program.csに成功する経路と比較対象の経路があり、Rescue.csに実ゲームの計算、RescueProgress.csに解放と自己ベストの保存規則があります。

v1.0.1では、保存記録の整合性の検査を1件追加しました。任務2のクリア記録が欠けているのに任務3の自己ベストだけ残る保存では、解放数を戻しても任務3の再訪ボタンが残っていました。解放範囲の確定後に範囲外の記録も除き、正常な任務1の自己ベストを保つ条件を確認しています。ZIPの末尾はこの追加を含むChecks 69です。牽引や敵の数値は変えていません。

艇・カプセル・ケーブルを分ける

  1. 手順 01

    艇を動かす

    壁と境界へ接触

  2. 手順 02

    カプセルを引く

    横風と角を検査

  3. 手順 03

    ケーブルを検査

    建物を横切るか

  4. 手順 04

    回収する

    位置・速度・時間

1/60秒のTick内で行う主な処理。描画のフレーム数で回収回数を増やさない。

艇の半径は0.42、カプセルは0.48、ケーブルの長さは2.2です。接続中は通常速度を5.2から3.4へ落としました。艇から遠いカプセルを毎回同じ位置へ瞬間移動させるのではなく、ケーブルの長さを超えた分を引き、途中の建物へ接触するかも調べます。

V offset = c.Position - Ship;
if (offset.Length > CableLength)
{
    V desired = Ship + offset.Unit * CableLength;
    c.Position = Sweep(c.Position, desired - c.Position,
                       CapsuleRadius, out bool hit);
    if (hit)
        HurtCapsule(c, "カプセルが建物に接触。外側へ回るか切り離そう。");

    V stretch = Ship - c.Position;
    if (stretch.Length > CableLength + .1f)
    {
        Ship = Sweep(Ship, c.Position + stretch.Unit * CableLength - Ship,
                     ShipRadius, out _);
        Velocity = new V();
    }
}

カプセルが壁に止まった後も艇だけ進ませると、ケーブルが際限なく伸びます。そこで、艇の位置も最大長の近くまで制限します。接触状態からは、外側へ回る、切り離して退避する、拾い直すという選択を残しました。

接触による耐久減少には0.7秒の待ち時間を入れています。ただし無敵のまま抜けられるようにはせず、同じ壁際で押し続けるとカプセルは失われます。同じ接触状態から、押し続ける操縦は1.633秒、加速を押し続ける操縦は1.517秒で喪失しました。退避する操縦は再接続して回収できました。

加速中の薄い壁と、ケーブルだけの接触を検査する

Sweepは移動量を最大0.08ずつに分け、各位置で円と建物の矩形を検査します。斜め移動で全体が止まった場合はX、Yを順に試して壁沿いに滑らせます。この数値は本作の速度と地形で検証した値で、任意の薄壁を通り抜けない数学的保証とは扱いません。

int n = Math.Max(1, (int)Math.Ceiling(delta.Length / .08f));
V d = delta / n, p = from;
for (int i = 0; i < n; i++)
{
    V full = p + d;
    if (Free(full, radius)) { p = full; continue; }
    contact = true;
    V x = new V(p.X + d.X, p.Y);
    if (Free(x, radius)) p = x;
    V y = new V(p.X, p.Y + d.Y);
    if (Free(y, radius)) p = y;
}

ケーブルは艇とカプセルの直線を0.08以下の間隔で調べ、半径0.025の接触を検査します。どちらの端も建物の外にいても、線が角を横切ればCableBlockedになります。接触中の艇の速度は0.35倍。ケーブルが建物の裏を通って問題なく引ける見た目を避けました。

別の記事との違い

星継ぎの掃引衝突と入力再生は移動線分と円の最初の接触を求めます。本作では二つの物体と直線のケーブルを別々に扱い、牽引物の接触で艇の移動も制限します。

艇だけの帰港を成功にしない

回収はカプセルが港の半径1.8以内、艇の速度が2.2以下、その状態が0.4秒続いた場合です。条件を外れたら滞在時間を0へ戻します。一度救助したものはRescuedにして再計上しません。

if (c.Health > 0 && (c.Position - Port).Length <= 1.8f
    && Velocity.Length <= 2.2f)
    c.DockTime += dt;
else
    c.DockTime = 0;

if (c.DockTime >= .4f)
{
    c.Rescued = true;
    Rescued++;
    Attached = -1;
    ReconnectTime = .8f;
}

最終回収とカプセル破壊の境界も検査しました。耐久0の必須カプセルを成功として数えず、すべての救助と艇の帰還がそろった場合にだけWonへ進みます。未達の帰港では、未回収の数や減速が必要な理由を画面に出します。

横風は「待つ意味があるか」まで比較した

01

横風を実装しても待つ意味がなかった

症状
最初の配置では、待たない大回りも無傷で成功した。
原因
風のある区間を短時間で通れ、予告を使う操縦と使わない操縦の結果が分かれなかった。
対処
風を遮らない低い架橋と、その狭い区間を組み直した。経路と周期を同条件で再実行し、別の救助順でも成功を確認した。
同じ横風任務の操縦方針結果時間カプセル耐久
予告を見て弱まる時間を待つ全員救助・帰還85.42043秒3 / 3 / 3
待たずに同じ経路を進む必須喪失・救助09.600025秒0 / 3 / 3
中央のカプセルから先に拾う全員救助・帰還85.42043秒3 / 3 / 3

風の周期は静穏4秒、予告2秒、横風4秒、静穏2秒です。建物の風下には3.5の陰を作り、艇だけでなくカプセルの位置からも風を調べます。未接続のカプセルは固定具で保護し、出動後に待っているだけで目的物が壊れないようにしました。

風が動いて見えることと、風を読む必要があることは別々に確かめる。

数字は固定刻みの検査操縦の結果です。人が初めて遊んだときの難度や所要時間は測っていません。PCと375px幅のブラウザでは、実際のキーボード・マウス操縦パッド・タッチで全3任務の救助と保存を確認しました。スマホ幅はPC上のエミュレーションです。

まとめ

  • 艇、カプセル、ケーブルの接触を分け、牽引物が止まったら艇の伸びすぎも制限する。
  • 切り離しと再接続、減速と港内の滞在時間を設け、押し続けるだけの操作と立て直しの結果を比較する。
  • 風のある任務は、予告を使う経路、使わない経路、別の救助順を実行して判断差を調べる。

よくある質問

よくある質問

RigidbodyとJointだけで作れますか?

この記事では比較していません。実装はUnityに依存しない固定刻みのCoreです。Jointを使わず、接続距離と速度、牽引物の移動と接触を自分で計算しました。

ケーブルは物理的なたるみを再現していますか?

再現していません。艇とカプセルを結ぶ直線に接触があるかを検査し、牽引の判断が必要になる範囲に絞っています。布やロープの物理シミュレーションではありません。

港へ入ったのに救助されないのはなぜですか?

カプセルも港の半径1.8以内へ入れ、艇の速度を2.2以下にして0.4秒待つ条件です。艇だけが入った場合、通過が速い場合、まだカプセルが外にある場合は回収しません。

サンプルZIPで画像付きのゲームも動きますか?

動きません。牽引・横風・砲撃・解放のCoreと検査操縦だけです。Unityの描画、生成素材、音、入力とWebGLの保存連携は含みません。

出典

出典

  1. 01.NET 10.0 のダウンロードMicrosoft2026-10-01にSDKの用途とWindows向け10.0.401の配布を確認。展開したサンプルを同SDKで実行した。

この記事は役に立ちましたか?

関連記事