🖥️

NGOでマルチプレイヤーを試す

に公開

前提

基本的には以下のサイトを参考にさせていただいています。

※kazuma_f様、大変有意義な記事をありがとうございます!

https://qiita.com/kazuma_f/items/907b01705e78bd199fdd

Unity 6やInput Systemで実装する際に若干異なる点があったため、それを踏まえた備忘録として記載します。補足としてご覧ください。

環境構成

  • Unity 6
  • Unity Netcode for GameObjects (NGO)
  • Input System

パッケージのインストール

kazuma_f様の記事と同様の手順でパッケージをインストールします。

NetworkManagerの作成

kazuma_f様の記事と同様の手順でNetworkManagerを作成します。

NetworkPlayerの作成

  1. NetworkPlayerPrefabを作成します
  2. Synchronize TransformをFalseに変更します(後述のNetworkTransformで制御するため)
  3. NetworkManagerのDefault Player Prefabにアタッチします

ネットワーク接続用のボタンを追加する

kazuma_f様の記事と同様の手順でボタンを追加します。

マルチプレイヤーを確認する

kazuma_f様の記事と同様の手順で動作確認を行います。

位置を同期する

Unity 6では、NetworkTransformの設定方法が変更されています。

  • NetworkTransformClientコンポーネントの作成は不要になりました
  • NetworkTransformコンポーネントのAuthority Modeを**Owner**に設定してください

Playerを移動するコードを作成する

Input Systemを使用する際の注意点

PlayerInputコンポーネントを使用すると、クライアント側で入力が正しく取得できない問題が発生しました。

解決方法: Input Action AssetからC#クラスを生成し、直接利用することで問題を解決できました。

以下はInputActionsという名前のInput Action Assetを作成した場合のコード例です:

using UnityEngine;
using Unity.Netcode;
using UnityEngine.InputSystem; 

public class PlayerMovement : NetworkBehaviour
{
    private const float Speed = 5f;
    private const float RotationSpeed = 500f;

    // 生成されたC#ラッパー
    private InputActions _actions;
    private InputAction _moveAction;
    private Vector2 _move;

    private void Awake()
    {
        _actions = new InputActions(); 
    }

    public override void OnNetworkSpawn()
    {
        if (IsOwner) EnableInput();
        else DisableInput();
    }

    private void OnEnable()
    {
        if (IsOwner) EnableInput();
    }

    private void OnDisable()
    {
        DisableInput();
    }

    private void OnDestroy()
    {
        DisableInput();
        _actions?.Dispose();
    }

    private void EnableInput()
    {
        // 作成したAction Map名に置き換えてください
        var map = _actions.Player;
        _moveAction = map.Move;

        // 入力値変更の購読
        _moveAction.performed += OnMovePerformed;
        _moveAction.canceled += OnMoveCanceled;

        _actions.Enable(); 
        map.Enable();  
    }

    private void DisableInput()
    {
        if (_moveAction != null)
        {
            _moveAction.performed -= OnMovePerformed;
            _moveAction.canceled -= OnMoveCanceled;
            _moveAction = null;
        }

        _actions?.Disable();
        _move = Vector2.zero;
    }

    private void OnMovePerformed(InputAction.CallbackContext ctx)
    {
        _move = ctx.ReadValue<Vector2>();
    }

    private void OnMoveCanceled(InputAction.CallbackContext ctx)
    {
        _move = Vector2.zero;
    }

    private void Update()
    {
        // オーナーのみ移動処理を実行
        if (!IsOwner) return;

        Vector3 dir = new Vector3(_move.x, 0f, _move.y);
        if (dir.sqrMagnitude > 1e-6f) dir.Normalize();

        // キャラクターを移動
        transform.Translate(dir * Speed * Time.deltaTime, Space.World);

        // 移動方向を向く
        if (dir != Vector3.zero)
        {
            var to = Quaternion.LookRotation(dir, Vector3.up);
            transform.rotation = Quaternion.RotateTowards(
                transform.rotation, to, RotationSpeed * Time.deltaTime);
        }
    }
}

動作確認

移動が正常に同期されることを確認できました!

まとめ

Unity 6でNetcode for GameObjectsを使用する際の主な変更点:

  1. NetworkTransform: NetworkTransformClientは不要。Authority ModeOwnerに設定
  2. Input System: PlayerInputコンポーネントではなく、生成されたC#クラスを直接使用することで、クライアント側の入力問題を解決

Discussion