Skip to content

Instantly share code, notes, and snippets.

@takashicompany
Last active June 21, 2016 10:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takashicompany/ca388c8be623dd7b8421bf89d53edac7 to your computer and use it in GitHub Desktop.
Save takashicompany/ca388c8be623dd7b8421bf89d53edac7 to your computer and use it in GitHub Desktop.
Unity Simple Photon Client and Sample.
// @takashicompany
using System;
using System.Reflection;
using System.Collections.Generic;
using ExitGames.Client.Photon;
using ExitGames.Client.Photon.LoadBalancing;
/// <summary>
/// Photonの拡張クラス
/// </summary>
public static class PhotonExtension
{
private static Dictionary<Type, List<FieldInfo>> _codes = new Dictionary<Type, List<FieldInfo>>();
/// <summary>
/// byte型の値からParameterCode名を取得する
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public static string GetParameterCodeName(byte code)
{
return GetCodeNames(typeof(ParameterCode), code);
}
/// <summary>
/// byte型の値からEventCode名を取得する
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public static string GetEventCodeName(byte code)
{
return GetCodeNames(typeof(EventCode), code);
}
/// <summary>
/// byte型の値からOperationCode名を取得する
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public static string GetOperationCodeName(byte code)
{
return GetCodeNames(typeof(OperationCode), code);
}
private static string GetCodeNames(Type type, byte code)
{
if (!_codes.ContainsKey(type))
{
_codes.Add(type, GetFields(type));
}
var list = _codes[type];
if (list == null) return null;
string str = "";
int count = 0;
for (int i = 0; i < list.Count; i++)
{
var field = list[i];
if ((byte)field.GetValue(field.Name) != code) continue;
str += count == 0 ? field.Name : " or " + field.Name;
count++;
}
return str;
}
private static List<FieldInfo> GetFields(Type type)
{
var fields = type.GetFields();
var list = new List<FieldInfo>();
foreach (var field in fields)
{
if (field.FieldType == typeof(byte))
{
list.Add(field);
}
}
return list;
}
/// <summary>
/// コード名を用いて文字列を生成
/// </summary>
/// <param name="self"></param>
/// <returns></returns>
public static string ToStringUseCodeName(this EventData self)
{
string str = string.Format("---EventData:{0} Parameters---", GetEventCodeName(self.Code));
foreach (var p in self.Parameters)
{
var pCode = GetParameterCodeName(p.Key);
str += string.Format("\n{0}:{1}", pCode, p.Value.ToString());
}
str += "\n---end---";
return str;
}
/// <summary>
/// コード名を用いて文字列を生成
/// </summary>
/// <param name="self"></param>
/// <returns></returns>
public static string ToStringUseCodeName(this OperationResponse self)
{
string str = string.Format("---OperationRequest:{0} Parameters---", GetOperationCodeName(self.OperationCode));
foreach (var p in self.Parameters)
{
var pCode = GetParameterCodeName(p.Key);
str += string.Format("\n{0}:{1}", pCode, p.Value.ToString());
}
str += "\n---end---";
return str;
}
}
// @takashicompany
using UnityEngine;
using ExitGames.Client.Photon;
using ExitGames.Client.Photon.LoadBalancing;
public class PhotonSimpleSample : MonoBehaviour
{
private SimpleClient _client;
void Awake()
{
byte maxPlayer = 2;
// インスタンスを生成
_client = new SimpleClient("App ID", maxPlayer);
// マスターサーバーに接続
_client.ConnectToRegionMaster("jp");
// リスナーを登録
_client.onJoin += OnJoin;
_client.onLeave += OnLeave;
_client.onJoinOther += OnJoinOther;
_client.onLeaveOther += OnLeaveOther;
}
void Update()
{
// 毎フレーム実行してください
_client.Service();
}
void Match()
{
// マッチング条件を設定
var roomProperties = new ExitGames.Client.Photon.Hashtable();
roomProperties["stage_id"] = 1;
_client.Match(roomProperties);
}
private void OnJoin(Room room)
{
// 自分が部屋に入った時の処理
}
private void OnLeave()
{
// 部屋から出た時の処理
}
private void OnJoinOther(int playerId)
{
// 誰かが部屋に入ってきた時の処理
}
private void OnLeaveOther(int playerId)
{
// 誰かが部屋から出ていた時の処理
}
private void OnEvent(EventData e)
{
// 送られてきたイベントを処理
// code e.g. 10=attack 20=defense 30=move
switch (e.Code)
{
case 10:
// attack...
break;
case 20:
// defense...
break;
case 30:
// move...
break;
}
}
}
// @takashicompany
using System;
using System.Collections.Generic;
using ExitGames.Client.Photon;
using ExitGames.Client.Photon.LoadBalancing;
using PHashtable = ExitGames.Client.Photon.Hashtable;
/// <summary>
/// 簡易型のPhotonクライアント
/// 指定した条件のルームに入室/作成する
/// </summary>
public class SimpleClient : LoadBalancingClient
{
/// <summary>
/// ルームからイベントを受信した時に実行される
/// </summary>
public event Action<EventData> onEvent;
/// <summary>
/// Operationのレスポンスを受信した際に呼ばれる
/// </summary>
public event Action<OperationResponse> onOperaionResponse;
/// <summary>
/// 部屋に入れた時に呼ばれる
/// </summary>
public event Action<Room> onJoin;
/// <summary>
/// 部屋から去った際に呼ばれる
/// </summary>
public event Action onLeave;
/// <summary>
/// 部屋に他のプレイヤーが入ってきた際に呼ばれる
/// </summary>
public event Action<int> onJoinOther;
/// <summary>
/// 部屋から他のプレイヤーが去った際に呼ばれる
/// </summary>
public event Action<int> onLeaveOther;
/// <summary>
/// マッチングに失敗した場合に呼ばれる
/// </summary>
public event Action onMatchFailure;
/// <summary>
/// クライアントの状態が変わった際に呼ばれる
/// </summary>
public event Action<ClientState> onStateChange;
/// <summary>
/// クライアントの部屋内での番号
/// </summary>
/// <returns></returns>
public int actorNr { get; private set; }
/// <summary>
/// マッチング条件/ルーム設定
/// </summary>
private PHashtable _roomProperties;
/// <summary>
/// 部屋の最大人数
/// </summary>
private byte _maxPlayer = 2;
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="appId"></param>
/// <param name="gameStartPlayer"></param>
/// <param name="base("></param>
public SimpleClient(
string appId,
byte maxPlayer
) : base()
{
this.AppId = appId;
_maxPlayer = maxPlayer;
this.OnStateChangeAction = OnStateChange;
}
/// <summary>
/// クライアントの状態が変わった際に呼ばれる
/// </summary>
/// <param name="state"></param>
protected virtual void OnStateChange(ClientState state)
{
if (onStateChange != null)
{
onStateChange(state);
}
}
/// <summary>
/// 条件を指定してマッチングする。
/// 条件にあった部屋があればそこに入り、無ければ新しく部屋を作る。
/// ロビーにいる時のみ実行可能
/// 既にルームにいる場合は何もしない(誰かが入ってくるのを待つ)
/// </summary>
/// <param name="roomProperties"></param>
public void Match(PHashtable roomProperties)
{
if (State == ClientState.Joined)
{
return;
}
if (State != ClientState.JoinedLobby)
{
OnMatchFailure();
return;
}
_roomProperties = roomProperties;
JoinRandomRoom();
}
/// <summary>
/// 適当な部屋を見つけて入ってみる
/// </summary>
protected void JoinRandomRoom()
{
this.OpJoinRandomRoom(_roomProperties, 0);
}
/// <summary>
/// 適当な部屋が見つからなかった
/// </summary>
private void OnJoinRandomRoomFailure()
{
CreateRoom();
}
/// <summary>
/// 部屋を作る
/// </summary>
private void CreateRoom()
{
var options = new RoomOptions()
{
MaxPlayers = _maxPlayer,
CustomRoomProperties = _roomProperties
};
var list = new List<string>();
if (_roomProperties != null)
{
foreach (var p in _roomProperties)
{
list.Add(p.Key.ToString());
}
}
// CustomRoomPropertiesForLobby で指定したキーがランダムマッチングに使われる
options.CustomRoomPropertiesForLobby = list.ToArray();
this.OpCreateRoom(null, options, TypedLobby.Default);
}
/// <summary>
/// 何らかの理由でマッチングが失敗した時に呼ばれる
/// </summary>
private void OnMatchFailure()
{
if (onMatchFailure != null)
{
onMatchFailure();
}
}
/// <summary>
/// 部屋に入った際に呼ばれる
/// </summary>
/// <param name="actorNr"></param>
private void OnJoin(int actorNr)
{
this.actorNr = actorNr;
if (onJoin != null)
{
onJoin(CurrentRoom);
}
}
/// <summary>
/// 部屋から出た際に呼ばれる
/// </summary>
/// <param name="actorNr"></param>
private void OnLeave()
{
this.actorNr = -1;
if (onLeave != null)
{
onLeave();
}
}
/// <summary>
/// 他のプレイヤーが部屋に入ってきた際に呼ばれる
/// </summary>
/// <param name="actorNr"></param>
private void OnJoinOther(int actorNr)
{
if (onJoinOther != null)
{
onJoinOther(actorNr);
}
}
/// <summary>
/// プレイヤーが部屋から去った時に呼ばれる
/// </summary>
/// <param name="actorNr"></param>
private void OnLeaveOther(int actorNr)
{
if (onLeaveOther != null)
{
onLeaveOther(actorNr);
}
}
public override void OnOperationResponse(OperationResponse operationResponse)
{
base.OnOperationResponse(operationResponse);
if (onOperaionResponse != null)
{
onOperaionResponse(operationResponse);
}
// オペレーションの種別毎に処理を分ける
switch (operationResponse.OperationCode)
{
// 部屋を作成した場合
case (byte)OperationCode.CreateGame:
// 成功しなかった場合
if (operationResponse.ReturnCode != 0)
{
OnMatchFailure();
}
else
{
if (this.State == ClientState.Joined)
{
var actorNr = (int)operationResponse.Parameters[ParameterCode.ActorNr];
OnJoin(actorNr);
}
}
break;
case (byte)OperationCode.JoinRandomGame:
if (operationResponse.ReturnCode == ErrorCode.Ok)
{
}
else if (operationResponse.ReturnCode == ErrorCode.NoRandomMatchFound)
{
OnJoinRandomRoomFailure();
}
else
{
OnMatchFailure();
}
break;
case (byte)OperationCode.JoinGame:
if (operationResponse.ReturnCode == ErrorCode.Ok)
{
if (State == ClientState.Joined)
{
var actorNr = (int)operationResponse.Parameters[ParameterCode.ActorNr];
OnJoin(actorNr);
}
}
break;
case (byte)OperationCode.Leave:
if (operationResponse.ReturnCode == ErrorCode.Ok && State == ClientState.JoinedLobby)
{
OnLeave();
}
break;
}
}
public override void OnEvent(EventData e)
{
base.OnEvent(e);
switch (e.Code)
{
case EventCode.Join:
if (e.Parameters.ContainsKey(ParameterCode.ActorNr))
{
var actorNr = (int)e.Parameters[ParameterCode.ActorNr];
if (actorNr != this.actorNr)
{
OnJoinOther(actorNr);
}
}
break;
case EventCode.Leave:
if (e.Parameters.ContainsKey(ParameterCode.ActorNr))
{
var actorNr = (int)e.Parameters[ParameterCode.ActorNr];
if (actorNr != this.actorNr)
{
OnLeaveOther(actorNr);
}
}
break;
default:
if (onEvent != null)
{
onEvent(e);
}
break;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment