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コンポーネントをもつことが必須
シーンにローカルプレイヤー用のオブジェクトを置いておいて、配下にアバターを生成する
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に登録して監視対象にする
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
定期的に呼ばれ、パラメータを送信・受信する
イベント同期 / RPC
PhotonView.RPC
他クライアントのメソッドを実行する
[PunRPC]
void Method(){}
メソッドを定義する際に[PunRPC]属性をつける