This document is about: FUSION 2
SWITCH TO

3 - 移動とカメラ

概要

ここでは、プレイヤーの入力に基づくプレイヤーの移動と、適切なプレイヤーオブジェクトに追従する一人称視点のカメラを、前章のシーンに追加します。

プレイヤーの移動

Fusionでは、毎ティック更新するゲームプレイのコード(プレイヤーの移動など)をUpdateFixedUpdateで実行すべきではありません。かわりにFixedUpdateNetworkを使用すべきです。このメソッドによって、全てのクライアント上で移動がスムーズに正しく補間されるようになります。

新規でスクリプトを作成して、名前をPlayerMovementにし、以下のコードを追加してください。

C#

using Fusion;
using UnityEngine;

public class PlayerMovement : NetworkBehaviour
{
    private CharacterController _controller;

    public float PlayerSpeed = 2f;

    private void Awake()
    {
        _controller = GetComponent<CharacterController>();
    }

    public override void FixedUpdateNetwork()
    {
        // Only move own player and not every other player. Each player controls its own player object.
        if (HasStateAuthority == false)
        {
            return;
        }

        Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")) * Runner.DeltaTime * PlayerSpeed;

        _controller.Move(move);

        if (move != Vector3.zero)
        {
            gameObject.transform.forward = move;
        }
    }
}

MonoBehaviourのかわりにNetworkBehaviourを継承することで、FixedUpdateNetworkとネットワークプロパティ(詳細は後述)が使用できるようになります。

HasStateAuthorityは、クライアントがオブジェクトを操作できるかどうかをチェックするために使用されます。NetworkTransformは状態権限を持つクライアントの変更のみを同期します。状態権限を持たないクライアントがオブジェクトの位置を変更しても、その変更はローカル上のみの変更となり、その後に状態権限を持つクライアントからのデータを受信した際に上書きされます。

FixedUpdateNetwork内では、必ずRunner.DeltaTimeを使用してください。

ボタン押下の処理

GetButtonDownなどはUnityのUpdateで1フレームのみ反応します。Fusionではゲームプレイのコード(プレイヤーの移動など)をFixedUpdateNetworkで実行するため、ボタン押下状態を正確に取得するための特別な処理が必要になります。

ボタン押下状態の取得の問題は、FixedUpdateでも発生する問題と同様です。

FixedUpdateNetworkで正確な入力を取得するには、複数のアプローチがあります。

  1. Updateでボタン押下状態を取得、結果をboolで保存し、ゲームロジックに使用する。その後のFixedUpdateNetworkの最後に取得結果をクリアする。
  2. Fusionのネットワーク入力で、NetworkButtonsを使用する。
  3. 新しいUnityのInput Systemを使用して、Update ModeManual Updateに設定、FixedUpdateNetworkInputSystem.Updateを呼び出す。
上記オプションは共有モードでのみ適用できます。ホストモードでは必ずネットワーク入力を使用してください。

ジャンプ

ボタン押下を処理する例として、プレイヤーのジャンプを実装してみましょう。今回は、前節のリストのオプション1を使用します。

PlayerMovenetのコードを、以下のように書き換えてください。

C#

using Fusion;
using UnityEngine;

public class PlayerMovement : NetworkBehaviour
{
    private Vector3 _velocity;
    private bool _jumpPressed;
    
    private CharacterController _controller;

    public float PlayerSpeed = 2f;
    
    public float JumpForce = 5f;
    public float GravityValue = -9.81f;

    private void Awake()
    {
        _controller = GetComponent<CharacterController>();
    }
    
    void Update()
    {
        if (Input.GetButtonDown("Jump"))
        {
            _jumpPressed = true;
        }
    }

    public override void FixedUpdateNetwork()
    {
        // Only move own player and not every other player. Each player controls its own player object.
        if (HasStateAuthority == false)
        {
            return;
        }
        
        if (_controller.isGrounded)
        {
            _velocity = new Vector3(0, -1, 0);
        }
        
        Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")) * Runner.DeltaTime * PlayerSpeed;

        _velocity.y += GravityValue * Runner.DeltaTime;
        if (_jumpPressed && _controller.isGrounded)
        {
            _velocity.y += JumpForce;
        }
        _controller.Move(move + _velocity * Runner.DeltaTime);

        if (move != Vector3.zero)
        {
            gameObject.transform.forward = move;
        } 
        
        _jumpPressed = false;
    }
}

このコードでは、ジャンプ処理と、KCC(キネマティックキャラクターコントローラー)に渡す速度と重力の計算を追加しています。

_jumpPressed変数はボタン押下状態を保存するために使用されます。ボタン押下状態は、判定ミスが起こらないようにUpdateでポーリングされ、FixedUpdateNetworkの最後にリセットされます。

C#

if (_controller.isGrounded)
{
    _velocity = new Vector3(0, -1, 0);
}

上記のコードで、小さな斜面上を歩いている時でも地面に着くようにすることで、ジャンプ中のみプレイヤーが地面から離れるようにします。

テストの実行

PlayerMovementが完成したら、Unityエディターに戻り、PlayerCharacterプレハブにPlayerMovementコンポーネントを追加します。

プレイヤーの移動がテストできるようになったので、ビルドして二つのクライアントを実行してみましょう。

File > Build Settingsで現在のシーンをリストに追加して、ウインドウ左下のPlayer Settingsボタンを押してください。

Player Settingsでは、Resolution and PresentationFullscreen Modeに設定して、Run In Backgroundにチェックを入れてください。

download the sdk
Player Settingsの設定

Build Settingsに戻り、Build and Runを押してビルドを作成します。ビルドが完了したら、Unityエディター上でもゲームを実行すると、二つのクライアントが立ち上がることになります。

両方のクライアントでStart Shared Clientボタンを押します。プレイヤーが二人表示され、それぞれが各クライアントで操作できます。歩き回ったりジャンプしたりできるか確認してみてください。

カメラ

プレイヤーのセットアップを完成させるため、カメラを追加しましょう。

カメラ設定には何通りかの方法(三人称、一人称など)があり、実装方法も異なります。マルチプレイゲームでは、ローカルプレイヤーオブジェクトに追従するカメラが一つあることが望ましい場合が多いです。一般的な実装は、以下の二つです。

  1. ローカルプレイヤーオブジェクトがスポーンされたら、カメラのインスタンスを生成する
  2. あらかじめカメラをシーン上に置いておき、ローカルプレイヤーオブジェクトがスポーンされたら、そのオブジェクトをカメラのターゲットにする

このチュートリアルでは2のアプローチを使用します。

新規スクリプトを作成して、名前をFirstPersonCameraにし、以下のコードを追加してください。

C#

using UnityEngine;

public class FirstPersonCamera : MonoBehaviour
{
    public Transform Target;
    public float MouseSensitivity = 10f;

    private float verticalRotation;
    private float horizontalRotation;
    
    void LateUpdate()
    {
        if (Target == null)
        {
            return;
        }

        transform.position = Target.position;
        
        float mouseX = Input.GetAxis("Mouse X");
        float mouseY = Input.GetAxis("Mouse Y");

        verticalRotation -= mouseY * MouseSensitivity;
        verticalRotation = Mathf.Clamp(verticalRotation, -70f, 70f);

        horizontalRotation += mouseX * MouseSensitivity;

        transform.rotation = Quaternion.Euler(verticalRotation, horizontalRotation, 0);
    }
}

これは、非常にシンプルな一人称カメラの実装です。このコードにはマルチプレイの要素が含まれていないことに注意してください。このカメラは、オブジェクトをターゲットにして動作する他のカメラ(Cinemachineなど)に置き換えることも可能です。

シーンのMainCameraFirstPersonCameraを追加します。ターゲットは実行時にコードから設定されるので、ターゲットオブジェクトは空のままで問題ありません。

add networking

カメラのターゲットを設定するために、PlayerMovementスクリプトを開いてCamera変数を追加します。

C#

public Camera Camera;

ローカルプレイヤーオブジェクトがスポーンされたら、カメラの参照を取得してターゲットを設定します。

C#

public override void Spawned()
{
    if (HasStateAuthority)
    {
        Camera = Camera.main;
        Camera.GetComponent<FirstPersonCamera>().Target = transform;
    }
}

NetworkObjectを初期化する時は、必ずSpawnedを使用するようにしてください。AwakeStartのタイミングでは、NetworkObjectがまだ正常にアクセスできるようになっていない可能性があります。

HasStateAuthorityはプレイヤーが操作するオブジェクト(ローカルプレイヤーオブジェクト)でのみtrueになります。

最後に一人称視点のゲームでは、プレイヤーキャラクターは視線方向基準で移動するので、少し調整が必要です。move変数の計算を以下のコードに置き換えてください。

C#

Quaternion cameraRotationY = Quaternion.Euler(0, Camera.transform.rotation.eulerAngles.y, 0);
Vector3 move = cameraRotationY * new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")) * Runner.DeltaTime * PlayerSpeed;

次は 共有モード入門 4 - ネットワークプロパティ

Back to top