This document is about: FUSION 2
SWITCH TO

概要

概要

Physicsアドオンには、Rigidbody/Rigidbody2Dコンポーネントの同期とクライアントサイド予測に必要なコンポーネントが含まれています。

  • NetworkRigidbody3D:状態権限者のRigidbodyの状態を、他のピアに同期します
  • NetworkRigidbody2D:状態権限者のRigidbody2Dの状態を、他のピアに同期します
  • RunnerSimulatePhysics3DNetworkRunnerのゲームオブジェクトに追加されるコンポーネントで、Fusionの3D物理シミュレーションを処理します
  • RunnerSimulatePhysics2DNetworkRunnerのゲームオブジェクトに追加されるコンポーネントで、Fusionの2D物理シミュレーションを処理します

使用方法

NetworkRigidbodyコンポーネントを使用するためには、以下の手順に従ってください。

  1. Rigidbodyが付いたプレハブ、またはシーンオブジェクトに、NetworkObjectNetworkRigidbody3D(2DならNetworkRigidbody2D)を追加してください。このコンポーネントは、状態権限者のRigidbodyの状態を他のピアへ複製したり、Rigidbodyに対するクライアントサイド予測・ロールバック・再シミュレーションを処理します。
  2. NetworkRunnerプレハブに、RunnerSimulatePhysics3D(またはRunnerSimulatePhysics2D)を追加してください。このコンポーネントが、UnityのPhysics.Simulate()を引き継ぐようになります。
  3. すべてのシミュレーションコードが、FixedUpdateNetwork()内で実行されていることを確認してください。

Fusion 1 から Fusion 2 への移行

- IBeforePhysicsStep/IAfterPhysicsStepコールバックは削除され、RunnerSimulatePhysicsのコールバックに置き換えられました。後述のコールバックをご覧ください。

拡張 / 修正

NetworkRigidbodyRunnerSimulatePhysicsコンポーネントは、多くのケースで動作するように設計されています。しかし、プロジェクト固有のニーズに合わせて、これらを修正することもできます。クラスのいくつかの仮想メンバーはオーバーライドすることができますが、コンポーネントを参考にして独自クラスを作成したり、コンポーネントを直接修正したりする方が多いかもしれません。Fusionは、これらのコンポーネントに全く依存していないため、自由に修正しても構いません。

NetworkRigidbody2D / NetworkRigidbody3D

NetworkRigidbody3DNetworkRigidbody2Dは、NetworkTRSPクラスを継承しているため、すべての関心領域(AOI)処理が有効です。関心領域の仕様については、マニュアルのNetworkTRSPをご覧ください。

スケールの同期

有効にすると、transform.localScaleが同期されます。

親子関係の同期

有効にすると、transform.parentが同期されます。Rigidbodyの親を持つRigidbodyは、キネマティックに設定されます。親子関係を設定したRigidbodyAreaOfInterestOverrideは、自動的に親のネットワークオブジェクトに設定されます。

親子関係の同期には、いくつかの注意点があります。

  • 親のtransformには、必ずNetworkBehaviourコンポーネントが必要です。親を見つけるために、NetworkBehaviourIdが使用されるからです。
  • NetworkRigidbodyは、ルートのネットワークオブジェクトにあることが必須です。(通常はそうなります)
  • 親のtransformを、ネットワークオブジェクトの子のtransformにすることができます。プレイヤーの手などが例です。

Interpolation Target

指定したtransformが、Render()間の補間に使用されます。これは通常、Rigidbodyの、Colliderを持たない子要素のtransformになります。

値をnullにすると、ルートのNetworkRigidbodyが補間に使用され、シミュレーションループ開始時にティック精度の位置が返るようになります。InterpolationTargetの値は、実行時にコードから変更することもできます。

子要素に対してInterpolationTargetを使用する利点は、補間によって物理演算のキャッシュがクリアされないことです。ただし、子要素のRigidbodyのスケールの同期が無効になります。

Rigidbodyで予期しない(摩擦や積み重ねの)挙動が発生することを避けるため、InterpolationTargetnullにしておくことを推奨します。

InterpolationTargetが必要なケースは、以下の通りです。

  • MovePosition()を使用してキネマティックRigidbodyを動かす場合: InterpolationTargetを使用しない場合、transform.positiontransform.rotationの更新によって補間が行われます。transformを直接変更すると、RigidbodyMovePosition()の呼び出しが上書きされてしまいます。
  • 補間の影響を受けずに物理演算を行う必要がある場合: Transformを直接移動させる補間は、Rigidbodyの静止摩擦やスリープモードなどに副作用を及ぼします。これはRender Sleep Thresholdsで軽減できますが、それでも許容できない干渉が発生することがあります。

InterpolationTargetを使用する場合は、Rigidbodyに干渉しないように、指定するオブジェクトにはColliderが含まれないことを確認してください(干渉を避けるためのInterpolationTargetだからです)。すべてのビジュアルエフェクトは、InterpolationTargetの方に合わせてください。

備考: スケールと親子関係の同期を同時に使用している場合、InterpolationTargetを使用すると正しい結果になりません。ゲームオブジェクトのスケールは、その親のスケールに影響を受けるため、コードで正しく複製することはほぼ不可能です。スケールの結果を正確に得るため、InterpolationTargetnullのままにしておくことを推奨します。

備考: カメラをInterpolationTargetに追従させていないのは、よくある間違いです。カメラをInterpolationTargetに追従させることで、カメラの移動を補間できます。

Sleep Thresholds

有効にすると、transformの変化量が、指定したすべてのしきい値を下回った時に、ルートのtransformの補間が行われなくなります。これによって、ルートのtransformが移動することによる影響(物理演算のキャッシュのクリアや、スリープの解除)を軽減できます。スリープは、サーバーの権限を持つピアにとって特に重要です。オブジェクトがほんの少し動いただけでもネットワークトラフィックが発生するため、Rigidbodyをスリープさせられるようにすることで、ネットワークトラフィックが大きく減少します。

備考: Sleep Thresholdsは、InterpolationTargetを使用していない場合のみ適用されます。

Use Render Sleep Thresholds

しきい値のチェックを有効にします。無効にすると、オブジェクトでは常に補間が行われます。

Render Thresholds

  • Use Energy: Rigidbodyの速度と角速度のエネルギーがしきい値を超えていたら、補間が行われます。
  • Position: transformの位置の変化量がこの値を超えていたら、補間が行われます。値が0なら、テストはスキップされます。
  • Rotation: transformの回転の変化量がこの値を超えていたら、補間が行われます。値が0なら、テストはスキップされます。
  • Scale: transformのスケールの変化量がこの値を超えていたら、補間が行われます。値が0なら、テストはスキップされます。

Teleport()

NetworkTransform.Teleportをご覧ください。

MovingTeleport()

テレポート移動を開始します。このメソッドは、RunnerSimulatePhysics3D/RunnerSimulatePhysics2Dで物理シミュレーションが行われる前に、FixedUpdateNetwork()内で呼び出す必要があります。テレポートは物理シミュレーション後まで延期され、物理シミュレーション前後の位置・回転の値がキャプチャされます。これによって、正しいテレポート前からテレポート先への補間が繋がるようになります。これは標準的なTelerport()の別バージョンで、補間が1ティック停止します。

RunnerSimulatePhysics2D / RunnerSimulatePhysics3D

NetworkRunnerプレハブに追加されるこれらのコンポーネントで、どのように物理シミュレーションを処理したいかを指定できます。コンポーネントは、適切な物理のSimulate()を、FixedUpdateNetwork()ごとに呼び出します。

Physics Authority

FusionがこのコンポーネントからPhysics.Simulate()/Physics2D.Simulate()を呼び出すようにするか、Unityの物理設定(自動またはスクリプト)のままにしておくかどうかを指定します。

Autoを指定すると、以下のケースでFusionがSimulate()を呼び出すようになります。

  • 共有モードの以外のモードで、ゲームを実行している場合。共有モードはデフォルトで、FixedUpdateNetwork()外でオブジェクトを動かすことを想定しています。FixedUpdateNetwork()内でネットコードを適用する場合、このコンポーネントを追加してPhysicsAuthorityFusionに設定する必要があります。
  • マルチピアモードを有効にしている場合。Unityは2つ目以降の物理シーンを自動でシミュレーションしないため、マルチピアモードを使用する際には、FusionがSimulate()呼び出しを制御する必要があります。

Physics Timing

Physics AuthorityFusionの場合、物理演算の実行タイミングを指定します。

Client Physics Simulation

クライアント上の物理シミュレーションを制御します。選択肢は以下の通りです。

  • Disabled: クライアント上で物理シミュレーションは実行されず、transformも同期されません。
  • SyncTransforms: すべてのティックでUnityEngine.Physics.SyncTransform()が呼び出されます。
  • SimulateForward: 再シミュレーションのティックではUnityEngine.Physics.SyncTransform()が呼び出され、順方向のティックではUnityEngine.Physics.Simulate()が呼び出されます。
  • SimulateAlways: すべてのティックでUnityEngine.Physics.Simulate()が呼び出されます。

⚠️ サーバーは常に物理シミュレーションを行います。共有モードでは、順方向のティックのみでシミュレーションが行われるため、SimulateForwardSimulateAlwaysの挙動は同じになります。

DeltaTime Multiplier

物理のSimulate()に渡される乗数値です。時間を加速させたい場合は1より大きい値を渡し、時間を遅くしたい場合は0~1の値を渡します。

Set FixedTimestep

有効にすると、UnityのFixedTimestepの値が、FusionのDeltaTimeに一致するように設定されます。これによって、FixedUpdate内のコードを、Fusionのティックとほぼ連動して動作させることができます。

重要: FixedUpdate()FixedUpdateNetwork()が完全に連動することは決してありません。まず検討すべきなのは、これら2つの実行タイミングを同時に使用するのを避けることです。通常、FusionのすべてのシミュレーションコードはFixedUpdateNetwork()内に記述し、共有モードでは例外として、必要に応じてUpdate()で変更を行うことがあります。

コールバック

Fusion 2ではINetworkRunnerCallbacks.IBeforePhysicsStepINetworkRunnerCallbacks.IAfterPhysicsStepが削除され、以下のように置き換えられました。

OnBeforeSimulate / OnAfterSimulate

登録されたこれらのメソッドは、RunnerSimulatePhysicsがシミュレーションを行うたびに呼び出されます。

C#

using Fusion;
using Fusion.Addons.Physics;
using UnityEngine;

public class FusionPhysicsAddonExample : NetworkBehaviour 
{
  private RunnerSimulatePhysics3D _physicsSimulator;
  
  public override void Spawned() 
  {
    // Get our RunnerSimulatorPhysics instance (this needs to be added to the Runner)
    _physicsSimulator = Runner.GetComponent<RunnerSimulatePhysics3D>();
    
    // Register callback for EVERY simulation tick
    _physicsSimulator.OnBeforeSimulate += OnBeforeEverySimulate;
  }
  
  public override void Despawned(NetworkRunner runner, bool hasState) 
  {
    // Unregister (a good practice)
    _physicsSimulator.OnBeforeSimulate -= OnBeforeEverySimulate;     
  }
  
  void OnBeforeEverySimulate() { 
    // Implement code to execute before every Physics.Simulate
  }
}

QueueBeforeSimulationCallback / QueueAfterSimulationCallback

これらのコールバックは、1度限りのコールバックとしてキューに入れられ、次にシミュレーションが行われた際にトリガーされます。

C#

using Fusion;
using Fusion.Addons.Physics;
using UnityEngine;

public class FusionPhysicsAddonExample : NetworkBehaviour 
{
  private RunnerSimulatePhysics3D _physicsSimulator;
  
  public override void Spawned() 
  {
    // Get our RunnerSimulatorPhysics instance (this needs to be added to the Runner)
    _physicsSimulator = Runner.GetComponent<RunnerSimulatePhysics3D>
  }

  public override void FixedUpdateNetwork() 
  {
    if (_physicsSimulator.HasSimulatedThisTick) 
    {
      Debug.LogWarning($"Component is running FixedUpdateNetwork AFTER Physics Simulation, check my Script Exec Order");
      PostSimulateActivity();
    } else {
      // Queue our method for a one time deferred callback
      _physicsSimulator.QueueAfterSimulationCallback(PostSimulateActivity);
    }
  }

  void PostSimulateActivity() { 
    // Implement code to execute after specific Physics.Simulate calls
  }
}

既知の問題

  • Rigidbodyの物理挙動は(キネマティックに設定されていても)予測困難で、Rigidbodyがシミュレーション前の位置にあったかのような形で、オブジェクトと衝突することがあります。そのため子要素は、Colliderを無効にし、コリジョンには依存させないことを推奨します。
  • 子要素のスケールは、InterpolationTargetを持たないすべての親要素に依存します。子要素のスケーリングとInterpolationTargetを組み合わせて使用したい場合は、InterpolationTargetをルートから切り離し、親子関係を再設定する独自のコードを記述する必要があります。基本的には、すべてのRigidbodyCollider/Transform階層(InterpolationTargetを含む)のコピーを作成します。
  • MovePosition()MoveRotation()は、InterpolationTargetを使用する必要があります。ただし、transform.position/transform.rotationの値を直接設定して、キネマティックRigidbodyを動かすなら、その必要はありません。
Back to top