4 - 物理
概要
Fusion 104では、サーバーオーソリテーティブゲームでFusionがどのようにPhysXと関わっていくのかを検証します。
このセクションの最後には、プロジェクトでプレイヤーが物理制御のあるボールをスポーンしたり使用したりできるようになります。
セットアップ
デフォルトで、Fusionはホスト上で物理シミュレーションを実行し、クライアントが後に続きます。ただし、これにはローカルの予測オブジェクトと比較して別の時間に物理オブジェクトが配置され、これらのオブジェクトが衝突すると物理シミュレーションに問題が生じます。
デュアル時間は複雑なトピックで、何通りかの扱われ方をし、それらはダウンライトハッキングであったり一般的なアプリケーションには高価すぎるものであったり、ゲーマーが期待する即時のフィードバックを提供しないものであったりします。全てのケースに通じるシンプルな修正は、残念ながら存在しません。
このチュートリアルでは、Fusionにくみこまれている機能を使用して、物理コントロールオブジェクトをプレイヤーコントロールアバターと同じ時間に配置し、物理オブジェクトをローカルに予測しています。これはかなりしっかりした修正(PhysXが決定性でないことは置いておき)ですが、物理エンジンがシミュレーションごと(ティックごとに数回などの頻度)に稼働しなければならないため、高価なものとなっています。
この機能を有効にするには、Fusionのメニューに進み、Network Project Config
を選択してください。するとインスペクターでFusionのコンフィグファイルが開きます。Server Physics Mode
の下でClient Prediction
を選択してください。
物理オブジェクト
ネットワークされ、PhysX制御されているオブジェクトのプレハブは通常のUnity PhysXオブジェクトと同様のRigidbody
を使用しますが、別のFusionコンポーネントでNetworkRigidbody
というビジュアル子オブジェクトを同期します。これは、ネットワーキングが続く限り、NetworkTransform
に置き換わります。
- Unity Editorで空の新しいGameObjectを作成する
GameObject
の名前をPhysxBall
にする- 新しい
NetworkRigidbody
コンポーネントを追加する - Fusionが
NetworkObject
コンポーネントが欠けているという警告を表示するので、続けてAdd Network Object
を押す。PhysXシミュレーションで必要なため、Fusionは自動的に通常のUnity RigidBodyも追加する Interpolation Data Source
をPredicted
へ変更しWorld Space
に設定する- Sphereの子を
PhysxBall
に追加する - 子のサイズを全方向において0.2まで下げる
- 子オブジェクトを親オブジェクトのNetworkTransformコンポーネントの
InterpolationTarget
内にドラッグする - Sphereからコライダーを削除する
- 親オブジェクトで新しいsphereコライダーを、子オブジェクトを完全に覆うように半径0.1で作成する
- ゲームオブジェクトに新しいスクリプトを追加し、
PhysxBall.cs
と名前を付ける - 全てのオブジェクトをプロジェクトフォルダにドラッグしプレハブを作成する
- シーンを保存してネットワークオブジェクトをベイクしシーンからプレハブインスタンスを削除する

PhysxBallスクリプト
ボールがPhysX主導であり、NetworkRigidbodyがネットワークデータを考慮するので、処理する特別なコードの必要が親戚にあたるキネマティックより少なくなります。PhysxBall.cs
に追加する必要があるのは、数秒経った後にボールをデスポーンするタイマー(キネマティックなボールと完全に同様)と、初期前進速度を設定するメソッドだけです。
これらは両方ともInit()
メソッドでカバーされます。以下を参照してください。:
using UnityEngine;
using Fusion;
public class PhysxBall : NetworkBehaviour
{
[Networked] private TickTimer life { get; set; }
public void Init(Vector3 forward)
{
life = TickTimer.CreateFromSeconds(Runner, 5.0f);
GetComponent<Rigidbody>().velocity = forward;
}
public override void FixedUpdateNetwork()
{
if(life.Expired(Runner))
Runner.Despawn(Object);
}
}
インプット
ボールをスポーンするには、キネマティックボールの場合と同じ3ステップに従ってコードを拡張する必要がありますが、代わりに第2のマウスボタンを使用するように変更しています。以下で見ていきましょう。
1. NetworkInputData
NetworkInputData.cs
で新しいボタンフラグを追加する:
using Fusion;
using UnityEngine;
public struct NetworkInputData : INetworkInput
{
public const byte MOUSEBUTTON1 = 0x01;
public const byte MOUSEBUTTON2 = 0x02;
public byte buttons;
public Vector3 direction;
}
2. BasicSpawner
BasicSpawner.cs
で、初めと同じ方法で第2のマウスボタンをポーリングし、以下の様にフラグを設定する:
private bool _mouseButton0;
private bool _mouseButton1;
private void Update()
{
_mouseButton0 = _mouseButton0 || Input.GetMouseButton(0);
_mouseButton1 = _mouseButton1 || Input.GetMouseButton(1);
}
public void OnInput(NetworkRunner runner, NetworkInput input)
{
var data = new NetworkInputData();
...
if (_mouseButton1)
data.buttons |= NetworkInputData.MOUSEBUTTON2;
_mouseButton1 = false;
input.Set(data);
}
3. Player
Player.cs
は、実際にボール振れハブをスポーンするコードを保持するため、以下のプレハブリファレンスに加えて、
[SerializeField] private PhysxBall _prefabPhysxBall;
Player
はスポーンを呼び出して、前に作成しておいたInit()
メソッドを使用して速度(前回の前進方向に乗算した定数)を設定する必要がある
public override void FixedUpdateNetwork()
{
if (GetInput(out NetworkInputData data))
{
data.direction.Normalize();
_cc.Move(5*data.direction*Runner.DeltaTime);
if (data.direction.sqrMagnitude > 0)
_forward = data.direction;
if (delay.ExpiredOrNotRunning(Runner))
{
if ((data.buttons & NetworkInputData.MOUSEBUTTON1) != 0)
{
delay = TickTimer.CreateFromSeconds(Runner, 0.5f);
Runner.Spawn(_prefabBall,
transform.position+_forward,
Quaternion.LookRotation(_forward),
Object.InputAuthority,
(runner, o) =>
{
// Initialize the Ball before synchronizing it
o.GetComponent<Ball>().Init();
});
}
else if ((data.buttons & NetworkInputData.MOUSEBUTTON2) != 0)
{
delay = TickTimer.CreateFromSeconds(Runner, 0.5f);
Runner.Spawn(_prefabPhysxBall,
transform.position+_forward,
Quaternion.LookRotation(_forward),
Object.InputAuthority,
(runner, o) =>
{
o.GetComponent<PhysxBall>().Init( 10*_forward );
});
}
}
}
}
最後に、新しいボールのテストのため、作成したプレハブをPlayer
プレハブのPrefab Physx Ball
フィールドに割り当て、ビルドして実行します。