[Unity热更新]tolua# & LuaFramework(九):网络通信实例

最近老师布置了一个作业,是做一个21点游戏,既可以跟AI玩,也可以进行网络对战。对于网络这块,因为最近接触了LuaFramework,它在通信方面也进行了一层封装,看了一下,觉得还是挺不错的,所以就打算用它来搞。因为客户端和服务器端的工程体积挺大的,所以就不放出来了。其实模仿框架中的例子已经可以做出很多东西了,所以这里就说一下额外的比较关键的东西。


游戏截图:






服务器端:

1.首先我们需要定义一些消息。在服务端找到Protocal.cs,复制下面的东西。在客户端也有一个Protocal.cs,同时也有一个protocal.lua。都复制复制吧。。。对于客户端来说,如果你想用c#写网络相关逻辑,那肯定要复制到Protocal.cs中;如果想用lua写,那就复制到protocal.lua中。前者优点是写起来很舒服,调试很方便,但不适用于热更,后者就相反。

        Wait = 3000,        //客户端等待
        WantEnter = 3001,   //客户端请求进入房间
        Enter = 3002,       //服务器允许进入房间
        HadEnter = 3003,    //客户端已进入房间
        ShowWantCard = 3004,//服务端显示要牌按钮
        WantCard = 3005,    //服务端允许要牌
        Card = 3006,        //服务端发牌
        NotWantCard = 3007, //客户端不要牌
        ShowResult = 3008,  //客户端显示结果

2.然后就是匹配的事情,我们需要把玩家都放在同一间房间中。

using SimpleFramework.Common;
using SimpleFramework.Message;
using SimpleFramework.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleFramework.MyCode21Game
{
    abstract class Room 
    {
        protected int number;//房间号
        protected int capacity;//房间容量
        protected int hadReadyCount;//已经准备好的玩家数
        protected List<ClientSession> playerList;

        public Room(int number, int capacity)
        {
            this.number = number;
            this.capacity = capacity;
            this.hadReadyCount = 0;
            this.playerList = new List<ClientSession>();       
        }

        public void AddReadyPlayerCount()
        {
            hadReadyCount++;
            if (hadReadyCount == capacity) OnAllReady();
        }

        public void RemoveReadyPlayerCount()
        {
            hadReadyCount--;
        }

        public void AddPlayer(ClientSession clientSession)
        {
            clientSession.roomId = number;//房间ID,或者是地图ID
            clientSession.roomIndex = playerList.Count;//房间序号,即表明是第几个玩家
            playerList.Add(clientSession);

            if (playerList.Count == capacity) OnFull();
            else OnNotFull();
        }

        public void RemovePlayer(ClientSession clientSession)
        {
            clientSession.roomId = -1;
            clientSession.roomIndex = -1;
            playerList.Remove(clientSession);

            if (playerList.Count == 0) OnClear();
        }

        public bool IsFull()
        {
            if (playerList.Count < capacity) return false;
            else return true;
        }

        public virtual void OnAllReady()
        {
        }

        public virtual void OnFull()
        {
        }

        public virtual void OnNotFull()
        {
        }

        public virtual void OnClear()
        {
        }

        //一般为一局游戏结束后调用
        public virtual void Reset()
        {
            hadReadyCount = 0;
        }

        public virtual void Destroy()
        { 
        }
    }
}

3.上面的是抽象类,我们可以继承它,实现一个棋牌房间类。

using SimpleFramework.Common;
using SimpleFramework.Message;
using SimpleFramework.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SimpleFramework.MyCode21Game
{
    class CardRoom : Room
    {
        private List<Card> cards;

        public CardRoom(int number, int capacity) : base(number, capacity)
        {
            cards = CardManager.Instance.GetCards();
        }

        public override void OnAllReady()
        {
            base.OnAllReady();
            Console.WriteLine("所有玩家已经准备好,开始发牌!!!");
   
            foreach (ClientSession client in playerList)
            {
                Deal(client);
            }

            Thread.Sleep(1500);

            foreach (ClientSession client in playerList)
            {
                Deal(client);
            }

            //同时第一个玩家可以要牌
            ByteBuffer bb = new ByteBuffer();
            bb.WriteByte((byte)ProtocalType.BINARY);
            SocketUtil.SendMessage(playerList[0], Protocal.ShowWantCard, bb);
        }

        public override void OnFull()
        {
            base.OnFull();
            Console.WriteLine("满人!!!");
            foreach (ClientSession cs in playerList)
            {
                ByteBuffer newBuffer = new ByteBuffer();
                newBuffer.WriteByte((byte)ProtocalType.BINARY);
                newBuffer.WriteString("匹配成功,房间号是:" + number);
                newBuffer.WriteInt(cs.roomId);//设置客户端
                newBuffer.WriteInt(cs.roomIndex);
                SocketUtil.SendMessage(cs, Protocal.Enter, newBuffer);
            }
        }

        public override void OnNotFull()
        {
            base.OnNotFull();
            Console.WriteLine("还没满人!!!");
            foreach (ClientSession cs in playerList)
            {
                ByteBuffer newBuffer = new ByteBuffer();
                newBuffer.WriteByte((byte)ProtocalType.BINARY);
                newBuffer.WriteString("人数不足,请等待...");
                SocketUtil.SendMessage(cs, Protocal.Wait, newBuffer);
            }
        }

        public override void Reset()
        {
            base.Reset();
            Console.WriteLine("重置房间");
            cards = CardManager.Instance.GetCards();
        }

        //抽牌
        public void Deal(ClientSession session)
        {
            Card card = cards[0];
            cards.RemoveAt(0);
            Console.WriteLine("玩家为:" + session.roomIndex + "牌为:" + card.ToString());

            ByteBuffer newBuffer = new ByteBuffer();
            newBuffer.WriteByte((byte)ProtocalType.BINARY);

            newBuffer.WriteInt(session.roomIndex);//第几个玩家
            newBuffer.WriteString(card.ToString());

            foreach (ClientSession client in playerList)
            {
                SocketUtil.SendMessage(client, Protocal.Card, newBuffer);
            }
        }

        //下一位玩家
        public void Next(ClientSession session)
        {
            ByteBuffer bb = new ByteBuffer();
            bb.WriteByte((byte)ProtocalType.BINARY);

            int nextIndx = session.roomIndex + 1;
            if (nextIndx < capacity)
            {
                SocketUtil.SendMessage(playerList[nextIndx], Protocal.ShowWantCard, bb);
            }
            else
            {
                foreach (ClientSession client in playerList)
                {
                    SocketUtil.SendMessage(client, Protocal.ShowResult, bb);
                }
                Reset();
            }
        }
    }
}

4.因为房间有很多,所以我们需要对它进行管理。

using SimpleFramework.Common;
using SimpleFramework.Message;
using SimpleFramework.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleFramework.MyCode21Game
{
    class RoomManager : CSharpSingletion<RoomManager> 
    {
        public Dictionary<int, Room> roomDir = new Dictionary<int, Room>();//房间号,房间
        private int roomNum = 0;//不要使用字典长度作为key

        //顺序分配
        public int GetDefaultRoomNum()
        {
            if (!roomDir.ContainsKey(roomNum)) return roomNum;
            if (!roomDir[roomNum].IsFull()) return roomNum;

            roomNum++;
            return roomNum;
        }
    }
}


客户端:

1.框架默认的是通过SocketCommand.cs将消息分发到lua中,在lua中写逻辑,这样逻辑随便写也没关系,因为可以通过热更新换掉。而我这里为了加快开发效率,直接在c#中写逻辑,因此就需要修改一下框架,将消息在c#中进行分发。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using LuaFramework;

public class SocketMessageManager : MonoSingletion<SocketMessageManager> {

    public delegate void SocketMessage(KeyValuePair<int, ByteBuffer> dir);
    public event SocketMessage socketMessage;

    public void Dispatch(KeyValuePair<int, ByteBuffer> dir)
    {
        Debug.Log(dir.Key.ToString());
        if (socketMessage != null) socketMessage(dir);   
    }

}

找到NetworkManager.cs,修改:

        void Update() {
            if (sEvents.Count > 0) {
                while (sEvents.Count > 0) {
                    KeyValuePair<int, ByteBuffer> _event = sEvents.Dequeue();
                    //facade.SendMessageCommand(NotiConst.DISPATCH_MESSAGE, _event);
                    SocketMessageManager.Instance.Dispatch(_event);
                }
            }
        }

2.那么,我们就可以通过订阅事件来响应消息了。例如我的PanelMain.cs(一个panel对应一个.cs,那么转化为.lua时就很方便了)

    public void HandleSockMessage(KeyValuePair<int, ByteBuffer> dir)
    {
        switch (dir.Key)
        {
            case Protocal.ShowWantCard:
                if (CardManager.Instance.CountCard(Game21Manager.Instance.player) < 21)
                {
                    ShowButtonWantCardAndNotWantCard();
                }
                break;

            case Protocal.Card:
                dir.Value.ReadByte();
                int roomIndex = dir.Value.ReadInt();
                string card = dir.Value.ReadString();

                if (roomIndex == Game21Manager.Instance.roomIndex)//如果是自己的牌
                {
                    GetCard(card);
                }
                else
                {
                    CardManager.Instance.GetCard(banker, new Card(card));
                }
                break;

            case Protocal.ShowResult:
                ShowCard();
                break;

            default:
                break;
        }
    }


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值