以罗培羽的《Unity3D网络游戏实战》最终项目为基础,添加排行榜(Scroll View的特殊使用)

在添加排行榜和商店之前,我们得先了解罗培羽网络游戏实战的框架是如何进行通信的,接下来我以他制作的登录功能为基础,带大家初步了解该项目基本网络通信方式,方便大家在后续了解我添加的内容。

两端接收和发送消息方式

首先查看的是客户端中登录面板(LoginPanel)的登录内容。查找并打开LoginPanel.cs脚本内容,由于罗培羽面板中的功能都是通过在脚本的编写统一实现的,所以Panel中的按钮他都是通过代码的方式添加Button.onClick事件。以下是LoginPanel.cs中有关登录按钮的内容。

    //当面板被打开    
    public override void OnShow(params object[] args) 
    {
		//寻找组件
        loginBtn = skin.transform.Find("LoginBtn").GetComponent<Button>();
		//监听
		loginBtn.onClick.AddListener(OnLoginClick);
		//网络协议监听
		NetManager.AddMsgListener("MsgLogin", OnMsgLogin);
	}

    //当按下登陆按钮
    public void OnLoginClick()
    {
        //用户名密码为空
        if (idInput.text == "" || pwInput.text == "")
        {
            PanelManager.Open<TipPanel>("用户名和密码不能为空");
            return;
        }
        //发送
        MsgLogin msgLogin = new MsgLogin();
        msgLogin.id = idInput.text;
        msgLogin.pw = pwInput.text;
        NetManager.Send(msgLogin);
    }

    //收到登陆协议
    public void OnMsgLogin(MsgBase msgBase)
    {
        MsgLogin msg = (MsgLogin)msgBase;
        if (msg.result == 0)
        {
            Debug.Log("登陆成功");
            //设置id
            GameMain.id = msg.id;
            //打开房间列表界面
            PanelManager.Open<RoomListPanel>();
            //关闭界面
            Close();
        }
        else
        {
            PanelManager.Open<TipPanel>("登陆失败");
        }
    }

从该代码中可以看到,LoginPanelOnShow()的时候给LoginButton添加了OnLoginClick(),通过该方法发送消息给服务端,我们再查看OnLogin()中的通信代码,发现该函数的最后一行使用NetManager.Send(MsgBase msg)方法发送消息,也就是说客户端要与服务端通信就必须要像OnLoginClick()方法一样,需要先new MsgBase()然后通过NetManger.Send()发送消息。

在MsgBase类中有个字段protoName(协议名),这个协议名代表,两端在解析出协议名之后,需要反射到对应的方法并执行该方法,例如OnShow()中最后一段

NetManager.AddMsgListener("MsgLogin", OnMsgLogin);

这段表示,当服务端发送消息给客户端后,客户端解析出发送的协议名MsgBase.protoName = "MsgLogin",然后调用已经通过NetManager.AddMsgListener()添加的事件,也就是说服务端返回"MsgLogin"后会调用函数OnMsgLogin(MsgBase msgBase)判断是否登录成功。

服务端的通信解析方式与客户端相同,我就不过多介绍了,这边需要注意到的是,两端在通信的过程中要保证两端都有对应的MsgBase,如果没有对应的MsgBase就无法使用罗培羽使用的协议方法去解析通信内容,会报错。

添加排行榜

通过上一部分内容了解了该游戏项目中服务端和客户端的通信方式和消息解析方式。所以添加排行榜这部分得先制作排行榜面板,这边我将排行榜面板取名为RankingPanel,并对应的创建名字相同的预制体和脚本。

RankingPanel.cs脚本内容如下:

public class RankingPanel : BasePanel
{
    //private BattleMsgLogic logic;
    public override void OnInit()
    {
        skinPath = "RankingPanel";
        layer = PanelManager.Layer.Panel;
    }

    public override void OnShow(params object[] para)
    {
        skin.transform.Find("BackButton").GetComponent<Button>().onClick.AddListener(OnBackButtonClick);
    }

    public void OnBackButtonClick()
    {
        PanelManager.Open<RoomListPanel>();
        Close();
    }
}

在面板中创建RankingPanel并将该对象创建成预制体,排行榜截图如下:

Scroll View可自动拓展大小

 在排行榜下边我创建了一个Scroll View、部分Text还有一个返回按钮,这边需要注意的是Scroll View组件下的Viewport中有个有个content对象,该对象下边存放着Scroll View内容,但是这个content对象大小是固定的,本身是无法实现自适应拓展(通过子物体的长度自动扩张),所以我现在需要content的大小能自适应,因为排行榜的信息是不确定的,可能服务器有100个人,也有可能只有2个人,所以我不可能一开始就使用100个人的大小的画布。当我在解决这个问题的时候查看到了官方关于这部分的文档,链接如下:

使 UI 元素适应其内容的大小 - Unity 手册

通过该文档了解到如果UI需要适应内容大小需要添加以下两个组件:Vertical Layout Group和Content Size Fitter,将这两个组件添加到content对象中,以下是我设置后的content截图

 

这里需要注意:Rect Transform组件的锚点需要设置在顶上,这样content里的内容才可以自上而下的拓张。

 RankingPanelLogic代码内容

我创建了一个新的脚本RankingPanelLogic.cs,该脚本方便我在Inspector中添加和修改RankingPanel面板中的内容。

创建完成面板内容后,我们需要用到第一部分的网络内容,客户端发送请求让服务端发回所有数据库中的玩家信息。

客户端

发送消息第一步,先创建两边都有的消息协议名和类,我取名为MsgGetAllPlayerInfo;由于该项目的服务端自带了PlayerData类,所以我直接在客户端也创建了同样的类,方便两边传送玩家数据。

这边需要注意:客户端的PlayerData类需要序列化,我不知道为啥,有没有懂哥教一下,懒得看书了(狗头保命)。

public class MsgGetAllPlayerInfo : MsgBase
{
    public MsgGetAllPlayerInfo() { protoName = "MsgGetAllPlayerInfo"; }
    public List<PlayerData> playerData = new List<PlayerData>();
}
[System.Serializable]
public class PlayerData
{
    //金币
    public int coin = 0;
    //记事本
    public string text = "new text";
    //胜利数
    public int win = 0;
    //失败数
    public int lost = 0;
    //坦克颜色
    public string color = "#ffffff";
}

服务端

服务端需要反射客户端发送回来的信息,所以同样要像客户端一样添加MsgGetAllPlayerInfo类和方法,以下代码是方法:

    //发送回所有玩家信息
    public static void MsgGetAllPlayerInfo(ClientState c,MsgBase msgBase)
    {
        MsgGetAllPlayerInfo msg =(MsgGetAllPlayerInfo)msgBase;
        msg.playerData = DbManager.GetPlayerData();
        if (msg != null)
        {
            NetManager.Send(c, msg);
        }
    }

服务端的MsgGetAllPlayerInfo类内容和客户端一样就不展示了,从代码中可以看到,我使用了服务端中DbManager类中的内容,DbManager类实现的是服务端获取数据库中内容功能,所以我如果要传回玩家信息到客户端需要在DbManager中添加玩家信息获取方法,我在DbManager中创建了GetPlayerData();方法,方法代码如下:

 public static List<PlayerData> GetPlayerData()
    {
        CheckAndReconnect();
        //sql
        //string sql = string.Format("select * from player where id ='{0}';", id);
		string sql_count = string.Format("select count(data) from player");
        try
        {
            MySqlCommand cmd = new MySqlCommand(sql_count, mysql);
            MySqlDataReader dataReader = cmd.ExecuteReader();
            if (!dataReader.HasRows)
            {
                dataReader.Close();
                return null;
            }
			dataReader.Read();
			int data_count = dataReader.GetInt16("count(data)");
            dataReader.Close();
            List<PlayerData> playerData = new List<PlayerData>();

            for (int i = 0; i < data_count; i++)
			{
				//limit查询单个数据
				string sql = string.Format("select data from player limit {0},{1}", i, i + 1);
                //查询
                cmd = new MySqlCommand(sql, mysql);
                dataReader = cmd.ExecuteReader();
                if (!dataReader.HasRows)
                {
                    dataReader.Close();
                    return null;
                }
                //读取
                dataReader.Read();
				string data = dataReader.GetString("data");
                //反序列化
                playerData.Add(Js.Deserialize<PlayerData>(data));
                dataReader.Close();
            }
            //return playerData;
			//返回playerdata数组
			return playerData;
        }
        catch (Exception e)
        {
            Console.WriteLine("[数据库] GetPlayerData fail, " + e.Message);
            return null;
        }
    }

差不多到这里就写完代码内容了,我就不多阐述代码有啥用了,都是蛮基础的内容,大家就随便看看吧。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值