🧱 EntityCall
EntityCall
在之前的章节中一直提到EntityCall,那什么是EntityCall?我们可以简单的理解为:封装远程交互、通讯等方法的一种对象,是脚本层与实体远程交互的常规手段。
下面我们会围绕这几个问题来进行解释:
底层是如何实现的?这可以方便开发者从底层了解EntityCall的概念
EntityCall在使用上有哪些限制或要求;
一个实体包含哪些EntityCall?
利用EntityCall调用实体组件上的方法
相关参考

EntityCall的底层实现原理:
EntityCall对象在C++底层实现非常简单,它只包含了实体的ID、目的地的地址、实体类型、EntityCall类型。
当用户请求一次远程交互时,底层首先能够通过实体类型找到实体定义的描述(def配置文件),通过该描述对用户输入的数据进行检查,如果检查合法那么底层将数据打包并发往目的地,接着目的地进程根据协议进行解包,根据实体ID找到实体,最终调用到脚本层。
EntityCall的限制或要求:
刚才在底层实现原理中提到,必须先通过实体类型找到实体定义的描述,而这个描述即是def配置文件!相当于互相之间的远程通讯都通过def“协议”规定好的。所以EntityCall只能:
- 调用其对应def配置文件中声明过的方法;
- def配置中的属性不可被访问;
- 在def定义之外的任何信息都不可被访问。
一个实体包含哪些EntityCall?
一个实体最多可以包含三个部分:
1.client:
当实体包含客户端部分时(通常为玩家),在服务器端可以访问实体的client属性,该属性是一个EntityCall。
如,Avatar.def中定义了client远程方法如下
<ClientMethods>
<hello>
</hello>
</ClientMethods>
在服务端可以通过如下方式向client进行远程方法调用:
class Avatar(KBEngine.Entity):
def test():
self.client.hello()
2.base:
当实体的一部分创建在Baseapp(如Baseapp A)时,在非当前Baseapp(如Baseapp B、Cellapp C等)中可以访问实体的base属性,该属性是一个EntityCall。
如,Avatar.def中定义了base远程方法如下
<BaseMethods>
<hello>
</hello>
</BaseMethods>
在服务端可以通过如下方式向base进行远程方法调用:
def test(entity):
"""
entity是一个不在当前app上的实体
"""
entity.base.hello()
3.cell:
当实体的一部分创建在Cellapp(如Cellapp A)时,在非当前Cellapp(如Cellapp B、Baseapp C等)中可以访问实体的cell属性,该属性是一个EntityCall。
如,Avatar.def中定义了cell远程方法如下
<CellMethods>
<hello>
</hello>
</CellMethods>
在服务端可以通过如下方式向cell进行远程方法调用:
def test(entity):
"""
entity是一个不在当前app上的实体
"""
entity.cell.hello()
利用EntityCall调用实体组件上的方法:
在新版本的引擎中,组件已被支持,并且EntityCall可以调用到实体组件上的方法了。因为组件及其方法也是声明在def配置文件中的,所以自然也是可以被远程调用到的(见上文的底层实现)。我们来看看如何进行调用:
如,Avatar.def中定义了其包含了Combat战斗组件,部分配置如下:
<Components>
<!--战斗组件-->
<combat>
<Type> Combat </Type>
<Persistent> true </Persistent>
</combat>
...
...
</Components>
Combat组件的部分def如下:
<CellMethods>
<!--受到伤害-->
<recvDamage>
<!--伤害来源方-->
<Arg> ENTITY_ID </Arg>
<!--伤害来源于的技能-->
<Arg> SKILLID </Arg>
<!--伤害类型-->
<Arg> DAMAGE_TYPE </Arg>
<!--伤害值-->
<Arg> HP </Arg>
</recvDamage>
</CellMethods>
<ClientMethods>
<!--给客户端通知,收到伤害反馈-->
<onRecvDamage>
<!--伤害来源方-->
<Arg> ENTITY_ID </Arg>
<!--来源技能-->
<Arg> SKILLID </Arg>
<!--伤害类型-->
<Arg> DAMAGE_TYPE </Arg>
<!--造成伤害的值-->
<Arg> HP </Arg>
</onRecvDamage>
</ClientMethods>
在服务端可以通过如下方式向Avatar
的Combat组件
进行远程方法调用:
假设,以下方法是在cell
上的实现。
逻辑脚本:
def doDamage(avatarCellEntityCall, attacker, skillID, damageType, damage):
"""
avatarCellEntityCall是一个不在当前app上的Avatar实体的cellEntityCall
"""
avatarCellEntityCall.combat.recvDamage(attacker.id, skillID, damageType, damage)
...
...
Combat组件上的实现代码Combat.py上:
def recvDamage(attackerID, skillID, damageType, damage):
"""
Combat组件上recvDamage的实现
"""
# 发送给组件对应的客户端
self.client.onRecvDamage(attackerID, skillID, damageType, damage)
我们可以看到,通过avatarCellEntityCall.combat
可以访问到实体在cell
上声明的组件,并调用def中配置的方法recvDamage
。同样的,在组件上调用self.client.onRecvDamage
,向其客户端发出远程方法调用。
相关参考:
关于EntityCall,还必须提一下API中Cellapp下的Entity.allClients、Entity.otherClients、Entity.clientEntity(self, destID),具体请查看API手册。
Entity.allClients
通过这个属性调用实体的客户端远程方法,引擎会将这个消息广播给实体View范围内所有的其他绑定了客户端的实体(包括自己的客户端,绑定了客户端的实体通常为玩家)。
Entity.otherClients
通过这个属性调用实体的客户端远程方法,引擎会将这个消息广播给实体View范围内所有的其他绑定了客户端的实体(不包括自己的客户端,绑定了客户端的实体通常为玩家)。
Entity.clientEntity(self, destID)
通过这个方法可以访问自己客户端(当前实体必须绑定了客户端)中某个实体的方法,只有在View范围内的实体才会同步到客户端。它只能在一个real实体上被调用。