这个帖子源于一个同学的提问,觉得比较多人会遇到,总结为独立的帖子方便遇到同样问题的。
他的问题是:
获取DB 的方法直接使用.Parent.Scene.World.DataBase 获取 DB 吗?这样是不是太死了组件挂载的位置决定写法不一样。如果实体是进程 Scene 下多级的 Child ,也要 Parent.Parent… 这样写吗?
基本思路是能找到Scene的实例,或者通过Session也能找到
在任何组件的逻辑中,最先就是看能不到找到这两个。这样获取db实例就是简单的。
包括服务器上的关键实体比如unit这些的组件上,都是这个思路,是肯定能直接找到Scene或Session的。
这种情况在组件上加一个方法,很方便获取。
public IDateBase GetDB()
{
return this.Scene.World.DateBase;
}
不方便直接取得Scene或Session时
比如说网关帐号GateAccount,这个实体上,或者他的组件上,就不方便直接取得Scene或Session。
GateAccount的实例是在缓存中的,但每个网关帐号在登录网关时,都记录了SessionRumtimeId。
public void LoggedIn(Session session,GateAccount gateAccount)
{
if (!session.IsDisposed)
{
// 在session上缓存玩家PlayerId,gateAccount
var sessionPlayer = session.GetComponent<SessionPlayerComponent>()
?? session.AddComponent<SessionPlayerComponent>();
// gateAccount.Id = accountId
sessionPlayer.gateAccount = gateAccount;
// gateAccount缓存登录状态,SessionRumtimeId
gateAccount.SessionRumtimeId = session.RuntimeId;
gateAccount.LoginedGate = true;
}
}
这样GetDB的方法就可以写成取得session再拿到DateBase
/// 根据SessionRumtimeId获取网关session
public static bool TryGeySession(this GateAccount self, out Session session)
{
session = default;
long rumtimeId = self.SessionRumtimeId;
if (rumtimeId != 0)
{
if (!Entity.TryGetEntity(rumtimeId, out var entity))
{
return false;
}
session = (Session) entity;
return true;
}
session = null;
return false;
}
public static IDateBase GetDB(this GateAccount self)
{
Scene scene = new Scene();
if (self.TryGeySession(out Session session))
return session.Scene.World.DateBase;
return null;
}
当然思路还很多,在确定采用什么方式时,要思考他能否把他融合到逻辑关系,组件关系,数据关系中去,这样就很自然。
其它方式还有:
- 通过方法参数把seesion,scene实例传过去,
- 通过事件也能传递Server,Scene,或其它重要实体,
- 创建组件管理器,给这些组件、实体建立更标准的逻辑关系。
结论:
如果只是固定的一级父子关系,通过Parent再拿到Scene是可取的,但如果是多级父子关系,不应该这样做,这样是不利于思考的。服务器上大多数思考的都是过程逻辑关系,组件关系,数据关系,而不应该思考物体层级关系。
在修改代码的过程中,很可能会改变物体层级关系的,这样容易注意不到,引起获取数据库错误,你还想不起来,改bug会变得很困难。
网关帐号肯定记录了与网关session的数据关系,你在排查错误时,通常思路都是 逻辑关系>组件关系>数据关系
。
这三种关系是整体的,有逻辑,有序列的。
而层级关系是局部的,无逻辑,弱序列的。