このページは編集中です。更新が保留になっている可能性があります。

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に置き換わります。

  1. Unity Editorで空の新しいGameObjectを作成する
  2. GameObjectの名前をPhysxBallにする
  3. 新しいNetworkRigidbodyコンポーネントを追加する
  4. FusionがNetworkObjectコンポーネントが欠けているという警告を表示するので、続けてAdd Network Objectを押す。PhysXシミュレーションで必要なため、Fusionは自動的に通常のUnity RigidBodyも追加する
  5. Interpolation Data SourcePredictedへ変更しWorld Spaceに設定する
  6. Sphereの子をPhysxBallに追加する
  7. 子のサイズを全方向において0.2まで下げる
  8. 子オブジェクトを親オブジェクトのNetworkTransformコンポーネントのInterpolationTarget内にドラッグする
  9. Sphereからコライダーを削除する
  10. 親オブジェクトで新しいsphereコライダーを、子オブジェクトを完全に覆うように半径0.1で作成する
  11. ゲームオブジェクトに新しいスクリプトを追加し、PhysxBall.csと名前を付ける
  12. 全てのオブジェクトをプロジェクトフォルダにドラッグしプレハブを作成する
  13. シーンを保存してネットワークオブジェクトをベイクしシーンからプレハブインスタンスを削除する
Ball Prefab
Ball Prefab

トップに戻る
 

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フィールドに割り当て、ビルドして実行します。

ドキュメントのトップへ戻る