更正!这里插入:前面的创建角色之后,点击开启冒险按钮返回角色选择面板,角色选择列表不显示的问题
原因:服务器创建角色后,没有把角色列表返回给客户端
>>首先我们点击开启冒险按钮创建角色:
此时:按钮挂了UICharacterSelect脚本中的OnClickCreate()方法
OnClickCreate
public void OnClickCreate()
{
if(string.IsNullOrEmpty(this.charName.text))
{
MessageBox.Show("请输入角色名称");
return;
//不能把空消息发给服务器
}
//启用协议创建角色
UserService.Instance.SendCharacterCreate(this.charName.text, this.charClass);
}
SendCharacterCreate
调用UserService中的SendCharacterCreate;把创建角色请求消息发送给服务端
OnCreateCharacter
服务端中对客服端的请求做出响应的方法是:OnCreateCharacter//在Game Server:UserService的构造方法中已经订阅
服务器打包信息发给客户端时,缺少了打包已有的角色:
//前面创建新角色,放进DBService;
类似于OnLogin方法,把DBService.Instance.Entities.Characters中的对象都遍历一次;
Characters即为所有角色;他是NCharacterInfo类型的;
我们只需要把它遍历一次,赋值并加进message中即可
//add部分是新加的
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
};
//这里添加了character=:因为可能对象会发生变化;因此把返回值往里面Add
character=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();
//add
//得到角色列表
foreach (var c in sender.Session.User.Player.Characters)
{//从该用户的已经创建的角色中找
NCharacterInfo info = new NCharacterInfo();
info.Id = c.ID;
info.Name = c.Name;
info.Class = (CharacterClass)c.Class;
info.Tid = c.TID;
info.mapId = c.MapID;
//把数据库当前已经有的填充到协议中发给客户端
//message.Response.userLogin.Userinfo.Player.Characters.Add(info);//添加角色到响应
message.Response.createChar.Characters.Add(info);
}
//回发消息吧结果告诉用户;创建是否成功
message.Response.createChar.Result = Result.Success;
message.Response.createChar.Errormsg = "None";
//创建一个包换角色列表的数组
byte[] data = PackageHandler.PackMessage(message);
sender.SendData(data, 0, data.Length);
}
回到客户端的UserService的OnUserCreateCharacter中的OnCharacterCreate方法
回到UICharacterSelect中的OnCharacterCreate方法
如果角色创建成功,调用InitCharacterSelect(true):创建列表等等。。
(详细请看MMO角色创建与选择:应用通讯协议中的角色列表填充InitCharacterSelect
结果图示:
主场景的UI制作
角色信息
修改UIMainCity的分辨率
名字等级信息框
UIMainCity.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Models;
public class UIMainCity0 : MonoBehaviour
{
public Text avatarName;
public Text avatarLevel;
void Start()
{//在启动时候刷新
this.UpdateAvatar();
}
void UpdateAvatar()
{
//User是单例类,存放用户和角色的各种相关信息//CurrentCharacter存储网络传回来的信息(姓名角色等级..)
this.avatarName.text = string.Format("{0}[{1}]", User.Instance.CurrentCharacter.Name, User.Instance.CurrentCharacter.Id);
this.avatarLevel.text = User.Instance.CurrentCharacter.Level.ToString();
}
}
启动后:
角色血条
//创建世界空间的UI
世界空间:在3d场景中是统一的坐标系
建立一个Canvas
与MainPlayerCamera共享同一个Camera
画布缩放器:
用于控制整体缩放和像素密度用户界面画布中的元素。此缩放会影响画布下的所有内容,包括字体大小和图像边框
每单位动态像素数 | UI 中动态创建的位图(如文本)所使用的每单位像素数量。 |
每单位参考像素 | 如果精灵具有此“每单位像素数”设置,则精灵中的一个像素将覆盖世界中的一个单位。如果“每单位参考像素数”设置为 1,则精灵中的“每单位像素数”设置将按原样使用。 |
创建UINameBar
把比例调成UIWorldElementManager1:UINameBar100
UINameBar
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Entities;
public class UINameBar1 : MonoBehaviour
{
public Text avatarName;
//信息条要给显示
public Character character;
void Start()
{
if(character!=null)
{
}
}
void Update()
{
}
void UpdateInfo()
{//角色的升级,更新
if(this.character!=null)
{
string name = this.character.Name + " Lv." + this.character.Info.Level;
if(name!=this.avatarName.text)
{//每一次的赋值会影响性能,回导致UI元素的重绘
this.avatarName.text = name;
}
}
}
}
UIWorldElement
是UIWorldElementManager世界元素管理器的通用属性
//owner做跟随
//把脚本挂到UINameBar下面
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIWorldElement0 : MonoBehaviour
{
//用来更新
public Transform owner;//需要更新的东西属于谁
//把Owner复制成角色的Transform,让世界元素自动做跟踪
public float height = 1.5f;
void Start()
{
}
void Update()
{
if(owner!=null)
{
//UINameBar的位置是角色位置的上方一点
this.transform.position = owner.position + Vector3.up * height;
}
}
}
UIWorldElementManager
维护各种数据,负责数据的增加删除
//把脚本绑到UIWorldMangaer上
单例:频繁用到世界元素动态添加到此管理器中
UIWorldElementManager : MonoSingleton<UIWorldElementManager>
用字典管理所有世界元素信息
隐藏UINameBar
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Entities;
public class UIWorldElementManager : MonoSingleton<UIWorldElementManager>
{
//取到信息条
public GameObject nameBarPrefab;
private Dictionary<Transform, GameObject> elements = new Dictionary<Transform, GameObject>();
void Start()
{
}
void Update()
{
}
public void AddCharacterNameBar(Transform owner,Character character)
{//添加owner
//实力化nameBarPrefab;管理器下创建的节点的根节点是This.transform
//当根节点(管理器)被销毁,其所有的子元素都被销毁
GameObject goNameBar = Instantiate(nameBarPrefab, this.transform);
//显示在管理器下面的go的名字等信息
goNameBar.name="NameBar"+character.entityId;
//取得上面绑定的脚本(血条角色
goNameBar.GetComponent<UIWorldElement>().owner = owner;
goNameBar.GetComponent<UINameBar>().character = character;
goNameBar.SetActive(true);
//加到管理器;每个人有一个血条,owner是唯一的
this.elements[owner] = goNameBar;
}
public void RemoveCharacterNameBar(Transform owner)
{//只需要把owner身上的移除掉
if(this.elements.ContainsKey(owner)) //判断是否已经被删过了
{
//销毁游戏对象,包括游戏对象本身,组件,脚本;函数执行是异步的,要在下一帧或指定时间才能销毁
Destroy(this.elements[owner]);
//确保当前帧游戏对象不被引用
this.elements.Remove(owner);
}
}
}
角色创建是在GamObjectManager中
在这里拿到参数:
UIWorldElementManager.Instance.AddCharacterNameBar(go.transform, character);
删除是在EntityController中
所有的角色都挂了此脚本;
void OnDestroy()
{
if (entity != null)
Debug.LogFormat("{0} OnDestroy :ID:{1} POS:{2} DIR:{3} SPD:{4} ", this.name, entity.entityId, entity.position, entity.direction, entity.speed);
//实体移除时把血条也删掉
if(UIWorldElementManager.Instance!=null)
{
UIWorldElementManager.Instance.RemoveCharacterNameBar(this.transform);
}
}
演示
nameBar方向不对
//关于高度,可以在根据配置表把每个角色血条的高度按照角色高度更新
在UINameBar.cs添加旋转逻辑,名字调用
关于血条对着摄像机
要朝向摄像机的方向;transform.LookAt
//给摄像机设置tag:
UINameBar.cs:Update
void Update()
{
this.UpdateInfo();
//this.transform.LookAt(Camera.main.transform,Vector3.up);
//把血条的Scale.x=-1
this.transform.forward = Camera.main.transform.forward;
}
效果图:
//当服务器没有关闭时,多次登录会有多个角色同时出现在场景里;
多个角色共用同一个实例,服务器做了角色进入没有做角色离开;缺少清理工作