This document is about: FUSION 1
SWITCH TO

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

4 - 物理

概要

Fusion 104では、サーバー主導のゲームでFusionがどのようにPhysXに作用するのかを検証します。

このセクションの最後には、プロジェクトでプレイヤーが物理的な挙動をするボールをスポーンして相互作用できるようになります。

本トピックの詳細説明についてはマニュアルを参照ください

セットアップ

デフォルトでは、Fusionはホスト上で物理シミュレーションを実行し、クライアントはそれに従います。しかし、ローカルで予測しているオブジェクトと異なる時間におかれた物理オブジェクトが配置されて、それらのオブジェクトが衝突すると物理シミュレーションに問題が発生します。

「時間差」の解決は複雑なトピックです。うまくごまかしたり、一般的なアプリケーションでは負荷が高すぎる処理であったり、プレイヤーへの即時のフィードバックを諦めたり、様々な方法で取り扱われます。残念ながら、あらゆるケースで有効なシンプルな解決方法は存在しません。

このチュートリアルでは、物理オブジェクトをローカルで予測するFusionの機能を使って、物理的な挙動をするオブジェクトとプレイヤーが操作するアバターを同じ時間におきます。これは実用的で堅牢なソリューションですが、PhysXで予測と再シミュレーション(ティックごとに複数回)を実行するのは負荷が高いことを覚えておいてください。

この機能を有効にするには、Fusionのメニューに進み、Network Project Configを選択してください。するとインスペクターでFusionのコンフィグファイルが開きます。そこでServer Physics ModeClient Predictionを選択してください。

物理オブジェクト

PhysXで動作するネットワークオブジェクトは、通常のUnityのオブジェクトと同じRigidbodyを使用しますが、NetworkRigidobyと呼ばれるオブジェクトを同期するFusionのコンポーネントも持ちます。NetworkRigidbodyNetworkTransformを代替します。

  1. Unity Editorで空の新しいGameObjectを作成する
  2. GameObjectの名前をPhysxBallにする
  3. 新しいNetworkRigidbodyコンポーネントを追加する
  4. FusionがNetworkObjectコンポーネントが足りないという警告を表示するので、続けてAdd Network Objectを押す。Fusionは自動的にPhysXのシミュレーションで必要な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がネットワーク上のデータの面倒を見てくれるので、Kinematicなボールよりも記述する必要があるコード量は少なくなります。PhysxBall.csに追加する必要があるのは、数秒後にボールをデスポーンするタイマー(Kinematicなボールと全く同じもの)と、初速度を設定するメソッドだけです。

これらは両方ともInit()メソッドでカバーされます。以下を参照してください。

C#

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);
  }
}

インプット

ボールをスポーンするには、Kinematicなボールの場合と同じ3ステップに従って、コードを記述する必要がありますが、かわりにマウス右ボタンを使用するように変更しています。以下で見ていきましょう。

1. NetworkInputData

NetworkInputData.csで新しいボタンフラグを追加する。

C#

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で、初めと同じ方法でマウス右ボタンをポーリングし、以下の様にフラグを設定する。

C#

  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は、実際にボールのプレハブをスポーンするコードを保持するため、以下のようにプレハブの参照を加える。

C#

[SerializeField] private PhysxBall _prefabPhysxBall;

Playerは事前に作成したInit()メソッドを使って、スポーンと速度の設定(向いている方向に定数を乗算しただけのもの)をする必要があります。

C#

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

Back to top