MMO角色创建与选择:应用通讯协议

这篇演示应用通讯协议

演示从登录界面进入

打开数据库SSMS;启动服务器

从Loading场景进入

输入已经注册过的账号和密码

//这里的登录按钮有切换场景的逻辑(在Cilent中

void OnLogin(Result result, string message)
{
    if (result == Result.Success)
    {
        //登录成功进入角色选择
        //MessageBox.Show("登录成功,准备角色选择"+message,"提示",message,"提示",MessageBoxType.Information);
        //切换到CharSelect场景
        SceneManager.Instance.LoadScene("CharSelect");
    }
    else
        MessageBox.Show(message, "错误", MessageBoxType.Error);
}

//这里的Scene Manager需要挂在场景的一个空物体上才能初始化

//由于我们要从Loading场景转换到CharSelect场景;在Edit的Build里面把这两个场景勾上

//成功登进去了友友们!

角色创建

给开启冒险按钮添加事件

OnClickCreate

角色名称!=null校验

创建角色需要让服务器知道角色名称和职业;传入两个参数

 public void OnClickCreate()
 {
     if(string.IsNullOrEmpty(this.charName.text))
     {
         MessageBox.Show("请输入角色名称");
         return;
         //不能把空消息发给服务器
     }
     //启用协议创建角色
     UserService.Instance.SendCharacterCreate(this.charName.text, this.charClass);   
 }
在客服端添加发送
添加UserService:SendCharacterCreate服务

//用来发给服务器角色创建的信息

//发给服务器的所以这里是Request请求

//在NetMessageRequest里面包含了所需的请求访问器

public void SendCharacterCreate(string charName, CharacterClass cls)
{
    Debug.LogFormat("SendCharacterCreate::charName :{0} cls:{1}", charName, cls);
    NetMessage message = new NetMessage();
    message.Request = new NetMessageRequest();
    message.Request.createChar = new UserCreateCharacterRequest();
    message.Request.createChar.Name = charName;
    message.Request.createChar.Class = cls;

    //这下面的都跟其他消息发送服务相同
    if (this.connected && NetClient.Instance.Connected)
    {
        this.pendingMessage = null;
        NetClient.Instance.SendMessage(message);
    }
    else
    {
        this.pendingMessage = message;
    }
}
添加OnUserCreateCharacter接收服务

//响应结束后通过这个回馈给UI层

 //接收
 void OnUserCreateCharacter(object sender, UserCreateCharacterResponse response)
 {
     Debug.LogFormat("OnUserCreateCharacter:{0} [{1}]", response.Result, response.Errormsg);

     if(response.Result == Result.Success)
     {
         Models.User.Instance.Info.Player.Characters.Clear();
         Models.User.Instance.Info.Player.Characters.AddRange(response.Characters);
     }
     //创建成功之后把返回一个角色列表;把角色填充到角色列表中

     if (this.OnCharacterCreate != null)
     {//通过事件通知UI 层
         this.OnCharacterCreate(response.Result, response.Errormsg);
     }
 }
 void OnCharacterCreate(Result result,string message)
 {
     if(result== Result.Success)
         InitCharacterSelect(true);
     else MessageBox.Show(message,"错误",MessageBoxType.Error);
 }
添加订阅OnUserCreateCharacter

接收方法必须要订阅才能有效

这里订阅的是角色创建响应的协议:UserCreateCharacterResponse

监听接收服务事件

在Dispose也加//订阅和取消订阅成对出现

OnCharacterCreate在Game面板上显示通知

显示角色创建是否成功

        public UnityEngine.Events.UnityAction<Result, string> OnCharacterCreate;
void OnCharacterCreate(Result result,string message)
{
    if(result== Result.Success)
        InitCharacterSelect(true);
    else MessageBox.Show(message,"错误",MessageBoxType.Error);
}

//UnityAction是Unity的一种事件;可以传两个参数

//如果有订阅了创建成功的消息,在OnUserCreateCharacter中判断是否为空

//在角色创建面板启动订阅

启动订阅的位置

在UICharacterSelect脚本挂的角色选择页面上需要得到角色创建是否成功的消息

因此在角色选择的面板打开时就启动订阅:

void Start()
{
  //  DataManager.Instance.Load();   
    InitCharacterSelect(true);//初始化当前点击选择的角色
    UserService.Instance.OnCharacterCreate = OnCharacterCreate;
}

//注意等号左边的OnCharacterCreate在UserService中,是UnityAction

//右边的在UICharacterSelect中

创建成功:InitCharacterSelect打开选择角色面板

public void InitCharacterSelect(bool init)
{
    panelCreate.SetActive(false);
    panelSelect.SetActive(true);

}

逻辑是创建成功返回角色选择面板

在服务端接收
订阅OnCreateCharacter

请求协议下的创建角色事件OnCreateCharacter

 public UserService()
 {
     //创建一个分发器的单例然后执行订阅;这里订阅登录消息,这是登陆消息的处理器:UserLoginRequest//协议
     MessageDistributer<NetConnection<NetSession>>.Instance.Subscribe<UserLoginRequest>(this.OnLogin);
     MessageDistributer<NetConnection<NetSession>>.Instance.Subscribe<UserRegisterRequest>(this.OnRegister);
     MessageDistributer<NetConnection<NetSession>>.Instance.Subscribe<UserCreateCharacterRequest>(this.OnCreateCharacter);

 }

OnCreateCharacter

void OnCreateCharacter(NetConnection<NetSession> sender,UserCreateCharacterRequest request)
{
    Log.InfoFormat("UserCreateCharacterRequest: Name:{0}  Class:{1}", request.Name, request.Class);

    
    //TCharacter是提前建好的一个类,里面有关于Character的所有字段的访问器
    TCharacter character = new TCharacter()
    {//创建新角色要把客户端传进来的信息填充
        Name = request.Name,
        Class = (int)request.Class,//这里加一个强制转换;数据库不允许枚举值
        TID=(int)request.Class,
        //mapID:第一次出现的时候要出现在那张地图;哪个位置
        MapID=1,//主城;某次登录需要用到上一次的位置
        MapPosX=5000,
        MapPosY=4000,
        MapPosZ=820,//这是地图的中心位置;单位是cm

    };

    DBService.Instance.Entities.Characters.Add(character);
//前面OnLogin里创建了用户User
    sender.Session.User.Player.Characters.Add(character);
    //对数据库做的任何增删改查的操作之后都要保存
    DBService.Instance.Entities.SaveChanges();

    //准备要发送客户端的信息
    NetMessage message = new NetMessage();
    message.Response = new NetMessageResponse();
    message.Response.createChar = new UserCreateCharacterResponse();

    //回发消息吧结果告诉用户;创建是否成功
    message.Response.createChar.Result = Result.Success;
    message.Response.createChar.Errormsg = "None";

    byte[] data = PackageHandler.PackMessage(message);
    sender.SendData(data, 0, data.Length);
}

创建了新的角色

创建新表!

//T开头的类代表EF框架使用的entity映射的对象//对应一个数据表里的对象

//TCharacter是提前建好的一个类,里面有关于Character的所有字段的访问器
TCharacter character = new TCharacter()
{//创建新角色要把客户端传进来的信息填充
    Name = request.Name,
    Class = (int)request.Class,//这里加一个强制转换;数据库不允许枚举值
    TID=(int)request.Class,
    //mapID:第一次出现的时候要出现在那张地图;哪个位置
    MapID=1,//主城;某次登录需要用到上一次的位置
    MapPosX=5000,
    MapPosY=4000,
    MapPosZ=820,//这是地图的中心位置;单位是cm

};

把刚建的新表加上

//Session会话 是内存中的角色对象;每次登录的用户信息会保存在Session中;

//创建新角色的同时更新Session,就不需要重新拉一个DB

DBService.Instance.Entities.Characters.Add(character);
sender.Session.User.Player.Characters.Add(character);
//对数据库做的任何增删改查的操作之后都要保存
DBService.Instance.Entities.SaveChanges();

填充信息,打包

//准备要发送客户端的信息
NetMessage message = new NetMessage();
message.Response = new NetMessageResponse();
message.Response.createChar = new UserCreateCharacterResponse();

//回发消息吧结果告诉用户;创建是否成功
message.Response.createChar.Result = Result.Success;
message.Response.createChar.Errormsg = "None";

byte[] data = PackageHandler.PackMessage(message);
sender.SendData(data, 0, data.Length);

完整的OnCreateCharacter

void OnCreateCharacter(NetConnection<NetSession> sender,UserCreateCharacterRequest request)
{
    Log.InfoFormat("UserCreateCharacterRequest: Name:{0}  Class:{1}", request.Name, request.Class);

    
    //TCharacter是提前建好的一个类,里面有关于Character的所有字段的访问器
    TCharacter character = new TCharacter()
    {//创建新角色要把客户端传进来的信息填充
        Name = request.Name,
        Class = (int)request.Class,//这里加一个强制转换;数据库不允许枚举值
        TID=(int)request.Class,
        //mapID:第一次出现的时候要出现在那张地图;哪个位置
        MapID=1,//主城;某次登录需要用到上一次的位置
        MapPosX=5000,
        MapPosY=4000,
        MapPosZ=820,//这是地图的中心位置;单位是cm

    };

    DBService.Instance.Entities.Characters.Add(character);
    sender.Session.User.Player.Characters.Add(character);
    //对数据库做的任何增删改查的操作之后都要保存
    DBService.Instance.Entities.SaveChanges();

    //准备要发送客户端的信息
    NetMessage message = new NetMessage();
    message.Response = new NetMessageResponse();
    message.Response.createChar = new UserCreateCharacterResponse();

    //回发消息吧结果告诉用户;创建是否成功
    message.Response.createChar.Result = Result.Success;
    message.Response.createChar.Errormsg = "None";

    byte[] data = PackageHandler.PackMessage(message);
    sender.SendData(data, 0, data.Length);
}
//关于角色名称输入框支持中文

在InputFiled中选择

演示创建角色

登陆->创建角色按钮->选择弓箭手;输入名字->开启游戏按钮:

页面会回到SelectCharacter;但是创建的角色没有显示在Srcoll view中//这部分业务未做

成功发到服务端://Game Server中

//在服务器面板打印出了

客户端也接收到了响应://Client中

//在日志中打印了

我画了一个信息传输示意图//或许不完全正确

角色成功被我们创建在数据库中了;现在需要把创建的角色刷新显示在选择角色的页面

切换已创建角色(按钮)状态
首先绑定CharInfo

//content是角色列表;Char Info是一个角色信息绑定到UICharacterSelect的脚本上

通过脚本初始化:角色创建成功;登录时服务端发来角色列表用到CharInfo更新

角色列表填充InitCharacterSelect

角色创建后需要刷新选择页面

在InitCharacterSelect中获取并初始化charInfo

给动态创建的Button添加监听事件:选择角色

public void InitCharacterSelect(bool init)
{
    panelCreate.SetActive(false);
    panelSelect.SetActive(true);

    if(init)
    {//把原来有的销毁
        foreach(var old in uiChars)
        {
            Destroy(old);
        }
        uiChars.Clear();

        //登录成功后信息会到charInfo
        //User中会包含Player的角色信息;登录或创建角色成功,User中的charInfo会更新
        for(int i=0;i<User.Instance.Info.Player.Characters.Count;i++)
        {//遍历所有角色
            GameObject go = Instantiate(uiCharInfo, this.uiCharList);
            //                            charInfo     根节点滚动区
            //初始化设置名字
            UICharInfo charInfo=go.GetComponent<UICharInfo>();
            charInfo.info = User.Instance.Info.Player.Characters[i];

            //Button初始化因为Button在charInfo上
            Button button=go.GetComponent<Button>();
            int idx = i;
            //这里的Button是动态创建的;因此对点击事件添加监听器
            button.onClick.AddListener(() =>
                {
                    OnSelectCharacter(idx);//执行选择角色
                });
            uiChars.Add(go);
            go.SetActive(true);
        }
    }
}

这里的方法是把原来uiChars列表里面的对象全部销毁;又把(可能包括新加进来的角色)全部角色都创建一个

选中后角色框高亮

UICharInfo.cs

//UICharInfo是CharIfo(每个创建出来的角色一个框)的脚本;里面有每个显示框需要的字段

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UICharInfo : MonoBehaviour {


    public SkillBridge.Message.NCharacterInfo info;

    public Text charClass;
    public Text charName;
    public Image highlight;

    public bool Selected
    {//某个节点是否被选中
        get { return highlight.IsActive(); }//被选中  
        set
        {
            highlight.gameObject.SetActive(value);
        }
    }

    // Use this for initialization
    void Start () {
		if(info!=null)
        {
            this.charClass.text = this.info.Class.ToString();
            this.charName.text = this.info.Name;
        }
	}
}
OnSelectCharacter(int idx)
public void OnSelectCharacter(int idx)
    {
        this.selectCharacterIdx = idx;
        //知道当前的索引
        var cha = User.Instance.Info.Player.Characters[idx];
        //按下按钮打印当前选择的已创建的角色信息
        Debug.LogFormat("Select Char:[{0}]{1}[{2}]", cha.Id, cha.Name, cha.Class);
        User.Instance.CurrentCharacter = cha;
        characterView.CurrectCharacter = ((int)cha.Class - 1);

        //点击的显示高亮,其他的不显示
        for (int i = 0; i < User.Instance.Info.Player.Characters.Count; i++)
        {
            //获取当前点击事件的对象
            UICharInfo ci = this.uiChars[i].GetComponent<UICharInfo>();
            ci.Selected = (idx==i);
        }
    }

//关于创建角色后角色列表的角色都消失了(未解决

服务器创建角色后没有把角色列表返回给客户端

总结!角色创建逻辑

Service层是处理对UI事件的订阅;对网络消息的订阅和收发响应;

UserService则是所有和用户相关的网络通讯;和一些基本的逻辑

//UICharacterSelect中

->OnClickCreate()点击创建按钮时把信息发给服务端

//UserService中

->SendCharacterCreate(在UserService中添加)构建消息发送//使用的是createChar协议

->OnUserCreateCharacter处理创建角色的返回//这样才知道角色创建之后是否成功

//UICharacterSelect中

->OnSelectCharacter切换按钮的状态

->打开选择角色面板或创建角色出现角色列表;InitCharacterSelect

//关于Session

当不同消息从给客户端发到服务端时,通过Session知当前接收到的是谁,用户是谁,以及拿到其他的消息

//关于端口

在Game Server中的Init初始端口8000

但服务器要做配置:

int Port = Properties.Settings.Default.ServerPort;
network = new NetService();
network.Init(Port);

这种配置在GameServer项目属性中

点击设置:

在这里面加完配置保存过后,会自动生成这个命名空间:Properties.Settings.Default.

生成的配置在这个里面

最终会体现在App.Config 中

//关于SQLServer配置管理器,可以在SQLServer服务中看SQL Server是否运行//保证服务器一定启动

//EF访问数据库(未解决

一种对象关系映射器;

在创建新角色时,用特定类New一个对象;通过实体类访问数据库:把新建角色放进DBService

//TCharacter是提前建好的一个类,里面有关于Character的所有字段的访问器
TCharacter character = new TCharacter()
{//创建新角色要把客户端传进来的信息填充
    Name = request.Name,
    Class = (int)request.Class,//这里加一个强制转换;数据库不允许枚举值
    TID=(int)request.Class,
    //mapID:第一次出现的时候要出现在那张地图;哪个位置
    MapID=1,//主城;某次登录需要用到上一次的位置
    MapPosX=5000,
    MapPosY=4000,
    MapPosZ=820,//这是地图的中心位置;单位是cm

};

DBService.Instance.Entities.Characters.Add(character);
sender.Session.User.Player.Characters.Add(character);
//对数据库做的任何增删改查的操作之后都要保存
DBService.Instance.Entities.SaveChanges();

创建角色后角色被放进数据库,显示已创建角色列表,需要在找到对应的用户创建的所有角色;

//登录成功后信息会到charInfo
//User中会包含Player的角色信息;登录或创建角色成功,User中的charInfo会更新
           
for(int i=0;i<User.Instance.Info.Player.Characters.Count;i++)
{//遍历所有角色
    GameObject go = Instantiate(uiCharInfo, this.uiCharList);
    //                            charInfo     根节点滚动区
    //初始化
    UICharInfo charInfo=go.GetComponent<UICharInfo>();
    charInfo.info = User.Instance.Info.Player.Characters[i];

    //Button初始化因为Button在charInfo上
    Button button=go.GetComponent<Button>();
    int idx = i;
    button.onClick.AddListener(() => {
        OnSelectCharacter(idx);
        //add;点击已创建角色显示动画
        //characterView.CurrentCharacter = (int)charInfo.info.Class-1;
    });

    uiChars.Add(go);
    go.SetActive(true);
}

  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值