Unity - Photon PUN 本地与网络同步的逻辑分离 (二)
上篇实现了事件系统的设计,这篇就来结合发送RPC消息 并且不用标记 [PunRPC]
先来看下上编的代码 GameEnvent.cs
private static Dictionary<CommandType, Delegate> EvnDic = new Dictionary<CommandType, Delegate>(); //保存所有函数方法的字典 public static List<CommandType> CommandTypeList = new List<CommandType>(); //注册监听____________________________________
public static void Listen(CommandType command, CallFunction call) //通过传递参数枚举 和方法 进行绑定到EvnDic字典中
{
if (!CommandTypeList.Contains(command)) //如果不包含就添加进去
{
CommandTypeList.Add(command);
EvnDic.Add(command, call);
}
else //如果包含1.判断是否是null 2.不是null则进行绑定(+=)
{
if (EvnDic[command] == null)
{
Consoles.WriteError("Delegate对象异常为NULL Key:" + command);
return;
}
EvnDic[command] = (CallFunction)EvnDic[command] + call;
}
} public static void Listen<T>(CommandType command, CallFunction<T> call)
{
if (!CommandTypeList.Contains(command))
{
CommandTypeList.Add(command);
EvnDic.Add(command, call);
}
else
{ if (EvnDic[command] == null || EvnDic[command].GetType() != call.GetType())
{
Consoles.WriteError("Delegate对象异常为NULL Key:" + command);
return;
}
EvnDic[command] = (CallFunction<T>)EvnDic[command] + call;
}
} public static void Listen<T, U>(CommandType command, CallFunction<T, U> call)
{
if (!CommandTypeList.Contains(command))
{
CommandTypeList.Add(command);
EvnDic.Add(command, call);
}
else
{
if (EvnDic[command] == null || EvnDic[command].GetType() != call.GetType())
{
Consoles.WriteError("Delegate对象异常为NULL Key:" + command);
return;
}
EvnDic[command] = (CallFunction<T, U>)EvnDic[command] + call;
}
} public static void Listen<T, U, O>(CommandType command, CallFunction<T, U, O> call)
{
if (!CommandTypeList.Contains(command))
{
CommandTypeList.Add(command);
EvnDic.Add(command, call); }
else
{ if (EvnDic[command] == null || EvnDic[command].GetType() != call.GetType())
{
Consoles.WriteError("Delegate对象异常为NULL Key:" + command);
return;
}
EvnDic[command] = (CallFunction<T, U, O>)EvnDic[command] + call;
}
} public static void Listen<T, U, O, P>(CommandType command, CallFunction<T, U, O, P> call)
{
if (!CommandTypeList.Contains(command))
{
CommandTypeList.Add(command);
EvnDic.Add(command, call); }
else
{ if (EvnDic[command] == null || EvnDic[command].GetType() != call.GetType())
{
Consoles.WriteError("Delegate对象异常为NULL Key:" + command);
return;
}
EvnDic[command] = (CallFunction<T, U, O, P>)EvnDic[command] + call;
}
} private static void CheckCommad(CommandType command)
{
if (EvnDic[command] == null)
{
EvnDic.Remove(command);
CommandTypeList.Remove(command);
}
} //移除事件--------------------------------------------------------
public static void Remove(CommandType command, CallFunction call) //通过枚举 和 方法 从EvnDic字典中移除绑定
{ if (!CommandTypeList.Contains(command)) return; Delegate @delegate = EvnDic[command];
if (@delegate == null)
{
Consoles.WriteError("Delegate结果为NULL Key:" + command);
return;
}
else if (@delegate.GetType() != call.GetType())
{
Consoles.WriteError("Delegate对象不匹配 Key:" + command);
return;
}
EvnDic[command] = (CallFunction)EvnDic[command] - call;; CheckCommad(command);
} public static void Remove<T>(CommandType command, CallFunction<T> call)
{
if (!CommandTypeList.Contains(command)) return; Delegate @delegate = EvnDic[command];
if (@delegate == null)
{
Consoles.WriteError("Delegate结果为NULL Key:" + command);
return;
}
else if (@delegate.GetType() != call.GetType())
{
Consoles.WriteError("Delegate对象不匹配 Key:" + command);
return;
} EvnDic[command] = (CallFunction<T>)EvnDic[command] - call; CheckCommad(command);
} public static void Remove<T, U>(CommandType command, CallFunction<T, U> call)
{
if (!CommandTypeList.Contains(command)) return; Delegate @delegate = EvnDic[command];
if (@delegate == null)
{
Consoles.WriteError("Delegate结果为NULL Key:" + command);
return;
}
else if (@delegate.GetType() != call.GetType())
{
Consoles.WriteError("Delegate对象不匹配 Key:" + command);
return;
} EvnDic[command] = (CallFunction<T, U>)EvnDic[command] - call; CheckCommad(command);
} public static void Remove<T, U, O>(CommandType command, CallFunction<T, U, O> call)
{
if (!CommandTypeList.Contains(command)) return; Delegate @delegate = EvnDic[command];
if (@delegate == null)
{
Consoles.WriteError("Delegate结果为NULL Key:" + command);
return;
}
else if (@delegate.GetType() != call.GetType())
{
Consoles.WriteError("Delegate对象不匹配 Key:" + command);
return;
} EvnDic[command] = (CallFunction<T, U, O>)EvnDic[command] - call; CheckCommad(command);
} public static void Remove<T, U, O, P>(CommandType command, CallFunction<T, U, O, P> call)
{
if (!CommandTypeList.Contains(command)) return; Delegate @delegate = EvnDic[command];
if (@delegate == null)
{
Consoles.WriteError("Delegate结果为NULL Key:" + command);
return;
}
else if (@delegate.GetType() != call.GetType())
{
Consoles.WriteError("Delegate对象不匹配 Key:" + command);
return;
} EvnDic[command] = (CallFunction<T, U, O, P>)EvnDic[command] - call; CheckCommad(command);
} public static void Remove<T, U, O, P, Q>(CommandType command, CallFunction<T, U, O, P, Q> call)
{
if (!CommandTypeList.Contains(command)) return; Delegate @delegate = EvnDic[command];
if (@delegate == null)
{
Consoles.WriteError("Delegate结果为NULL Key:" + command);
return;
}
else if (@delegate.GetType() != call.GetType())
{
Consoles.WriteError("Delegate对象不匹配 Key:" + command);
return;
} EvnDic[command] = (CallFunction<T, U, O, P, Q>)EvnDic[command] - call; CheckCommad(command);
} //执行事件-------------------------------------------------------------
public static void Broadcast(CommandType command) //通过枚举 和要执行方法参数 从EvnDic中获取对象方法并调用
{
if (!CommandTypeList.Contains(command)) return;
Delegate @delegate;
if (EvnDic.TryGetValue(command, out @delegate))
{
CallFunction call = @delegate as CallFunction;
if (call != null)
call();
else
Consoles.WriteError("对应key的De'le'gate为空 Key:" + command);
}
} public static void Broadcast<T>(CommandType command, T arg1)
{
if (!CommandTypeList.Contains(command)) return;
Delegate @delegate;
if (EvnDic.TryGetValue(command, out @delegate))
{
CallFunction<T> call = @delegate as CallFunction<T>;
if (call != null)
call(arg1);
else
Consoles.WriteError("对应key的De'le'gate为空 Key:" + command);
}
} public static void Broadcast<T, U>(CommandType command, T arg1, U arg2)
{
if (!CommandTypeList.Contains(command)) return;
Delegate @delegate;
if (EvnDic.TryGetValue(command, out @delegate))
{
CallFunction<T, U> call = @delegate as CallFunction<T, U>;
if (call != null)
call(arg1, arg2);
else
Consoles.WriteError("对应key的De'le'gate为空 Key:" + command);
}
} public static void Broadcast<T, U, O>(CommandType command, T arg1, U arg2, O arg3)
{
if (!CommandTypeList.Contains(command)) return;
Delegate @delegate;
if (EvnDic.TryGetValue(command, out @delegate))
{
CallFunction<T, U, O> call = @delegate as CallFunction<T, U, O>;
if (call != null)
call(arg1, arg2, arg3);
else
Consoles.WriteError("对应key的De'le'gate为空 Key:" + command);
}
} public static void Broadcast<T, U, O, P>(CommandType command, T arg1, U arg2, O arg3, P arg4)
{
if (!CommandTypeList.Contains(command)) return;
Delegate @delegate;
if (EvnDic.TryGetValue(command, out @delegate))
{
CallFunction<T, U, O, P> call = @delegate as CallFunction<T, U, O, P>;
if (call != null)
call(arg1, arg2, arg3, arg4);
else
Consoles.WriteError("对应key的De'le'gate为空 Key:" + command);
}
} public static void Broadcast<T, U, O, P, Q>(CommandType command, T arg1, U arg2, O arg3, P arg4, Q arg5)
{
if (!CommandTypeList.Contains(command)) return;
Delegate @delegate;
if (EvnDic.TryGetValue(command, out @delegate))
{
CallFunction<T, U, O, P, Q> call = @delegate as CallFunction<T, U, O, P, Q>;
if (call != null)
call(arg1, arg2, arg3, arg4, arg5);
else
Consoles.WriteError("对应key的De'le'gate为空 Key:" + command);
}
} //清空事件--------------------------------------------------------
public static void Cleanup()
{
EvnDic.Clear();
}
其中 public static void Broadcast 方法已经具备了调用绑定事件的功能,但如果发送rpc同步消息还是需要添加 [PunRPC]标记,所以下一步我们再建立一个类 GeneralSubmit.cs ,这个类主要负责就是通过让其他地方可以跳过添加标记的繁琐。
是的其实原理很简单,我们先说下思路:
GameEnvent.cs 负责同步消息的绑定和执行
GeneralSubmit.cs 是个rpc消息的中介类
过程是 通过GameEnvent .cs 向GeneralSubmit .cs 发送一个 要调用的 delegate函数 (CallBackFunction),GeneralSubmit .cs 通过内部的一个PRC方法再来执行这个 delegate ,这样简单几步除了GeneralSubmit这个rpc消息中介类外 再也没有什么地方需要我们添加 [PunRPC]标记 就可以执行rpc消息同步了。
1.那么我们就开始写一下 GeneralSubmit.cs
using System.Reflection;
using System.Linq;
public class GeneralSubmit :MonoBehaviour
{
private PhotonView view;
public PhotonView GetView
{
set { view = value; }
get
{
if (view == null)
view = GetComponent<PhotonView>();
if (view == null)
view = gameObject.AddComponent<PhotonView>();
return view;
}
} private Type _SelfType;
public Type SelfType
{
private set
{
_SelfType = value;
}
get
{
if (_SelfType == null)
_SelfType = GetType();
return _SelfType;
}
} public static GeneralSubmit instance; private void Awake()
{
instance = this;
DontDestroyOnLoad(gameObject);
} //--------------------------------------------------------------
//简单封装了Rpc发送,发送方式是PhotonTargets.AllBuffered
public void Net(string functionName,params object[]args)
{
PhotonView view =GetComponent<PhotonView>();
if (view == null){Debug.LogError("ERROR"); return;}
view.RPC(functionName, PhotonTargets.AllBuffered, args);
} // GameMessage 方法接收枚举(因为绑定事件是由枚举作为key)和参数并以RPC的方式执行OnGameEvent函数,当然这个只是个基础的,重要的是看GameMessage 的泛型
public void GameMessage(CommandType command)
{
int commandIndex = (int)command;
Net("OnGameEvent", commandIndex);
} //接收枚举(因为绑定事件是由枚举作为key)和参数并已RPC的方式执行OnGameEvent函数
public void GameMessage<T>(CommandType command,T arg1)
{
//重要 这里将T等泛型以字符串的形式传出,接收方再将其转换成类型,这样就巧妙地实现了类型的传送 给 本类的OnGameEvent方法
Net("OnGameEvent", typeof(T).FullName, (int)command, arg1);
} public void GameMessage<T,U>(CommandType command, T arg1,U arg2)
{
//重要 这里将T,U等泛型以字符串的形式传出,接收方再将其转换成类型,这样就巧妙地实现了类型的传送 给 本类的OnGameEvent方法 ,其他的泛型也是如此
Net("OnGameEvent", typeof(T).FullName,typeof(U).FullName, (int)command, arg1,arg2);
} public void GameMessage<T, U, V>(CommandType command, T arg1, U arg2, V arg3)
{
transform.Net("OnGameEvent", typeof(T).FullName, typeof(U).FullName,typeof(V).FullName, (int)command, arg1, arg2,arg3);
} public void GameMessage<T, U, V,N>(CommandType command, T arg1, U arg2, V arg3,N arg4)
{
Net("OnGameEvent", typeof(T).FullName, typeof(U).FullName, typeof(V).FullName, typeof(N).FullName, (int)command, arg1, arg2,arg3,arg4);
} //*********
//OnGameEvent 是以RPC方式执行所以都要加上[PunRpc],声明 我们只会在OnGameEvent和他的泛型添加这个标记,完成后其他地方是全部不需要添加标记的 [PunRPC]
public void OnGameEvent(int index)
{
//TODO:将 index转换成枚举作为key
} /// <summary> Params 1 </summary>
[PunRPC]
public void OnGameEvent(string typeName, int index,object t1)
{ object o = Activator.CreateInstance(SelfType);
MethodInfo MI = SelfType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default).FirstOrDefault(F => F.IsGenericMethod && F.Name == "SendFunctionToServer" && F.GetParameters().Length == );
MethodInfo mi1 = MI.MakeGenericMethod(new Type[] { Type.GetType(typeName) });
mi1.Invoke(o, new object[] {index,t1 }); /* 注释: FirstOrDefault(F => F.IsGenericMethod && F.Name == "SendFunctionToServer" && F.GetParameters().Length == 2);
F.Name == "SendFunctionToServer -要调用的方法名
F.GetParameters().Length == 2 -该方法的参数有几个
当有个这两个条件,我们就可以调用我们需要的重载函数了。
其他的也是一样的方式
这样我们就把 泛型的类型 和参数 都拿到了并用反射SendFunctionToServer调用了对应的重载函数
看完 OnGameEvent的泛型后我们再去看下SendFunctionToServer 方法
*/
} public void SendFunctionToServer<T>(int command, object arg1)
{
T arg = (T)arg1;
将 参数转换成对应的类型 (因为是反射调用的所以我们不用去关心太多) //TODO: 将 command转换为枚举类型做为key
} /// <summary> Params 2 </summary>
[PunRPC]
public void OnGameEvent(string typeName,string typeNameU, int index, object t1,object t2)
{ object o = Activator.CreateInstance(SelfType);
MethodInfo MI = SelfType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default).FirstOrDefault(F => F.IsGenericMethod && F.Name == "SendFunctionToServer" && F.GetParameters().Length == );
MethodInfo mi1 = MI.MakeGenericMethod(new Type[] { Type.GetType(typeName) , Type.GetType(typeNameU) });
mi1.Invoke(o, new object[] { index, t1 ,t2});
} public void SendFunctionToServer<T,U>(int command, object arg1,object arg2)
{
T par1 = (T)arg1;
U par2 = (U)arg2;
} /// <summary> Params 3 </summary>
[PunRPC]
public void OnGameEvent(string typeName, string typeNameU, string typeNameV, int index, object t1, object t2,object t3)
{
// print(Type.GetType(typeName));
object o = Activator.CreateInstance(SelfType);
MethodInfo MI = SelfType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default).FirstOrDefault(F => F.IsGenericMethod && F.Name == "SendFunctionToServer" && F.GetParameters().Length == );
MethodInfo mi1 = MI.MakeGenericMethod(new Type[] { Type.GetType(typeName), Type.GetType(typeNameU),Type.GetType(typeNameV) });
mi1.Invoke(o, new object[] { index, t1, t2 ,t3 });
} public void SendFunctionToServer<T, U,V>(int command, object arg1, object arg2,object arg3)
{
T par1 = (T)arg1;
U par2 = (U)arg2;
V par3 = (V)arg3;
} /// <summary> Params 4 </summary>
[PunRPC]
public void OnGameEvent(string typeName, string typeNameU, string typeNameV, string typeNameN, int index, object t1, object t2, object t3,object t4)
{
// print(Type.GetType(typeName));
object o = Activator.CreateInstance(SelfType);
MethodInfo MI = SelfType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default).FirstOrDefault(F => F.IsGenericMethod && F.Name == "SendFunctionToServer" && F.GetParameters().Length == );
MethodInfo mi1 = MI.MakeGenericMethod(new Type[] { Type.GetType(typeName), Type.GetType(typeNameU), Type.GetType(typeNameV),Type.GetType(typeNameN) });
mi1.Invoke(o, new object[] { index, t1, t2, t3 ,t4});
} public void SendFunctionToServer<T, U, V,N>(int command, object arg1, object arg2, object arg3,object arg4)
{
T par1 = (T)arg1;
U par2 = (U)arg2;
V par3 = (V)arg3;
N par4 = (N)arg4;
}
现在已经可以使用泛型作为参数传递了,那么就差具体执行 通过传来的key做为键去调用值的这一步了
下面我们为GameEnvent.cs中添加一个泛型方法 用来专门调用 GeneralSubmit .cs的静态方法
//同步调用
public static void Execute(CommandType command)
{
if (!CommandTypeList.Contains(command)) return;
Delegate @delegate;
if (EvnDic.TryGetValue(command, out @delegate))
{
CallFunction call = @delegate as CallFunction;
if (call != null)
{
GeneralSubmit.instance.GameMessage(command);
}
else
Consoles.WriteError("对应key的De'le'gate为空 Key:" + command);
}
} public static void Execute<T>(CommandType command,T arg1)
{
if (!CommandTypeList.Contains(command)) return;
Delegate @delegate;
if (EvnDic.TryGetValue(command, out @delegate))
{
CallFunction<T> call = @delegate as CallFunction<T>;
if (call != null)
{
GeneralSubmit.instance.GameMessage<T>(command, arg1);
}
else
Consoles.WriteError("对应key的De'le'gate为空 Key:" + command);
}
} public static void Execute<T,U>(CommandType command, T arg1,U arg2)
{
if (!CommandTypeList.Contains(command)) return;
Delegate @delegate;
if (EvnDic.TryGetValue(command, out @delegate))
{
CallFunction<T,U> call = @delegate as CallFunction<T,U>;
if (call != null)
{
GeneralSubmit.instance.GameMessage<T,U>(command, arg1,arg2);
}
else
Consoles.WriteError("对应key的De'le'gate为空 Key:" + command);
}
} public static void Execute<T,U,V>(CommandType command, T arg1,U arg2 ,V arg3)
{
if (!CommandTypeList.Contains(command)) return;
Delegate @delegate;
if (EvnDic.TryGetValue(command, out @delegate))
{
CallFunction<T,U,V> call = @delegate as CallFunction<T,U,V>;
if (call != null)
{
GeneralSubmit.instance.GameMessage<T,U,V>(command, arg1,arg2,arg3);
}
else
Consoles.WriteError("对应key的De'le'gate为空 Key:" + command);
}
} public static void Execute<T,U,V,N>(CommandType command, T arg1,U arg2, V arg3, N arg4)
{
if (!CommandTypeList.Contains(command)) return;
Delegate @delegate;
if (EvnDic.TryGetValue(command, out @delegate))
{
CallFunction<T,U,V,N> call = @delegate as CallFunction<T,U,V,N>;
if (call != null)
{
GeneralSubmit.instance.GameMessage<T,U,V,N>(command, arg1,arg2,arg3,arg4);
}
else
Consoles.WriteError("对应key的De'le'gate为空 Key:" + command);
}
}
现在梳理下 执行GameEvent.Execute方法 会到
GeneralSubmit.instance.GameMessage 这里 然后会根据传来的 枚举和参数的不同执行OnGameEvent泛型,然后会反射执行
GeneralSubmit类中的SendFunctionToServer方法。
到这里SendFunctionToServer 中可以得到 枚举和参数,那么我们只需要再通过GameEvent.Broadcast 执行下去就可以了(注意通过此方式(GameEvent.Execute)此时是以rpc同步方式调用的
GameEvent.Broadcast,这样其他客户端也同样会做相同的调用,并且其他类中不再需要添加[PunRPC]标记
)
GeneralSubmit完整的代码
public class GeneralSubmit :PunBehaviour
{
private PhotonView view; public PhotonView GetView
{
set { view = value; } get
{
if (view == null)
view = GetComponent<PhotonView>();
if (view == null)
view = gameObject.AddComponent<PhotonView>();
return view;
}
} private Type _SelfType;
public Type SelfType
{
private set
{
_SelfType = value;
}
get
{
if (_SelfType == null)
_SelfType = GetType();
return _SelfType;
}
} public static GeneralSubmit instance; private void Awake()
{
instance = this;
DontDestroyOnLoad(gameObject);
} public void GameMessage(CommandType command)
{
int commandIndex = (int)command;
transform.Net("OnGameEvent", commandIndex);
} public void GameMessage<T>(CommandType command,T arg1)
{
transform.Net("OnGameEvent", typeof(T).FullName, (int)command, arg1);
} public void GameMessage<T,U>(CommandType command, T arg1,U arg2)
{
transform.Net("OnGameEvent", typeof(T).FullName,typeof(U).FullName, (int)command, arg1,arg2);
} public void GameMessage<T, U, V>(CommandType command, T arg1, U arg2, V arg3)
{
transform.Net("OnGameEvent", typeof(T).FullName, typeof(U).FullName,typeof(V).FullName, (int)command, arg1, arg2,arg3);
} public void GameMessage<T, U, V,N>(CommandType command, T arg1, U arg2, V arg3,N arg4)
{
transform.Net("OnGameEvent", typeof(T).FullName, typeof(U).FullName, typeof(V).FullName, typeof(N).FullName, (int)command, arg1, arg2,arg3,arg4);
} public void GameMessage<T, U, V, N,K>(CommandType command, T arg1, U arg2, V arg3, N arg4, K arg5)
{
transform.Net("OnGameEvent", typeof(T).FullName, typeof(U).FullName, typeof(V).FullName, typeof(N).FullName,typeof(K).FullName,(int)command, arg1, arg2, arg3, arg4,arg5);
} [PunRPC]
public void OnGameEvent(int index)
{
GameEnvent.Broadcast((CommandType)index);
} /// <summary> Params 1 </summary>
[PunRPC]
public void OnGameEvent(string typeName, int index,object t1)
{
object o = Activator.CreateInstance(SelfType);
MethodInfo MI = SelfType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default).FirstOrDefault(F => F.IsGenericMethod && F.Name == "SendFunctionToServer" && F.GetParameters().Length == );
MethodInfo mi1 = MI.MakeGenericMethod(new Type[] { Type.GetType(typeName) });
mi1.Invoke(o, new object[] {index,t1 });
} public void SendFunctionToServer<T>(int command, object arg1)
{
T arg = (T)arg1;
GameEnvent.Broadcast<T>((CommandType)command, arg);
} /// <summary> Params 2 </summary>
[PunRPC]
public void OnGameEvent(string typeName,string typeNameU, int index, object t1,object t2)
{
object o = Activator.CreateInstance(SelfType);
MethodInfo MI = SelfType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default).FirstOrDefault(F => F.IsGenericMethod && F.Name == "SendFunctionToServer" && F.GetParameters().Length == );
MethodInfo mi1 = MI.MakeGenericMethod(new Type[] { Type.GetType(typeName) , Type.GetType(typeNameU) });
mi1.Invoke(o, new object[] { index, t1 ,t2});
} public void SendFunctionToServer<T,U>(int command, object arg1,object arg2)
{
T par1 = (T)arg1;
U par2 = (U)arg2;
GameEnvent.Broadcast<T,U>((CommandType)command, par1,par2);
} /// <summary> Params 3 </summary>
[PunRPC]
public void OnGameEvent(string typeName, string typeNameU, string typeNameV, int index, object t1, object t2,object t3)
{
object o = Activator.CreateInstance(SelfType);
MethodInfo MI = SelfType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default).FirstOrDefault(F => F.IsGenericMethod && F.Name == "SendFunctionToServer" && F.GetParameters().Length == );
MethodInfo mi1 = MI.MakeGenericMethod(new Type[] { Type.GetType(typeName), Type.GetType(typeNameU),Type.GetType(typeNameV) });
mi1.Invoke(o, new object[] { index, t1, t2 ,t3 });
} public void SendFunctionToServer<T, U,V>(int command, object arg1, object arg2,object arg3)
{
T par1 = (T)arg1;
U par2 = (U)arg2;
V par3 = (V)arg3;
GameEnvent.Broadcast<T, U, V>((CommandType)command, par1, par2,par3);
} /// <summary> Params 4 </summary>
[PunRPC]
public void OnGameEvent(string typeName, string typeNameU, string typeNameV, string typeNameN, int index, object t1, object t2, object t3,object t4)
{
object o = Activator.CreateInstance(SelfType);
MethodInfo MI = SelfType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default).FirstOrDefault(F => F.IsGenericMethod && F.Name == "SendFunctionToServer" && F.GetParameters().Length == );
MethodInfo mi1 = MI.MakeGenericMethod(new Type[] { Type.GetType(typeName), Type.GetType(typeNameU), Type.GetType(typeNameV),Type.GetType(typeNameN) });
mi1.Invoke(o, new object[] { index, t1, t2, t3 ,t4});
} public void SendFunctionToServer<T, U, V,N>(int command, object arg1, object arg2, object arg3,object arg4)
{
T par1 = (T)arg1;
U par2 = (U)arg2;
V par3 = (V)arg3;
N par4 = (N)arg4;
GameEnvent.Broadcast<T, U, V, N>((CommandType)command, par1, par2, par3,par4);
}
当完成了上述后我们来看下使用示例:
public class Test2 : MonoBehaviour
{
private void Start()
{
GameEnvent.Listen<string>(CommandType.Test1, Say); }
private void Say(string Name)
{
print("Hello"+Name);
}
} public class Test3 : MonoBehaviour
{
private void Update()
{
if (Input.GetKeyDown(KeyCode.T))
GameEnvent.Execute<string>(CommandType.Test1, "NAME");
//按T后调用会以rpc方式调用 Test2类中的Say方法 且这个Say方法不需要添加[PunRpc]标记,这也是我们要达到的目的。
}
}
最后大家只要记住
1.GameEnvent.Broadcast 是本地调用
2.GameEnvent.Execute 是rpc同步调用
任何以GameEnvent.Execute方式执行的都会被以rpc方式同步调用
这种方式下开发时不需要考虑这个方法是同步方法 还是本地执行的方法。 我们只是在调用时使用不同的方式 GameEnvent.Broadcast / GameEnvent.Execute 就可以使一个函数随意成为本地或网络同步的方法,这个函数用来本地调用还是网络同步调用在着之间是可以很好很容易的切换。同时我们也去除了繁琐的标记。且任何方法通过GameEnvent.Execute执行都是以rpc方式调用的,而且不需要到处添加[PunRPC]标记
Unity - Photon PUN 本地与网络同步的逻辑分离 (二)的更多相关文章
- Unity - Photon PUN 本地与网络同步的逻辑分离 (一)
服务器大家可以使用Photon官网提供的,这样会变得很简单,直接搭建下就好.或者下载到本地开启本地端Photon服务器 (大家也可以使用和我一样方式有时间做了个winform 程序用来管理本地服务器开 ...
- 实现一个简易的Unity网络同步引擎——netgo
实现一个简易的Unity网络同步引擎Netgo 目前GOLANG有大行其道的趋势,尤其是在网络编程方面.因为和c/c++比较起来,虽然GC占用了一部分机器性能,但是出错概率小了,开发效率大大提升,而且 ...
- Unite 2017 | 从《闹闹天宫》看MOBA游戏里的网络同步技术
http://mp.weixin.qq.com/s/0v0EU79Q6rFafrh8ptlmhw 在Unite 2017 Shanghai案例分享专场,来自蓝港互动<闹闹天宫>项目组的主程 ...
- MOBA游戏的网络同步技术
转自:http://www.gameres.com/750888.html 在5月13日Unite 2017 案例分享专场上,蓝港互动<闹闹天宫>项目组的主程序陈实分享了MOBA游戏的网络 ...
- Photon PUN 一 介绍
有句话说的好 , 官网永远是最好的学习地方 . 虽然国内的资料不多 , 但是官网的资料还是很充足 , 这就带着英汉词典就着作阅读理解的劲头去官网学习吧 https://doc.photonengine ...
- 游戏中的网络同步机制——Lockstep(帧同步)
本文来自: https://bindog.github.io/blog/2015/03/10/synchronization-in-multiplayer-networked-game-lockste ...
- 手游后台PVP系统网络同步方案总结
游戏程序 平台类型: 程序设计: 编程语言: 引擎/SDK: 概述 PVP系统俨然成为现在新手游的上线标配,手游Pvp系统体验是否优秀,很大程度上决定了游戏的品质.从最近半年上线的新手 ...
- Photon PUN 三 RPCs & RaiseEvent
官方文档地址 https://doc.photonengine.com/en-us/pun/current/manuals-and-demos/rpcsandraiseevent 一, RPC P ...
- 百度云+ KeePass 网络同步你的密码
百度云+ KeePass 网络同步你的密码 百度云一个目前不限流量不限格式能直链的网盘,速度在我这里很快,难得了!KeePass(小众介绍过 KeePass.) 是一个免费开源的密码管理类软件, ...
随机推荐
- 转载及总结:cron表达式详解,cron表达式写法,cron表达式例子
cron表达式格式:{秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}例 "0 0 12 ? * WED" 在每星期三下午12:00 执行(年份通常 ...
- springboot学习随笔(二):简单的HelloWorld
接上章搭建好springboot环境后,开始开发入门级HelloWorld 一.构建简单的springboot项目 1.新建项目,选择Spring/Spring Starter Project 2.N ...
- Spring注解测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicatio ...
- 1_Python历史及入门
前提:简述CPU 内存 硬盘 操作系统 应用程序CPU:计算机的运算核心和控制核心,好像人类的”大脑“内存:负责数据与CPU直接数据交流处理,将临时数据和应用程序加载到内存,然后在交由CPU处理. 造 ...
- Java数据类型(Primivite 和引用数据类型!)
一.byte(8位) short(16位) int(32位) long(64位) float(32位) double(64位) boolean(Java虚拟机决定) true 或者false! ...
- NGINX轻松管理10万长连接
先说说服务为什么使用HTTPs长连接技术?有如下几个原因:对响应时间要求较高:服务走的是公网,客户端与服务端的TCP建立的三次握手和断开的四次握手都需要40ms左右(真实数据包计算出来的),共需要80 ...
- python_13 面向对象
面向对象 类:把一类事物的相同特征和动作整合到一起就是类,类是一个抽象的概念 对象:就是基于类出而创建的一个具体的事物或实例(具体存在),其特征和动作整合到一起 面向对象设计:将一类具体事物的数据和动 ...
- 用es5原生模仿-es6Promise异步处理
用es5原生模仿-es6Promise异步处理,不过在处理异常的时候有点小bug不是很完美,不过多级then 是没问题的和resolve, rejec 正常调用和异常处理调用是没问题的.本帖属于原创 ...
- mac安装linux双系统的吐槽
[First day] 尝试安装mac - linux 双系统 首先,尝试的是ubuntu16.06版本,要把双系统安装至电脑硬盘512G SSD中, *** 分盘 1.1 打开实用工具中的磁盘管理工 ...
- 5.LNMP(Linux + Nginx + MySQL + PHP)环境安装
1.安装Nginx: yum install yum-priorities -y wget http://nginx.org/packages/centos/7/noarch/RPMS/nginx-r ...