バルーンポケモンが好きな人のブログ

つまらない はなしで あいてをねむらせるのが とくいだぞ

Photonでマルチプレイを実装・事始め

動機

今までUnityで簡単なアプリを作ってはunity roomで公開してきましたが、一人用ではなくみんなで集まってゲームをしたりコミュニケーションをとれたりできるようなものを作りたいと思い、Unityでマルチプレイを実装してみました!

ちなみにこういうのを作ってました↓

unityroom.com

マルチプレイの実装に必要なエンジン

Photonを使います。Unityでマルチプレイを実装するなら、これが最有力候補に上がるみたいですね。
ネットでググってみても記事が多くヒットするのでこれを採用してみます。

というか、この記事が入門として素晴らしい出来です。これ無料で読んでいいんですか...?

zenn.dev

この記事では2Dのゲーム開発をテーマにチュートリアルが進んでいますが、私は3Dで作りたかったので、3Dの環境で開発をしています。
進め方に大きな差はほとんどないです。

ヒエラルキーのウィンドウ内で適当なEmpty Objectを作成し、以下のスクリプトをアタッチします。

// SampleScene.cs

using Photon.Pun;
using Photon.Realtime;
using UnityEngine;

// MonoBehaviourPunCallbacksを継承して、PUNのコールバックを受け取れるようにする
public class SampleScene : MonoBehaviourPunCallbacks
{
    private void Start() {
        // PhotonServerSettingsの設定内容を使ってマスターサーバーへ接続する
        PhotonNetwork.ConnectUsingSettings();
    }

    // マスターサーバーへの接続が成功した時に呼ばれるコールバック
    public override void OnConnectedToMaster() {
        // "Room"という名前のルームに参加する(ルームが存在しなければ作成して参加する)
        PhotonNetwork.JoinOrCreateRoom("Room", new RoomOptions(), TypedLobby.Default);
    }

    // ゲームサーバーへの接続が成功した時に呼ばれるコールバック
    public override void OnJoinedRoom() {
        // ランダムな座標に自身のアバター(ネットワークオブジェクト)を生成する
        var position = new Vector3(Random.Range(-3f, 3f), 5f, Random.Range(-3f, 3f));
        GameObject fixedMaleDummy = PhotonNetwork.Instantiate("FixedRobotKyle", position, Quaternion.identity);
    }
}

FixedRobotKyleは読み込んだPhotonの中に含まれているAssets/Photon/PhotonUnityNetworking/Demos/Shared Assets/Models/Robot Kyle.fbxをもとにprefabをコピーし、そこに以下のコンポーネントを追加したものになります。

  • Animator

個々のアニメーション(Idle状態やWalk状態等)はAssets/Photon/PhotonUnityNetworking/Demos/Shared Assets/Animationsにあるので、それを流用し、Controllerだけ自作しました。
単純にスピードによってIdleとWalkのアニメーションを切り替えるだけです。

f:id:slowsingle:20210808003936p:plain

  • Photon View
  • Photon Transform View
  • Photon Animator View

f:id:slowsingle:20210808003834p:plain

  • Rigidbody
  • Box Collider

RigidbodyのConstraintでX軸とZ軸中心の回転に制約を与えています。これで、キャラクター同士がぶつかった際に倒れてしまって身動きが取れなくなる、という問題を防ぐことができます。

f:id:slowsingle:20210808004305p:plain

最後にキャラクターを操作するためのControllerのスクリプトを作成してアタッチします。

// PlayerController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;


public class PlayerController : MonoBehaviourPunCallbacks
{
    [SerializeField] private GameObject cameraObj;
    [SerializeField] private Animator animator;
    [SerializeField] private float speed;
    [SerializeField] private float rorationSpeed;

    private Vector3 moveVelocity;
 
    private void Start()
    {
        if (!photonView.IsMine)
        {
            cameraObj.SetActive(false);
        }

        moveVelocity = Vector3.zero;
    }

    private void Update()
    {
        if (photonView.IsMine)
        {
            float h = Input.GetAxis("Horizontal");
            float v = Input.GetAxis("Vertical");

            transform.Rotate(new Vector3 (0, rorationSpeed * h, 0));
            moveVelocity = speed * v * transform.forward;
            transform.position += moveVelocity * Time.deltaTime;

            animator.SetFloat("MoveSpeed", new Vector3(moveVelocity.x, 0, moveVelocity.z).magnitude * v);
        }
    }
}

マルチプレイなので当然キャラクターは複数いて、キャラクターの数だけカメラがあります。
どのキャラクターを操作するか、どのカメラを追従させるかを設定するために、Start関数で自分のキャラクターではないカメラを非アクティブにし、Update関数では自分のキャラクターの動きに対してのみキー入力による速度変化を適用させています。

カメラはこんな感じでキャラクターの中に同梱させています。

f:id:slowsingle:20210808005332p:plain

ビルドしてアプリを実行するともにUnity内でも起動し2パターンで起動すると、それぞれでキャラクターが生成されるので、フィールド上には以下のようにキャラクターが2人出現します。
それぞれの画面でキャラクターを操作すると、その位置や向き、アニメーションがもう片方のアプリにも反映されます。

f:id:slowsingle:20210808005856p:plain

楽しい!