AIゲーム制作ラボ

Unityの物理は同じ操作で同じ結果になるか:5回の実測

物理を使うゲームは「毎回ちがう」から自動テストできない、と思っていました。6本目で実際に5回回して測ったところ、記録は一致するのに着地のフレームだけが1回おきにずれました。そこから決めたテストの合格ラインを残します。

Libra

公開 2026年9月20日

この記事の要点5 points

同じ種・同じ入力で5回回すと、<strong>置いた数・落とした数・記録(cm)は毎回一致</strong>した。

残り4つの要点を読む
  • ただし着地の判定フレームが<strong>1回おきに1つずれ</strong>、山の高さが 0.000126 ユニット動いた。
  • だから合格ラインは「同じ」ではなく<strong>「この範囲に収まる」</strong>で書く。
  • エディターと Web ビルド(IL2CPP・wasm)でも<strong>記録は一致</strong>。 てっぺんの差は 0.0003 ユニット。
  • Physics2D.Simulate で時間を自分で進めると、物理のテスト5件が<strong>0.13秒</strong>で終わる。
目次8
検証した環境最終確認 2026年9月20日
  • Windows 11
  • Unity 6.6(6000.6.0f1)
  • Physics2D 既定の固定 50Hz(Fixed Timestep 0.02)
  • Claude Code(Opus 5 / 思考レベル 高)

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

5本目まで、当たり判定も移動も全部自分で計算していました。だからテストは簡単でした。同じ種を渡せば同じ結果が出ます。

6本目で初めて Unity の物理(Physics2D)を使いました。落ちて、ぶつかって、転がる。ここで最初に確かめたのは、遊びの面白さではなくこれです。

同じ操作をしたら、同じ結果になるのか。

ならないなら、リプレイも作れませんし、テストの書き方も変えなければいけません。

5回

同じ種で回した回数

同じ入力の規則

一致

置いた数・記録(cm)

5回とも同じ

1フレーム

着地判定のずれ

1回おきに発生

0.13秒

物理テスト5件の実行

実時間を待たない

カサネのプレイ画面。台座の上に色のちがう石が7つ積み上がり、1つが右へ転げ落ちている
6本目「カサネ」。落下と衝突は Unity の物理に任せている

測り方:時間を自分で進める

実時間を待つと、60秒のプレイを5回5分かかります。そこで物理の進め方を切り替えました。

テストの中で物理を自分で進める
Physics2D.simulationMode = SimulationMode2D.Script;
Physics2D.gravity = new Vector2(0f, -11f);

for (int f = 1; f <= 3000; f++)
{
  var obs = pieces.Observe(0.02f);   // 物理の結果を読む
  game.Tick(0.02f, input, obs);      // ルールを1ステップ進める
  Physics2D.Simulate(0.02f);         // 物理を1ステップ進める
}

入力も規則で決めます。「吊り下げがまん中に来たら離す」。種が同じなら形も揺れ方も同じなので、この規則だけで入力列も必ず同じになります。人の手を借りずに、同じプレイを何度でも作れます。

ゲームのルールも 物理と同じ 0.02 秒刻みFixedUpdate)で進めます。Update で進めると、フレームの間隔がばらついた瞬間に「同じ入力」が成り立たなくなります。

結果:記録は一致、フレームは1回おきにずれる

同じ種(4242)で5回続けて回しました。

こうすると壊れる

こうする

離したフレーム山のてっぺん
186,149,263,441,618-6.7995430
286,149,263,441,617-6.7994170
386,149,263,441,618-6.7995430
486,149,263,441,617-6.7994170
586,149,263,441,618-6.7995430

きれいに2つの状態を行き来しています。奇数回どうし・偶数回どうしは、小数第7位までぴったり同じでした。

差は 0.000126 ユニット。このゲームの表示に直すと 0.00126 cm です。それでも「止まった」と判定されるフレームが1つずれ、次に手を離せるようになるタイミングまで動きました。

⚠️ 注意

1回のプレイのでは完全に再現します。ずれるのは、前のプレイを片づけて次を始めたときです。原因は確かめられていません(作り直しの順番が影響していると見ていますが、Unity の内部は見ていないので推測です)。

エディターとブラウザでも比べた

ここまでは全部エディター(Mono・x64)の話です。実際に遊ばれるのは Web ビルド(IL2CPP・wasm)なので、同じ手順をどちらでも動かせる入口をゲームに作りました。?probe=1 を付けて開くと、同じ種・同じ入力で3回回して結果をコンソールに出すだけのモードです。

エディター(Mono・x64)Web ビルド(IL2CPP・wasm)
離したフレーム86, 149, 263, 441, 61786, 149, 262, 440, 617
山のてっぺん-6.7994170-6.7991150
置けた数・落とした数2 個・3 個2 個・3 個
記録12 cm12 cm

環境が変わっても、記録は同じだった。

ずれたのは中間の操作フレーム(1フレーム)と、てっぺんの座標 0.000302 ユニット(0.003cm)です。同じエディターの中で回したときのずれ(0.000126)より大きいものの、桁は同じでした。

Web ビルドの側でも、同じプロセスの中で2つの状態を行き来します(差 0.000038)。エディターで見た現象は、環境を変えても消えませんでした。

そこで、合格ラインをこう書いた

「同じ結果になること」を条件にすると、このテストは1回おきに落ちます。落ちるテストは、そのうち誰も見なくなります。

実際の合格ライン(PlayMode テスト)
Assert.AreEqual(a.Placed, b.Placed, "置けた数は変わらない");
Assert.AreEqual(a.Misses, b.Misses, "落とした数は変わらない");
Assert.AreEqual(a.HeightCm, b.HeightCm, "記録(cm)は変わらない");
Assert.AreEqual(a.TopY, b.TopY, 0.01f, "山のてっぺんの差は 1cm 未満");

測った値(0.000126)より2桁広い 0.01 を許容にしました。これなら普段は落ちず、設計を壊すような変更をしたときには落ちます。

物理を「1か所」に閉じ込める

もうひとつ決めたのは、置き場所です。ルールの側は今までどおりエンジンから切り離し、物理の結果だけを外から受け取る形にしました。

ルール側が受け取るのは、この4つだけ
public readonly struct Observation
{
  public readonly float TopY;         // 積み上がった山のてっぺん
  public readonly float ActiveX;      // いま落としている塊の位置
  public readonly float ActiveY;
  public readonly bool ActiveAsleep;  // その塊が止まったか
  public readonly bool AllAsleep;     // 山全体が止まったか
}

こうすると、段階の切り替え・スコア・ミスの判定は物理なしでテストできます。「こう観測された」を手で渡すだけです。実際、ルール側のテスト33件4.3 秒で終わり、シーンを1つも開きません。物理そのものは PlayMode の5件だけで見ます。

ルール側(段階・スコア・ミス・揺れ)はエンジンを参照しない場所に置く
物理の結果は、決まった形の観測値としてだけ渡す
物理のテストは Physics2D.Simulate で時間を自分で進める
合格ラインは「一致」ではなく「範囲」で書く

つまずいたところ

undefined

症状
塊を吊り下げた位置から落としたはずが、毎回ちがう場所から落ちてくる
原因
使い回すために隠していた(SetActive(false))ゲームオブジェクトに、位置を入れていた。隠れている間は物理の体が無いので、入れた値は捨てられる
対処
先に有効にしてから位置・角度・速度を入れる。順番を入れ替えただけで直った

最初は原因が見えず、「物理が暴れている」と思い込んでいました。落とした位置と着地の位置を1行ずつ出して並べたら、どの塊も同じ場所から落ちていることが分かり、そこで気づきました。

undefined

症状
高さの判定が、見た目をなめらかにした瞬間にぶれ始めた
原因
当たり判定の箱(collider.bounds)から高さを読んでいた。補間(Interpolate)を入れると、描画用の位置は物理の位置と少しずれる
対処
物理の位置と角度から、頂点を回して自分で計算する。描画の設定に影響されなくなった

この方法が向いているもの・向いていないもの

こうすると壊れる

こうする

動くものをエンジン抜きでテストする話はUnityのゲームロジックをテストする方法に、テストとビルドを AI に回させる手順はAIにUnityのテストとビルドを実行させるに書きました。

FAQ

よくある質問

物理を使うゲームは、自動テストできませんか?

できます。ただし「同じ結果になること」を合格ラインにすると落ちます。置けた数・記録・最終的な高さのように、小さなずれを吸収する値で判定します。

テストのたびに再生モードへ入ると時間がかかりませんか?

Physics2D.simulationModeScript にして Physics2D.Simulate(0.02f) を自分で呼べば、実時間を待たずに進みます。60秒ぶんのプレイも一瞬で終わります。

入力だけを保存するリプレイは作れますか?

「何フレーム目で操作したか」は保存できますし、そこは毎回一致しました。ただし再生した結果が完全に同じになるとは限らないので、記録の検証には使えません。

どうして1回おきにずれるのですか?

確かめられていません。前のプレイの物体を片づけてから次を始めているので、作り直しの順番が影響していると考えていますが、Unity の内部までは見ていないので推測です。

エディターとブラウザで結果は変わりますか?

変わりました。同じ種・同じ入力で、着地の判定が1フレームずれ、山のてっぺんが 0.0003 ユニット動きました。ただし置けた数と記録(cm)は一致しました。

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

NEXT