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.) 是一个免费开源的密码管理类软件, ...
随机推荐
- 啊哈算法第四章第二节解救小哈Java实现
package corejava; public class FourTwo { static int m;//(m,n)为几行几列 static int n; static int p;//(p,q ...
- JEECG3.8 全套实战视频全部开放,免费下载!
JEECG快速开发平台V3.8版本自去年10月份发布以来,下载使用数屡创新高,并受到众多开发者积极反馈.为帮助更多初学者能够快速上手,JEECG V3.8版本实战教程现已全面开放,免费下载!本教程深入 ...
- python杂记二
1. 写文件可以直接使用print函数 file_name = open("file_name.txt","w") print("file conta ...
- Echart ,X轴显示的为tooltip内显示的一部分内容放在上面显示的一部分如下图所示
如图所示:X轴只显示tooltip部分内容解决方案 在xAxis下面,实现方法如下 axisLabel: { interval: 0, formatter:function(value) { var ...
- redis+thinkphp5的注册、登陆、关注基础例子
最近初步接触redis,结合thinkphp5与redis,写了一个用户注册的基础例子,用于学习. 这个例子是结合了兄弟连的redis视频,最后两节的内容写的:https://study.163.co ...
- java swing button和键盘监听冲突问题
原因: 点击button会让jframe失去焦点,然后键盘监听不起作用 解决: 让jframe重新获取焦点就行了 jf.setFocusable(true); // JFrame jf = new J ...
- python中os模块操作目录与文件名小结
(1). 创建目录: SigleDir = 'sigle_layer' MultiDir = 'D:\\Web\\multi_layer' 创建单层目录: os.mkdir(SigleDir) 创建多 ...
- 给C#Control组件统一增加加属性
http://www.cnblogs.com/SharkXu/archive/2006/08/24/EnterGoto.html
- 《DOM Scripting》学习笔记-——第八章 充实文档的内容
本章内容 一.一个为文档创建“缩略词语表”的函数 二.一个为文档创建“文献来源链接”的函数 三.一个为文档创建“快速访问键清单”的函数 利用DOM动态的收集和创建一些有用的辅助信息,并把它们呈现在网页 ...
- Taro之使用百度地图
适配h5的时候要使用地图功能获取位置,选取了百度地图.首先在index.html文件引入. <script type="text/javascript" src=" ...