Actor模型只需要知道对方的InstanceId就能发送消息,十分方便,但是有时候我们可能无法知道对方的InstanceId,或者是一个Actor的InstanceId会发生变化。这种场景很常见,比如:很多游戏是分线的,一个玩家可能从1线换到2线,还有的游戏是分场景的,一个场景一个进程,玩家从场景1进入到场景2。因为做了进程迁移,玩家对象的InstanceId也就变化了。ET提供了给这类对象发送消息的机制,叫做Actor Location机制。其原理比较简单:
注意,Actor模型是纯粹的服务端消息通信机制,跟客户端是没什么关系的,很多用ET的新人看到ET客户端消息也有Actor接口,以为这是客户端跟服务端通信的机制,其实不是的。ET客户端使用这个Actor完全是因为Gate需要对客户端消息进行转发,我们可以正好利用服务端actor模型来进行转发,所以客户端有些消息也是继承了actor的接口。假如我们客户端不使用actor接口会怎么样呢?比如,Frame_ClickMap这个消息
message Frame_ClickMap // IActorLocationMessage
{
int64 ActorId = 93;
int64 Id = 94;
float X = 1;
float Y = 2;
float Z = 3;
}
我们可能就不需要ActorId这个字段,消息发送到Gate,gate看到是Frame_ClickMap消息,它需要转发给Map上的Unit,转发还好办,gate可以从session中获取对应的map的unit的位置,然后转发,问题来了,Frame_ClickMap消息到了map,map怎么知道消息需要给哪个对象呢?这时候有几种设计:
ActorLocation消息发送
// 从Game.Scene上获取ActorLocationSenderComponent,然后通过Entity.Id获取ActorLocationSender
ActorLocationSender actorLocationSender = Game.Scene.GetComponent<ActorLocationSenderComponent>().Get(unitId);
// 通过ActorLocationSender来发送消息
actorLocationSender.Send(actorLocationMessage);
// 发送Rpc消息
IResponse response = await actorLocationSender.Call(actorLocationRequest);
ActorLocation消息的处理跟Actor消息几乎一样,不同的是继承的两个抽象类不同,注意actorlocation的抽象类多了个Location
// 处理send过来的消息, 需要继承AMActorLocationHandler抽象类,抽象类第一个泛型参数是Actor的类型,第二个参数是消息的类型
[ActorMessageHandler(AppType.Map)]
public class Frame_ClickMapHandler : AMActorLocationHandler<Unit, Frame_ClickMap>
{
protected override ETTask Run(Unit unit, Frame_ClickMap message)
{
Vector3 target = new Vector3(message.X, message.Y, message.Z);
unit.GetComponent<UnitPathComponent>().MoveTo(target).Coroutine();
}
}
// 处理Rpc消息, 需要继承AMActorRpcHandler抽象类,抽象类第一个泛型参数是Actor的类型,第二个参数是消息的类型,第三个参数是返回消息的类型
[ActorMessageHandler(AppType.Map)]
public class C2M_TestActorRequestHandler : AMActorLocationRpcHandler<Unit, C2M_TestActorRequest, M2C_TestActorResponse>
{
protected override async ETTask Run(Unit unit, C2M_TestActorRequest message, Action<M2C_TestActorResponse> reply)
{
reply(new M2C_TestActorResponse(){Info = "actor rpc response"});
await ETTask.CompletedTask;
}
}
中国有很多城市(进程),城市中有很多人(entity对象)居住,每个人都有身份证号码(Entity.Id)。一个人每到一个市都需要办理居住证,分配到唯一的居住证号码(InstanceId),居住证号码的格式是2个字节市编号+4个字节时间+2个字节递增。身份证号码是永远不会变化的,但是居住证号码每到一个城市都变化的。 现在有个中国邮政(actor)。假设小明要发信给女朋友小红