QBaseEntityComp ^1.3.0
通用的实体组件功能 适用于服务端/客户端
python
from QuModLibs.Modules.EntityComps.Server import QBaseEntityComp
# from QuModLibs.Modules.EntityComps.Client import QBaseEntityComp
基本构造
QBaseEntityComp 在设计上遵循先构造后绑定原则
python
from QuModLibs.Server import *
from QuModLibs.Modules.EntityComps.Server import QBaseEntityComp
class MyComp(QBaseEntityComp):
""" 自定义组件 一定时间后杀死生物 """
def __init__(self, deathTime=2.0):
QBaseEntityComp.__init__(self) # 请确保父类方法被正确调用以便初始化功能
self.deathTime = deathTime
def onBind(self):
""" 生物绑定触发 """
QBaseEntityComp.onBind(self)
print("成功绑定生物 {}".format(self.entityId)) # 组件在绑定后即可拿到 entityId
self.addTimer(QBaseEntityComp.Timer(self.kill, time=self.deathTime)) # 同理 组件类也继承了的定时器有自己的生命周期(随着组件绑定/解除绑定开始/销毁)
def kill(self):
# 组件在绑定后也能拿到entityObj对象 以便快速操作
self.entityObj.Kill()
def onUnBind(self):
""" 生物解除绑定触发 """
QBaseEntityComp.onUnBind(self)
print("组件解除绑定")
def onGameTick(self):
""" 网易Tick更新 """
QBaseEntityComp.onGameTick(self)
# 此处GAME TICK对应网易Tick 即30/s
pass
def update(self):
""" 微软Tick更新 """
QBaseEntityComp.update(self)
# update对应微软TICK 即20/s
pass
# 在玩家攻击生物时施加组件
@Listen(Events.PlayerAttackEntityEvent)
def PlayerAttackEntityEvent(args={}):
comp = MyComp(3.0) # 组件在不绑定生物时 什么功能都不会有 就像一个常规对象
comp.bind(args["victimId"]) # 绑定成功后 组件进入管理模式 持续进行tick更新
组件生命周期
了解组件的生命周期有助于开发理解
组件无绑定: 跟随Python垃圾回收器释放 (不作讨论)
组件有绑定: 跟随生物内存状态更新
update/onGameTick: 生物处于当前渲染区块内时更新(通过JSON组件设置持久化更新同样生效)
onUnBind: 生物被系统回收(不在内存状态中,但不一定被杀死)/脚本主动解绑/游戏关闭
(onUnBind中可以通过方法getUnBindINFO拿到解绑原因)
关于生物内存状态的特殊情况(以下内容基于网易接口数据讨论 不代表真实的游戏底层)
处于主世界的生物: 在内存不足/一些其他条件因素(如区块距离玩家特远)释放
处于非主世界的生物: 当前维度中不存在任何玩家时一定会被释放
相关事件对应AddEntityServerEvent, EntityRemoveEvent
客户端差异:
对于客户端而言 离开自己的渲染区块就会释放 同理重新进入渲染视野又会触发增加
玩家实体:
在双端无论如何都不会触发自动释放(始终渲染) 除非游戏关闭
多例组件模式
默认的情况下 同一个组件类在单个生物上至多持有一个实例 通过权限声明可以改变这一机制
python
from QuModLibs.Modules.EntityComps.Server import QBaseEntityComp, QEntityCompFlags
# QEntityCompFlags 包含了所有可以设置的声明
@QBaseEntityComp.setFlags(QEntityCompFlags.ALLOW_DUPLICATES) # 允许重复持有特定类型的组件
class MyComp(QBaseEntityComp):
# 此时 该组件将允许重复构造实例添加在单个生物身上
...
渲染状态忽略
默认的情况下 组件仅在生物渲染时触发更新方法 通过权限声明可以改变这一机制(通常来说不会这么做 更多的是在JSON中设置)
python
from QuModLibs.Modules.EntityComps.Server import QBaseEntityComp, QEntityCompFlags
# 通过位运算可以同时使用多个声明
@QBaseEntityComp.setFlags(QEntityCompFlags.ALLOW_DUPLICATES | QEntityCompFlags.IGNORE_RENDERING_STATUS)
class MyComp(QBaseEntityComp):
# 此时 该组件将允许重复构造实例添加在单个生物身上 并且忽略渲染状态(但不会忽略内存状态 被系统释放后依然会掉组件)
...
按类型绑定生物
在了解组件生命周期后 可知无论如何生物都有可能被系统暂时回收 在重新回到加载时会触发对应的事件 对于持久化组件绑定特定生物的需求 可以使用内置的regEntity装饰器完成(暂不支持玩家)
python
from QuModLibs.Modules.EntityComps.Server import QBaseEntityComp, QEntityCompFlags
@QBaseEntityComp.regEntity("minecraft:zombie", "minecraft:husk", ...)
@QBaseEntityComp.setFlags(QEntityCompFlags.IGNORE_RENDERING_STATUS)
class MyComp(QBaseEntityComp):
...
获取实体持有组件
组件类提供了多种实体持有组件获取方法 可根据需求选择使用
python
from QuModLibs.Modules.EntityComps.Server import QBaseEntityComp
class MyComp(QBaseEntityComp):
pass
MyComp.getComp(entityId) # 基于实体id获取单个组件 失败/无组件则返回None
MyComp.getSubComp(entityId) # 基于实体id获取单个组件 相较于getComp, getSubComp支持带有继承关系的子类组件获取
for comp in MyComp.getComps(entityId):
# 获取实体身上的所有该组件实例(浅拷贝) 适用于读写操作
pass
for comp in MyComp.getCompsGen(entityId):
# [生成器过程时] 获取实体身上的所有该组件实例 适用于只读操作
pass
for comp in MyComp.getSubComps(entityId):
# 同理 获取全部带有继承关系的组件实例(浅拷贝)
pass
for comp in MyComp.subCompsGen(entityId):
# [生成器过程时] 获取全部带有继承关系的组件实例
pass
# 一种特殊的获取 当不存在任何此类型的组件时将抛出一个万能对象而不是None 万能对象的一切操作都将合法
MyComp.getTempSubComp(entityId)
主动销毁实体组件
组件类也提供了多种组件销毁方法(严格来说是解绑)
python
comp = MyComp.getComp(entityId)
if comp:
# 调用实例方法解除绑定
comp.unBind()
# 注: 以下方法均不支持子类组件销毁 需自行搭配获取与unBind方法实现
MyComp.removeComp(entityId) # 使用类方法而不是实例方法解除组件绑定(该方法会自动判断存在与否)
MyComp.removeAllComps(entityId) # 删除所有特定类型的组件(多例模式)
rebind 复用组件
组件的绑定机制使得单个组件实例能够被复用绑定到任意生物身上
python
comp = MyComp() # 构造单个组件复用
comp.rebind(entityId1) # 绑定到实体1
comp.rebind(entityId2) # 从实体1解除绑定并绑定到实体2
组件高阶用法
仅演示设计思路
python
# 角色扮演类
class 通用变身(QBaseEntityComp):
def onBind(self):
构造技能UI() # 可搭配UI管理器确保切换维度也能自动恢复
def onUnBind(self):
销毁技能UI()
def defaultAttact(self):
# 由用户UI输入调用 普通攻击逻辑
pass
class 广智(通用变身):
def onBind(self):
super(...).onBind()
# 实现资源处理 修改外观 动画
def onUnBind(self):
super(...).onUnBind()
# 实现资源恢复
def defaultAttact(self):
# 实现普通攻击
pass
class 石猴(通用变身):
def onBind(self):
super(...).onBind()
# 实现资源处理 修改外观 动画
def onUnBind(self):
super(...).onUnBind()
# 实现资源恢复
def defaultAttact(self):
# 实现普通攻击
pass