C#
Unity
Photon
Unity2017

【PUN】アバター生成・同期

Photon Unity Networkの基本機能メモ

【PUN】ログイン
【PUN】アバター生成・同期
【PUN】ゲーム終了判定


開発方法

Unity Collaborateでプロジェクトを共有して、PCを2台並べて開発するのがおすすめ。
(3台で見たい場合はアプリでビルド)

アバター生成

PhotonNetwork.Instantiate
https://doc.photonengine.com/ja-jp/pun/current/gameplay/instantiation
生成するゲームオブジェクトはResources以下にプレファブとして格納
生成対象はクライアント間通信の橋渡しであるPhotonViewコンポーネントをもつことが必須

シーンにローカルプレイヤー用のオブジェクトを置いておいて、配下にアバターを生成する

Player.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Player : MonoBehaviour {

    void OnJoinedRoom(){
        CreateAvatar ();
    }

    void CreateAvatar() {
        Debug.Log ("Player: 自分のアバターを生成");

        // 自分のアバターを他のクライアントにも生成
        GameObject avatar = PhotonNetwork.Instantiate ("Avatar", new Vector3(0f,1f,0f), Quaternion.identity, 0);
        avatar.transform.parent = transform;

        GameObject camera = transform.Find ("Camera").gameObject;
        camera.transform.parent = avatar.transform;
    }
}

Transformの同期

アバターにPhotonTransformViewコンポーネントをアタッチ
PositionとRotationを同期にチェック
PhotonViewコンポーネントのObservedComponentsに登録して監視対象にする

2017-12-23 (2).png

MonoBehaviourの同期

Avatar.csを作成、アバターにアタッチ
MonoBehaviourからPhoton.MonoBehaviour(自身のスクリプトが紐づくオブジェクトにアタッチされているPhotonViewコンポーネントへのアクセスを提供するヘルパークラス)に変更
PhotonViewコンポーネントのObservedComponentsに登録して監視対象にする

using UnityEngine;
using System.Collections;

public class Avatar : Photon.MonoBehaviour {

    [SerializeField] int m_healthPoint = 100;
    [SerializeField] GameObject m_BulletPrefab;
    [SerializeField] GameObject m_DamageEffectPrefab;

    PhotonView m_photonView;

    void Start() {
        m_photonView = GetComponent<PhotonView> ();
        if(m_photonView.isMine) {
            // 初期設定
            SetPlayerName ("Player-Id: " + PhotonNetwork.player.ID);
            SetHealthPoint (m_healthPoint);
        }
    }

    void SetPlayerName(string name) {
        this.gameObject.name = name;
        transform.Find ("NameUI").gameObject.GetComponent<TextMesh> ().text = name;
    }

    void SetHealthPoint(int num) {
        m_healthPoint = num;
        transform.Find ("HealthPointUI").gameObject.GetComponent<TextMesh> ().text = num.ToString();
    }

    // ストリーム同期
    void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) {
        if (stream.isWriting) {
            // 自分の情報を送る
            string myName = this.gameObject.name;
            int myHealthPoint = m_healthPoint;
            stream.SendNext (myName);
            stream.SendNext (myHealthPoint);
        } else {
            // 他人の情報を受け取る
            string otherName = (string)stream.ReceiveNext();
            int otherHealthPoint = (int)stream.ReceiveNext();
            SetPlayerName (otherName);
            SetHealthPoint (otherHealthPoint);
        }
    }

    // イベント同期
    [PunRPC]
    void Shoot(Vector3 i_pos, Vector3 i_angle) {
        // 全クライアントの中の自分が一斉に弾丸を発射する
        Quaternion rot = Quaternion.Euler (i_angle);
        GameObject bullet = GameObject.Instantiate (m_BulletPrefab ,i_pos ,rot);
        bullet.GetComponent<Rigidbody> ().AddForce (transform.forward * 20, ForceMode.VelocityChange);
        Destroy(bullet, 3);
    }

    void OnTriggerEnter(Collider i_other) {
        if(i_other.tag == "bullet"){
            // ダメージを受ける
            SetHealthPoint (m_healthPoint - 10);
            Instantiate (m_DamageEffectPrefab, transform.position, Quaternion.identity);
            Destroy (i_other.gameObject);

            if(m_healthPoint <= 0){
                if (m_photonView.isMine) {
                    // gameover
                }
            }
        }
    }

    void Update () {

        if(m_photonView.isMine == false){
            return;
        }

        if (Input.GetKeyDown (KeyCode.Space)) {
            // 自分自身の弾丸でトリガーを引かないようにやや手前から発射
            Vector3 pos = transform.position + transform.forward * 2f;
            Vector3 angle = transform.eulerAngles;
            // 処理が重いのでPhotonViewを付けずに位置と角度を渡す
            m_photonView.RPC ("Shoot", PhotonTargets.AllViaServer, pos, angle);
        }

        // 自分のアバターだけを動かす
        var v = Input.GetAxis ("Vertical");
        Vector3 velocity = new Vector3 (0, 0, v);
        velocity = transform.TransformDirection (velocity);
        velocity *= 5f;
        transform.localPosition += velocity * Time.fixedDeltaTime;

        var h = Input.GetAxis ("Horizontal");
        transform.Rotate (0, h * 3f, 0);
    }

}

ストリーム同期 / OnPhotonSerializeView

OnPhotonSerializeView
定期的に呼ばれ、パラメータを送信・受信する

202 2 連続的な値の同期方法

イベント同期 / RPC

PhotonView.RPC
他クライアントのメソッドを実行する

[PunRPC]
void Method(){}
メソッドを定義する際に[PunRPC]属性をつける

202 3 離散的な値の同期方法