[LayaAir3]自定义Laya.Component,经代码修改其属性(不是人手修改)之后,保存prefab无变化,重新打开prefab发现改动丢失
一.猜测可能是需要调用某个api来标记prefab脏了,这样才能在prefab保存的时候对变动了的属性进行序列化; 但是我查文档和查社区都没有看到类似的描述; 全局搜索dirty相关的内容,也没有看到明显相关的api
二.用代码来修改自定义Laya.Component的思路是:
1.先定义这个组件
const {regClass, property, classInfo} = Laya;
@regClass()
exporty class TextComp extends Laya.Component {
// 无意义的成员变量,只是为了在属性设置里画一个按钮
@property({type : "number", inspector : "TextCompButtonInspector", private : false})
private test_number: number;
//准备使用自己写的编辑器窗口来修改的属性,专门指定inspector = null以在属性设置中隐藏默认的inspector
@property({type : [TextCompSubData]}, inspector = null)
public sub_datas?: TextCompSubData[] = [];
}
2.画按钮
@IEditor.inspectorField("TextCompButtonInspector")
export class TextCompButtonInspector extends IEditor.PropertyField {
// 按照文档说的要先加载widget资源
@IEditor.onLoad
static async onLoad() {
await gui.UIPackage.resourtceMgr.load("editorResources/myWidget.widget");
}
// 画一个按钮
create(){
const input: gui.Widget = gui.UIPackage.createWidgetSync("editorResources/myWidget.widget");
// 提前在widget里画好一个叫open_btn的按钮
const open_btn: gui.Button = input.getChild("open_btn");
open_btn.on("click", () => {
Editor.panelManager.showPanel("TextCompEditEditor"); // 打开真正用来编辑组件属性的编辑器窗口
// 需要把TextComp对象传到刚刚打开的编辑器窗口里,方便在窗口里修改TextComp的属性
// 但是按文档说,场景进程和UI进程不互通,所以当按下"open_btn"按钮时,只能找到当前选择的node_id: string
// 然后异步执行Edition.scene.runScript去让场景进程下的代码,根据node_id找到对应的TextComp
const selection_obj = Editor.scene.getSelection()[0];
const node_id: string = selection_obj.id; //
Edition.scene.runScript("TextCompSceneProcess.InitMyComp", node_id);
})
}
}
3.场景进程里根据node_id找到TextComp
@IEditorEnv.regClass()
export class TextCompSceneProcess {
static async InitMyComp(this: IEditorEnv.IGameScene, node_id: string) {
const node = <Laya.Node>this.getNodeById(node_id);
if (node == undefined) {
throw Error("未找到节点");
}
const myComp: TextComp = node.getComponent(TextComp);
// 再转发到UI进程, 但是测试没有办法直接传MyComponent到编辑器窗口下,所以曲线救国,把用于展示的内容以string形式传过去
// 再把node_id也传过去,在编辑器窗口有任何试图对属性的操作,继续使用Edition.scene.runScript再绕到本场景进程里来操作TextComp的属性
EditorEnv.sendMessageToPanel("TextCompEditEditor", "SetMyComp", myComp:GenShowInfoString(), node_id);
}
}
三.bug描述:
1.当使用上面第二大点里的流程,来在自己绘制的TextCompEditEditor窗口中修改TextComp的属性,是有效果的; 鼠标左键点其他节点再点回来,依然能在编辑器窗口上看到修改后的值。但是保存prefab的时候并没有保存这个内容,查看.lh文件无变化.
2.倘若执行了三.1的操作时先不急着保存prefab退出,而是在属性设置窗口里,把TextComp先disable一下,再enable起来,这样保存prefab,就能观察到.lh文件发生了变化,重新打开prefab也能看到TextComp的属性是修改后的属性
3.对照实验,倘若去掉'inspctor = null', 改成使用默认的inspector
@property({type : [TextCompSubData]})
public sub_datas?: TextCompSubData[] = [];
则直接在属性设置中,添加、删除、修改这个数组的属性,再保存prefab,是能正常保存prefab的
4.猜测:可能默认的inspector对属性的操作,会标记这个component脏了,在保存prefab的时候才会重新序列化它; 而我自己写的编辑器窗口,是使用代码去修改TextComp的属性,就遗漏了上面那个标记dirty的api,从而保存prefab不会重新序列TextComp,导致改动无保存,得手动disable 再enable一下整个TextComp才能正常保存。
5.所以我的猜测对吗?如果是对的,有哪个api能标记Compoent为脏呢?
二.用代码来修改自定义Laya.Component的思路是:
1.先定义这个组件
const {regClass, property, classInfo} = Laya;
@regClass()
exporty class TextComp extends Laya.Component {
// 无意义的成员变量,只是为了在属性设置里画一个按钮
@property({type : "number", inspector : "TextCompButtonInspector", private : false})
private test_number: number;
//准备使用自己写的编辑器窗口来修改的属性,专门指定inspector = null以在属性设置中隐藏默认的inspector
@property({type : [TextCompSubData]}, inspector = null)
public sub_datas?: TextCompSubData[] = [];
}
2.画按钮
@IEditor.inspectorField("TextCompButtonInspector")
export class TextCompButtonInspector extends IEditor.PropertyField {
// 按照文档说的要先加载widget资源
@IEditor.onLoad
static async onLoad() {
await gui.UIPackage.resourtceMgr.load("editorResources/myWidget.widget");
}
// 画一个按钮
create(){
const input: gui.Widget = gui.UIPackage.createWidgetSync("editorResources/myWidget.widget");
// 提前在widget里画好一个叫open_btn的按钮
const open_btn: gui.Button = input.getChild("open_btn");
open_btn.on("click", () => {
Editor.panelManager.showPanel("TextCompEditEditor"); // 打开真正用来编辑组件属性的编辑器窗口
// 需要把TextComp对象传到刚刚打开的编辑器窗口里,方便在窗口里修改TextComp的属性
// 但是按文档说,场景进程和UI进程不互通,所以当按下"open_btn"按钮时,只能找到当前选择的node_id: string
// 然后异步执行Edition.scene.runScript去让场景进程下的代码,根据node_id找到对应的TextComp
const selection_obj = Editor.scene.getSelection()[0];
const node_id: string = selection_obj.id; //
Edition.scene.runScript("TextCompSceneProcess.InitMyComp", node_id);
})
}
}
3.场景进程里根据node_id找到TextComp
@IEditorEnv.regClass()
export class TextCompSceneProcess {
static async InitMyComp(this: IEditorEnv.IGameScene, node_id: string) {
const node = <Laya.Node>this.getNodeById(node_id);
if (node == undefined) {
throw Error("未找到节点");
}
const myComp: TextComp = node.getComponent(TextComp);
// 再转发到UI进程, 但是测试没有办法直接传MyComponent到编辑器窗口下,所以曲线救国,把用于展示的内容以string形式传过去
// 再把node_id也传过去,在编辑器窗口有任何试图对属性的操作,继续使用Edition.scene.runScript再绕到本场景进程里来操作TextComp的属性
EditorEnv.sendMessageToPanel("TextCompEditEditor", "SetMyComp", myComp:GenShowInfoString(), node_id);
}
}
三.bug描述:
1.当使用上面第二大点里的流程,来在自己绘制的TextCompEditEditor窗口中修改TextComp的属性,是有效果的; 鼠标左键点其他节点再点回来,依然能在编辑器窗口上看到修改后的值。但是保存prefab的时候并没有保存这个内容,查看.lh文件无变化.
2.倘若执行了三.1的操作时先不急着保存prefab退出,而是在属性设置窗口里,把TextComp先disable一下,再enable起来,这样保存prefab,就能观察到.lh文件发生了变化,重新打开prefab也能看到TextComp的属性是修改后的属性
3.对照实验,倘若去掉'inspctor = null', 改成使用默认的inspector
@property({type : [TextCompSubData]})
public sub_datas?: TextCompSubData[] = [];
则直接在属性设置中,添加、删除、修改这个数组的属性,再保存prefab,是能正常保存prefab的
4.猜测:可能默认的inspector对属性的操作,会标记这个component脏了,在保存prefab的时候才会重新序列化它; 而我自己写的编辑器窗口,是使用代码去修改TextComp的属性,就遗漏了上面那个标记dirty的api,从而保存prefab不会重新序列TextComp,导致改动无保存,得手动disable 再enable一下整个TextComp才能正常保存。
5.所以我的猜测对吗?如果是对的,有哪个api能标记Compoent为脏呢?
没有找到相关结果
已邀请:
1 个回复
谷主
赞同来自: 峰竹百回
正确的方式是
1、在UI进程修改数据
2、如果确实想在场景进程修改,那么要使用EditorEnv.scene.recordObject,标记你在修改的属性。
3、如果不想用2,只想无脑告诉IDE场景已经发生修改,可以直接用EditorEnv.scene.setModified。