# 自定义属性面板
# 前言
一个完整的零件一般主要有3个文件,以自定义零件中的PortalPart为例,包含Portal.part,PortalMeta.py,Portal.py。
其中Portal.py中定义了一个继承自PartBase的类PortalPart,该类的实例属性诸如"_init_"方法中的一些"self."开头的变量可通过编辑器的属性面板来进行配置。
PortalMeta.py文件中则定义了各个变量希望在编辑器中呈现的编辑方式。
在编辑器中点击对应的".part"文件即可在属性面板中预览并编辑对应的属性。
# 总览
当前自定义的零件支持编辑python的所有基本类型,即:整数int,浮点数float,布尔bool,字符串str,字典dict,列表list,除此之外,针对一些特定需求,也提供了相应的支持,如下拉列表选择,多维向量等。
下图给出了所有的基本控件样式与写法。代码可在附录中找到。
# 类型与属性
# 通用属性
属性 | 默认值 | 说明 |
---|---|---|
default | None | 属性默认值,当实际值与默认值不相等时会提供一个按钮设为默认值 |
text | "" | 属性在编辑器中的显示名称 |
sort | 0 | 属性显示顺序,数字越小越靠前 |
editable | True | 属性在编辑器中是否可编辑(反过来就是是否只读) |
tip | "" | 鼠标移上去显示的Tips |
visible | True | 属性是否可见 |
group | "" | 所属分组,可用于简单布局。(只适用于第一级结点) |
# 整数:PInt
除通用属性外的额外属性:
属性 | 默认值 | 说明 |
---|---|---|
min | None | 最小值 |
max | None | 最大值 |
step | 1 | 步进量,即每次点击控件中增加/减少按钮修改的差值 |
# 浮点数:PFloat
除通用属性外的额外属性:
属性 | 默认值 | 说明 |
---|---|---|
min | None | 最小值 |
max | None | 最大值 |
step | 1.0 | 步进量,即每次点击控件中增加/减少按钮修改的差值 |
precision | 2 | 小数点后位数 |
# 布尔值:PBool
无额外属性
# 字符串:PStr
除通用属性外的额外属性:
属性 | 默认值 | 说明 |
---|---|---|
regex | None | 内容需要符合的正则表达式 |
# 列表:PArray
除通用属性外的额外属性:
属性 | 默认值 | 说明 |
---|---|---|
childAttribute | None | 子元素的Meta定义,如:childAttribute=PStr() |
maxSize | 9999 | 最大长度 |
moveChild | False | 子元素添加可以上下移动按钮 |
insertChild | False | 子元素添加插入新元素到当前位置按钮 |
# 字典:PDict
除通用属性外的额外属性:
属性 | 默认值 | 说明 |
---|---|---|
addable | False | 允许动态增加子元素,该子元素的key需要存在于此Meta定义中。 |
removable | False | 允许动态删除已存在的子元素 |
fixList | [] | 不受动态增删影响的固定属性,如["key1"] |
# 枚举类型:PEnum
除通用属性外的额外属性:
属性 | 默认值 | 说明 |
---|---|---|
enumType | "" | 使用DefEnum定义的枚举类型数据名称,如DefEnum("MyEnum", {1: "a", 2: "b"}),这里就填"MyEnum" |
# 多维向量:PVector2,PVector3,PVector4
除通用属性外的额外属性:
属性 | 默认值 | 说明 |
---|---|---|
min | None | 最小值 |
max | None | 最大值 |
step | 1.0 | 步进量,即每次点击控件中增加/减少按钮修改的差值 |
precision | 2 | 小数点后位数 |
# 颜色:PColor
除通用属性外的额外属性:
属性 | 默认值 | 说明 |
---|---|---|
format | (0,0,0)或"#000000" | 颜色格式:当format="#RRGGBB"(现在只有这一种),此时,实际值形如:"#A6B7C8",当不传format参数时或传的值不支持时,会使用默认格式(r, g, b)。如:(244, 37, 18) |
# 对象数组:PObjectArray
除通用属性外的额外属性:
属性 | 默认值 | 说明 |
---|---|---|
itemCreator | None | 继承于PropertyListObject的自定义类 |
moveChild | False | 子元素添加可以上下移动按钮 |
insertChild | False | 子元素添加插入新元素到当前位置按钮 |
对于较为复杂的编辑数据,我们除了可以使用列表PArray、字典PDict进行多层嵌套以外,还可以使用对象数组。
首先我们需要创建一个新的编辑对象类,它需要继承PropertyListObject
from Meta.PropertyObject import sunshine_property_object
from Preset.Model.PresetDataMeta import PropertyListObject
@sunshine_property_object
class EventDataObject(PropertyListObject):
PROPERTIES = {}
将EventDataObject用到对象数组PObjectArray中:
@sunshine_class_meta
class LevelStagePartMeta(StageBasePartMeta):
CLASS_NAME = "LevelStagePart"
ObjectArrayDef = {
"events": EventDataObject,
}
PROPERTIES = {
"events": PObjectArray(
text="刷怪事件", sort=1004, group="阶段",
itemCreator=EventDataObject
)
}
ObjectArrayDef和PROPERTIES的key必须保持一致,EventDataObject作为itemCreator参数传入。
这种方式除了让层级更为清晰之外,更重要的是可以隔离自定义函数func传入的obj。
上面这个例子里,EventDataObject里属性func所传入的是EventDataObject,而不是根节点LevelStagePart。
当你需要控制列表内单个对象的不同属性的互相影响时,必须使用对象数组,比如我们通过condition属性来控制dependencyEvents和startTime属性的显隐。
class EventDataObject(PropertyListObject):
PROPERTIES = {
"classType": PStr(sort=0, text="类名", default="SpawnMobEvent", visible=False),
"condition": PEnum(sort=2, text="事件触发条件", enumType='ConditionType'),
"dependencyEvents": PArray(
sort=3, text="条件事件", childAttribute=PCustom(text='事件ID', sort=0, editAttribute='MCEnum', extend=getEvents),
func=lambda obj: {'visible': obj.condition == "dependency"}
),
"startTime": PInt(
text="间隔时间", sort=4, default=10,
func=lambda obj: {'visible': obj.condition == "timer"}
),
}
这里的PROPERTIES每个编辑子项的写法与前面介绍的一致。
实际效果:
# 其他
PCoordinate 继承自 PVector3,具有与PVector3相同的属性和功能,在原有功能基础上添加了两个辅助按钮,分别是选择地图位置,定位位置。
PGameObjectArea
区域选定组件, 可视化当前设置的区域大小。
继承自PDict, children字段必须为:{ 'min': PVector3(sort=0, text="顶点1"), 'max': PVector3(sort=1, text="顶点2"), 'dimensionId': PInt(sort=2, text="维度") }
PCustom
用于扩展的自定义控件类型,editAttribute属性用于设置扩展的控件的类型
注意:PCustom 很多属性是随着editAttribute的不同设置而有所不同。
customFunc 和func 属性没有关系,func是用于动态设置属性的,即可以通过Func属性动态修改控件的text , visible , sort 等属性,customFunc 是某些拓展组件用于设置数据更新函数。
实例代码:
PROPERTIES = { "MCEnum": PCustom(sort=0, text="MCEnum", group="custom", editAttribute="MCEnum", customFunc=updateMCEnum), "MCBlock": PCustom(editAttribute="MCBlock", default=('dirt', 0), group="custom", sort=1), 'MCBlockName': PCustom(sort=2, editAttribute="MCBlockName", group="custom"), "SelectItemWidget": PCustom(sort=3, editAttribute="SelectItemWidget", group="custom"), "MCEnumEx": PCustom(sort=4, editAttribute="MCEnumEx", customFunc=updateMCEnum, group="custom"), "MCCustomCreature": PCustom(sort=5, editAttribute="MCCustomCreature", group="custom"), 'MCItems': PCustom( sort=6, editAttribute='MCItems', default=('minecraft:apple', 0), withNamespace=False, withAuxValue=True, isBlock=None, itemCategory=8, categoryList=[8, 31], group="custom" ), 'MCResource': PCustom( sort=7, tip='背包格子中该物品显示的贴图。', editAttribute="MCResource", baseFolder=["textures"], relativePath=['items'], extension='.png', default='textures/items/apple', description='image', withExtension=True, group="custom" ), 'MCTipWidget': PCustom( group="custom", sort=8, editAttribute="MCTipWidget", text="MCTipWidget", content="说明内容:", linkText="链接文本>>", linkUrl="https://www.baidu.com") }
效果:
下面是各种扩展控件用法的说明:
editAttribute="MCEnum":
这是一个自定义的枚举控件,相比PEnum这个会自动添加 "":"空" 的默认枚举子项
跟随属性:
- customFunc=updateMCEnum //必要
updateMCEnum是一个参数为当前零件逻辑对象放回值为一个key值和value都为str类型的字典的函数,该返回值将作为枚举控件的选项数据显示。 - searchable=False
是否以搜索样式显示
example:
PROPERTIES = { "MCEnum": PCustom(sort=0, text="MCEnum", editAttribute="MCEnum", customFunc=updateMCEnum), } ... def updateMCEnum(obj): return { "A" : "1", "B" : "2", "C" : "dd", }
- customFunc=updateMCEnum //必要
editAttribute="MCTipWidget":
这是一个自定义的说明控件,用来显示一些必要的提示,注意链接最后不要有/
example:
'MCTipWidget': PCustom( group="custom", sort=8, editAttribute="MCTipWidget", text="MCTipWidget", content="说明内容:", linkText="链接文本>>", linkUrl="https://www.baidu.com")
editAttribute="MCBlock":
选取原版方块的控件,返回值是一个第一个元素是去命名空间的identifier的tuple,如:('birch_button', 0)
example:
"MCBlock": PCustom( text="原生方块", editAttribute="MCBlock", default=('dirt', 0) )
editAttribute="MCBlockName":
选取原版方块的控件,返回值是方块的identifier , 如 "minecraft:stone"
example:
'MCBlockName': PCustom(sort=4, editAttribute="MCBlockName"),
editAttribute="SelectItemWidget":
选取mc物品的控件,只显示图标,返回值是形如{'itemName': 'minecraft:stripped_crimson_hyphae', 'auxValue': 0}的dict,
example:
"SelectItemWidget": PCustom(sort=11, text="物品", editAttribute="SelectItemWidget"),
editAttribute="MCEnumEx"
效果同editAttribute="MCEnum",可以支持当前值不在枚举列表的情况。
editAttribute="MCCustomCreature":
自定义生物选取控件,返回值是生物的identifier
example:
"MCCustomCreature":PCustom(sort=3, editAttribute="MCCustomCreature"),
editAttribute='MCItems':
选取mc物品的控件,显示小图标和名字
跟随属性:withNamespace:bool(False)
返回值是否带命名空间 默认值FalsewithAuxValue:bool(True)
是否返回Aux值 ,该值用于指定子物品分类id 默认值TrueisBlock:bool(None)
选择列表是否需要方块,三种情况:None 所有物品,True 只有方块,False 只没有方块,默认值为None
itemCategory:int(31)
默认选中分类,与categoryList配合使用建筑 = 1 自然 = 8 物品 = 4 装备 = 2 全部 = 31
categoryList;[int]
这是一个数组,用于设置显示物品分类列表页的内容,物品分类参考 itemCategory,默认值是None,会显示所有分类页
example:
'MCItems': PCustom( sort=4, editAttribute='MCItems', default=('minecraft:apple', 0), withNamespace=False, withAuxValue=True, isBlock=None, itemCategory=16, categoryList=[16,31] ), ... self.MCItems = ('end_bricks', 0)
返回值:如果有多个默认值则使用tuple类型,withAuxValue与的设置相关
editAttribute="MCResource":
文件资源选取控件,返回相对于当前存档路径的相对路径
跟随属性:
extension: [str]
过滤文件后缀名
pack:str(None)
如果是“b”字符串则以行为包里的文件夹为查找目录,否则是资源包的文件夹,默认值None
baseFolder:[str]
字符串数组,文件夹路径数组,用于设置搜索路径,返回结果不包含此路径 默认值[]
relativePath:[str]
字符串数组,文件夹路径数组,在baseFolder的基础上继续设置搜索路径,返回结果包含此路径 默认值[]
recursive: bool(False)
是否递归遍历文件搜索文件,如果False只会搜索当前路径(由pack,baseFolder,relativePath决定)下的文件,默认值False
withExtension:bool(False)
返回值是否带文件后缀
example:
'MCResource': PCustom( sort=4, tip='背包格子中该物品显示的贴图。', editAttribute="MCResource", baseFolder = ["textures"], relativePath=['items'], extension='.png', default='textures/items/apple', description='image', withExtension= True, ) ... self.MCResource = "items/apple.png"
原生/自定义生物下拉列表
在编辑器内,我们提供了EntityManager单例,它提供了如下接口:
- getCreatureEnum:原生生物和自定义生物
- getExtraCreatureEnum:自定义生物
利用这些接口,结合PCustom,可以定制编辑器的生物下拉列表,以下为内置的实体零件样例:
from Meta.ClassMetaManager import sunshine_class_meta from Meta.TypeMeta import PBool, PStr, PInt, PCustom, PVector3, PVector3TF, PEnum, PDict, PFloat, PArray, PVector2 from MC.World.EntityManager import EntityManager from Preset.Model import PartBaseMeta @sunshine_class_meta class EntityBasePartMeta(PartBaseMeta): CLASS_NAME = "EntityBasePart" PROPERTIES = { "engineType": PCustom(sort=101, group="实体", text="实体类型", editAttribute="MCEnum", customFunc=lambda _: EntityManager.getCreatureEnum()), }
原生/自定义物品选择
"item": PCustom(sort=102, text="类型", editAttribute='MCItems', withAuxValue=True, withNamespace=True),
func 属性说明实例
def updateMCEnum(obj): return { "d": "2", "b": "1", } def updateMCEnum1(obj): return { "d": "2d", "b": "1d", } def fun1(): return {"visible": True, "text": "hello", "customFunc": updateMCEnum1} @sunshine_class_meta class EmptyPartMeta(PartBaseMeta): CLASS_NAME = "EmptyPart" PROPERTIES = { "test": PCustom(sort=1, text="test", func=fun1, editAttribute="MCEnum", customFunc=updateMCEnum) }
如上所示组件通过func属性动态修改了text属性和customFunc属性,func所指向的函数会在当前编辑数据有变化时被调用。
# 附录:
# 示例
# MyPart.py
from Preset.Model.PartBase import PartBase
from Preset.Model.GameObject import registerGenericClass
@registerGenericClass("MyPart")
class MyPart(PartBase):
def __init__(self, uuid):
super(SafeAreaData, self).__init__(uuid)
self.int1 = 1
self.int2 = 2
self.float1 = 1.1
self.float2 = 2.2
self.bool1 = True
self.bool2 = False
self.str1 = "str1"
self.str2 = "str2"
self.enum1 = 1
self.enum2 = "zombie"
self.vector2 = [1.0, 2.0]
self.vector3 = [1.0, 2.0, 3.0]
self.vector4 = [1.0, 2.0, 3.0, 4.0]
self.color1 = [10, 20, 30]
self.color2 = "#A6B7C8"
self.array1 = ["item1", "item2", "item3"]
self.array2 = [1, 2, 3]
self.dict1 = {"key1": 1, "key2": "str_2"}
self.dict2 = {"key1": 1, "key2": "str_2"}
self.groupedInt = 1
self.groupedFloat = 1.1
self.groupedBool = True
self.groupedStr = "grouped str"
self.groupedEnum = "pig"
self.groupedVector3 = [1.0, 2.0, 3.0]
self.groupedColor = [40, 50, 60]
self.groupedArray = ["item1", "item2", "item3"]
self.groupedDict = {"key1": 1, "key2": "str_2"}
# MyPartMeta.py
from Meta.ClassMetaManager import sunshine_class_meta
from Meta.EnumMeta import DefEnum
from Meta.TypeMeta import PInt, PEnum
from Preset.Model import PartBaseMeta
DefEnum("IntOption", {1: "选项1", 2: "选项2"})
DefEnum("StrOption", {"zombie": "僵尸", "pig": "猪"})
@sunshine_class_meta
class SafeAreaDataMeta(PartBaseMeta):
CLASS_NAME = "SafeAreaData"
PROPERTIES = {
"int1": PInt(text="整数1", sort=1, default=1, tip="这是个整数"),
"int2": PInt(text="整数2", sort=2, default=1),
"float1": PFloat(text="浮点数1", sort=3, default=1.1),
"float2": PFloat(text="浮点数2", sort=4, default=2.0),
"bool1": PBool(text="Bool1", sort=5, default=False),
"bool2": PBool(text="Bool2", sort=6),
"str1": PStr(text="字符串1", sort=7, default=""),
"str2": PStr(text="字符串2", sort=8, default="default"),
"enum1": PEnum(text="枚举1", sort=9, enumType="IntOption"),
"enum2": PEnum(text="枚举2", sort=10, enumType="StrOption"),
"vector2": PVector2(text="二维向量", sort=11),
"vector3": PVector3(text="三维向量", sort=13),
"vector4": PVector4(text="四维向量", sort=15),
"color1": PColor(text="颜色1", sort=16),
"color2": PColor(text="颜色2", sort=17, format="#RRGGBB"),
"array1": PArray(text="字符串数组", sort=18, childAttribute=PStr()),
"array2": PArray(text="整数数组", sort=19, childAttribute=PInt()),
"dict1": PDict(text="字典1", sort=20, children={
"key1": PInt(),
"key2": PStr(),
}),
"dict2": PDict(text="字典2", sort=21, removable=True, addable=True, children={
"key1": PInt(),
"key2": PStr(),
}),
"groupedInt": PInt(text="组内整数", sort=22, group="分组1:数字"),
"groupedFloat": PFloat(text="组内浮点数", sort=23, group="分组1:数字"),
"groupedBool": PBool(text="组内Bool", sort=24, group="分组2:其他"),
"groupedStr": PStr(text="组内字符串", sort=25, group="分组2:其他"),
"groupedEnum": PEnum(text="组内枚举", sort=26, enumType="StrOption", group="分组2:其他"),
"groupedVector3": PVector3(text="组内Vector3", sort=27, group="分组2:其他"),
"groupedColor": PColor(text="组内颜色", sort=28, group="分组2:其他"),
"groupedArray": PArray(text="组内数组", sort=29, childAttribute=PStr(), group="分组2:其他"),
"groupedDict": PDict(text="组内字典", sort=30, group="分组2:其他", children={
"key1": PInt(),
"key2": PStr(),
}),
}
# 实际效果
← 零件开发 自定义属性零件示例模板 →