继续上一次的一般消息流转,那么这次,我们来走一遍ET的另一核心——Actor消息机制
建议先看ETBook的Actor消息机制介绍,地址:
- https://blog.csdn.net/qq_15020543/article/details/88539225
- https://blog.csdn.net/qq_15020543/article/details/89219390
继续上次,我们回到客户端
// 创建一个ETModel层的Session,并且保存到ETModel.SessionComponent中
ETModel.Session gateSession = ETModel.Game.Scene.GetComponent<NetOuterComponent>().Create(r2CLogin.Address);
//添加ETModel.SessionComponent组件,并对Session赋值
ETModel.Game.Scene.AddComponent<ETModel.SessionComponent>().Session = gateSession;
// 创建一个ETHotfix层的Session, 并且保存到ETHotfix.SessionComponent中,
// 目前为止,所有客户端的消息的收发都将被GateSession管理
//(Model层和Hotfix层都有各自的GateSession,
// 本质上还是ETModel.Session和ETHotfix.Session)
Game.Scene.AddComponent<SessionComponent>().Session = ComponentFactory.Create<Session, ETModel.Session>(gateSession);
//向服务端请求登录进gate服务器,如果登录成功,此后与服务端的通信都将通过gate服务器这个中介者
G2C_LoginGate g2CLoginGate = (G2C_LoginGate)await SessionComponent.Instance.Session.Call(new C2G_LoginGate() { Key = r2CLogin.Key });
Log.Info("登陆gate成功!");
然后回到服务端
//从已经分发的KEY里面寻找,如果没找到,说明非法用户,不给他连接gate服务器
string account = Game.Scene.GetComponent<GateSessionKeyComponent>().Get(message.Key);
if (account == null)
{
response.Error = ErrorCode.ERR_ConnectGateKeyError;
response.Message = "Gate key验证失败!";
reply(response);
return;
}
//专门给这个玩家创建一个Player对象
Player player = ComponentFactory.Create<Player, string>(account);
//注册到PlayerComponent,方便管理
Game.Scene.GetComponent<PlayerComponent>().Add(player);
//给这个session安排上Player
session.AddComponent<SessionPlayerComponent>().Player = player;
//添加邮箱组件表示该session是一个Actor,接收的消息将会队列处理
session.AddComponent<MailBoxComponent, string>(MailboxType.GateSession);
response.PlayerId = player.Id;
//回复客户端的连接gate服务器请求
reply(response);
//向客户端发送热更层信息
session.Send(new G2C_TestHotfixMessage() { Info = "recv hotfix message success" });
再回到客户端
Log.Info("登陆gate成功!");
// 创建Player
Player player = ETModel.ComponentFactory.CreateWithId<Player>(g2CLoginGate.PlayerId);
PlayerComponent playerComponent = ETModel.Game.Scene.GetComponent<PlayerComponent>();
playerComponent.MyPlayer = player;
//分发登录完成的事件
Game.EventSystem.Run(EventIdType.LoginFinish);
// 测试消息有成员是class类型
G2C_PlayerInfo g2CPlayerInfo = (G2C_PlayerInfo) await SessionComponent.Instance.Session.Call(new C2G_PlayerInfo());
Debug.Log("测试玩家信息为" + g2CPlayerInfo.Message);
这下我们不用再回到服务端了,因为一般消息的流转我们了解的已经差不多了,我们直接来到下一个重要的阶段
来到MapHelper.cs类,查看登录Map服务器相关代码
// 获取资源组件
ResourcesComponent resourcesComponent = ETModel.Game.Scene.GetComponent<ResourcesComponent>();
// 加载Unit资源
await resourcesComponent.LoadBundleAsync($"unit.unity3d");
// 加载场景资源
await ETModel.Game.Scene.GetComponent<ResourcesComponent>().LoadBundleAsync("map.unity3d");
// 切换到map场景
using (SceneChangeComponent sceneChangeComponent = ETModel.Game.Scene.AddComponent<SceneChangeComponent>())
{
await sceneChangeComponent.ChangeSceneAsync(SceneType.Map);
}
//请求登录Map服务器
G2C_EnterMap g2CEnterMap = await ETModel.SessionComponent.Instance.Session.Call(new C2G_EnterMap()) as G2C_EnterMap;
好了,我们又要去服务端了,注意,下面的消息是属于服务器内部的消息流通,所以不用网络层的传输,直接进行内部数据传输最后被响应Handler处理
//获取Player对象引用
Player player = session.GetComponent<SessionPlayerComponent>().Player;
// 在map服务器上创建战斗Unit
IPEndPoint mapAddress = StartConfigComponent.Instance.MapConfigs[0].GetComponent<InnerConfig>().IPEndPoint;
Session mapSession = Game.Scene.GetComponent<NetInnerComponent>().Get(mapAddress);
//由gate服务器向map服务器发送创建战斗单位请求,这里的session.InstanceId将由IdGenerater创建,
//用以保证不会冲突
M2G_CreateUnit createUnit =
(M2G_CreateUnit) await mapSession.Call(new G2M_CreateUnit() { PlayerId = player.Id, GateSessionId = session.InstanceId });
那我们直接到它的Handler这里吧
//创建战斗单位(小骷髅给劲哦)
Unit unit = ComponentFactory.CreateWithId<Unit>(IdGenerater.GenerateId());
//增加移动组件
unit.AddComponent<MoveComponent>();
//增加寻路相关组件
unit.AddComponent<UnitPathComponent>();
//设置小骷髅位置
unit.Position = new Vector3(-10, 0, -10);
//给小骷髅添加信箱组件,队列处理收到的消息
await unit.AddComponent<MailBoxComponent>().AddLocation();
//添加同gate服务器通信基础组件,主要是赋予ID
unit.AddComponent<UnitGateComponent, long>(message.GateSessionId);
//将这个小骷髅维护在Unit组件里
Game.Scene.GetComponent<UnitComponent>().Add(unit);
//设置回复消息的ID
response.UnitId = unit.Id;
// 广播创建的unit
M2C_CreateUnits createUnits = new M2C_CreateUnits();
Unit[] units = Game.Scene.GetComponent<UnitComponent>().GetAll();
foreach (Unit u in units)
{
UnitInfo unitInfo = new UnitInfo();
unitInfo.X = u.Position.x;
unitInfo.Y = u.Position.y;
unitInfo.Z = u.Position.z;
unitInfo.UnitId = u.Id;
createUnits.Units.Add(unitInfo);
}
//广播所有小骷髅信息
MessageHelper.Broadcast(createUnits);
//广播完回复客户端,这边搞好了
reply(response);
注意,这里面的MessageHelper.Broadcast(createUnits); 就是我们的核心Actor机制的体现了,我们只要把消息发到gatesession,gatesession将会自动根据id转发消息到相应客户端,而里面涉及到的相关ID,我们在由gate服务器向map服务器发送创建战斗单位请求的时候,已经传好参数了
// 从Game.Scene上获取ActorSenderComponent,然后通过InstanceId获取ActorMessageSender
ActorSenderComponent actorSenderComponent = Game.Scene.GetComponent<ActorSenderComponent>();
ActorMessageSender actorMessageSender = actorSenderComponent.Get(unitGateComponent.GateSessionActorId);
// send
actorMessageSender.Send(message);
// rpc
var response = actorMessageSender.Call(message);
好了,回到客户端
//设置UnitID
PlayerComponent.Instance.MyPlayer.UnitId = g2CEnterMap.UnitId;
//增加。。。emmm不知道怎么翻译这个组件好,他负责点击地面控制小骷髅移动
Game.Scene.AddComponent<OperaComponent>();
//分发进入正式游戏成功事件
Game.EventSystem.Run(EventIdType.EnterMapFinish);
然后我们突然想起来,刚刚亡灵领主出生的时候向别的小骷髅广播了一下来着,我们看会做什么操作
foreach (UnitInfo unitInfo in message.Units)
{
if (unitComponent.Get(unitInfo.UnitId) != null)
{
continue;
}
//根据不同ID,创建小骷髅
Unit unit = UnitFactory.Create(unitInfo.UnitId);
unit.Position = new Vector3(unitInfo.X, unitInfo.Y, unitInfo.Z);
}
好了 ,小骷髅都安排好了,该向服务端发消息了,也就是寻路,还记得我们之前添加的OperaComponent,他就是负责寻路的
//发送点击地图消息
ETModel.SessionComponent.Instance.Session.Send(frameClickMap);
|
|
|
\|/
//服务端Frame_ClickMapHandler处理位置信息
Vector3 target = new Vector3(message.X, message.Y, message.Z);
unit.GetComponent<UnitPathComponent>().MoveTo(target).Coroutine();
|
|
|
\|/
//移动到指定位置
await self.MoveAsync(self.ABPath.Result);
|
|
|
\|/
// 每移动3个点发送下3个点给客户端
if (i % 3 == 1)
{
self.BroadcastPath(path, i, 3);
}
|
|
|
\|/
// 客户端处理服务端的同步信息,利用寻路组件进行寻路
unitPathComponent.StartMove(message).Coroutine();
后记
至此,连接服务器,创建小骷髅,并且自动寻路的操作就完成了,我中间很多细节没有讲,其实没必要讲(观众:懒还有理了???),那些东西需要大家自行理解和体会的,而到现在master的Demo解读也该告一段落了。