白轻易

博客园 首页 新随笔 联系 订阅 管理

服务器大家可以使用Photon官网提供的,这样会变得很简单,直接搭建下就好。或者下载到本地开启本地端Photon服务器

(大家也可以使用和我一样方式有时间做了个winform 程序用来管理本地服务器开启关闭等,不论用哪种方式下面要说的都是通用的)

在unity中我们使用 Photon Unity Networking Classic 这个官方免费的插件,地址 https://assetstore.unity.com/packages/tools/network/photon-unity-networking-classic-free-1786

 

下面我们先来简单看下 PUN 的同步是怎么使用的

void PhotonView.RPC (string methodName, PhotonTargets target, params object[] parameters)

从上面方法看 我们需要使用挂载了PhotonView的对象去调用 PRC 方法

下面看下参数的具体意思:

string methodName

要同步调用的方法名 该方法名必须加上 [PunRPC] 特性

PhotonTargets target  

PhotonTargets 是个枚举 表示消息以那种方式发送

public enum PhotonTargets
{
发送rpc消息给所有人包括自己
All,
向其他人发送rpc消息
Others,
仅向MasterClient发送rpc消息
MasterClient,
发送rpc消息给所有人包括自己,当新玩家加入会得到这个rpc缓冲
AllBuffered,
向其他人发送rpc消息,当新玩家加入会得到这个rpc缓冲
OthersBuffered,

一下两个都是由服务器发送的方式
AllViaServer,
AllBufferedViaServer
}

 

 

params object[] parameters

可变参数,用来写参数的,也就是methodName 方法的参数,如果没有就不写

下面举例:

1:
public
void WelcomeNewPlayer(string welcomeData) { GetComponent<PhotonView>().RPC("ConsoleNewPlayer", PhotonTargets.AllBuffered, welcomeData); } [PunRPC] public void ConsoleNewPlayer(string strData) { //TODO:逻辑 }

2:
public void WelcomeNewPlayer()
        {
            GetComponent<PhotonView>().RPC("ConsoleNewPlayer", PhotonTargets.AllBuffered); 
        }

        [PunRPC]
        public void ConsoleNewPlayer()
        {
           //TODO:逻辑
        }


 

 

总结:发送同步 rpc消息 要

1.进行发送的对象(使用Rpc这个方法的对象)需要挂在PhotonView 组件

2.要同步的方法 需要加上 [PunRPC] 特性

当然参数也只能发送一些基本参数,字典一类的就需要序列化了

由此我们可以想象 每个同步方法都要加上PunRPC 不仅如此由于同步方法必须还要由 挂载PhotonView 的对象来调用才行,这样做的话就要为每一个同步方法 再写个发送的方法,这样算下来着实麻烦,且容易混乱。

下面我们就来分析一下:

”使用挂载PhotonView 的对象发送一个带有 [PunRPC] 标识的方法可以实现同步“

以此思考我们就得出了结论 :

 使用一个专门负责发送rpc消息的单利对象用来作为消息传输的中介者。

但问题还是有的,使用其他还是麻烦,其实我们要达到的目标是这样的:

1.使用一个通用的静态类来调用(比单利使用起来方便)。

2. 我们不想写 [PunRPC]这个特性。

3. 为什么还要单独为一个同步方法写个调用它的方法,不能接受。

 

下面我们就对这3点要求进行实现:

为了不到处添加 [PunRPC] 这个特性就考虑使用 Delegate代替所有要同步的方法,将delegate放入字典中,通过传key与参数 实现跳过[PunRPC],当然实际还是通过punRPC调用的只不过我们开发中调用的仅仅只是同一个方法,简单说就是通过一个带有[PunPRC]的方法

来调用所有需要同步的方法。下面我们就开始了:

既然要使用Delegate绑定所有同步方法那就自然的想到了使用事件系统的方式来做,下面完整介绍下这个简单事件系统,下一篇说下事件系统与rpc同步的结合使用来跳过一系列麻烦的操作实现本地与同步逻辑分离。

事件系统通过字符串或枚举作为key,来调用value中保存的所有方法

1.写个枚举

 public enum CommandType
 {
        Test,
 }

 

2.由于我们是想把所有同步方法都储存起来因此需要写个泛型的delegate来绑定不同类型的方法

    public delegate void CallFunction();

    public delegate void CallFunction<T>(T arg);

    public delegate void CallFunction<T, U>(T arg1, U arg2);

    public delegate void CallFunction<T, U, O>(T arg1, U arg2, O arg3);

    public delegate void CallFunction<T, U, O, P>(T arg1, U arg2, O arg3, P arg4);

 

3.下面要做的就是要写个用来绑定和调用的类,类名: GameEnvent

        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();
        }
View Code

 这个类只是看着内容多,其实就是三个函数,Listen (注册) , Remove (移除) Broadcast(执行),其他的都是根据他们各自的泛型扩展而已

下面演示下调用

  

void Start() {       //注册 GameEnvent.Listen(CommandType.Test, TestPrintLocalHost); //执行 } [PunRPC] public void TestPrintLocalHost() { print("LocalHOST:************"); }

private void Update()
{
  if (Input.GetKeyDown(KeyCode.T))
  GameEnvent.Broadcast(CommandType.Test);
}

 

//这样就可以按T输出

 

//”LocalHOST:************“

 

--------------------------------------------------

//泛型

 

void Start()
        {
       //注册
            GameEnvent.Listen<string>(CommandType.Test, TestPrintLocalHost1);
            //执行
        }

        [PunRPC]
        public void TestPrintLocalHost1(string str)
        {
            print(str);
        }

 

private void Update()
{
  if (Input.GetKeyDown(KeyCode.T))
  GameEnvent.Broadcast<string>(CommandType.Test,"Hello");
}

 

// 这样就可以输出 "Hello"

 


现在我们只是使用了事件系统来绑定和调用方法,下一篇我们来跳过 [PunRPC]这个繁琐的标记。

 

posted on 2018-11-20 16:16  白轻易  阅读(3069)  评论(0编辑  收藏  举报