前言
前段时间有遇到一个需求,需要在资源保存的时候做一些验证,检查资源是否正确。
这就需要实现保存的时候触发一个 HOOK 执行相应的检验函数。
C++ 实现
面对这个问题,我简单查了一下 Python 相关的调用,似乎没有找到合适的调用函数,于是就去 C++ 里面加一个保存 HOOK 了
其实当时是找到很是的函数了, 只是 Validator 误导了我,所以我就没有仔细去研究。
关于 C++ 的保存调用是相当的简单,在 UPackage 下有相关的 Event 链接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| void FPyToolkitModule::StartupModule(){
PluginName = "PyToolkit"; Content = FPaths::ProjectPluginsDir() / PluginName + TEXT("/Content");
FString JsonSetting = Content + TEXT("/settings.json"); TSharedPtr<FJsonObject> JsonObject = FPyCommandList::ReadJson(JsonSetting); SettingObject = JsonObject->GetObjectField("script");
UPackage::PreSavePackageEvent.AddRaw(this, &FPyToolkitModule::OnPackageSaved); }
void FPyToolkitModule::OnPackageSaved(UPackage *Package) { static FFormatNamedArguments Arguments; Arguments.Add(TEXT("Content"), FText::FromString(Content)); FString Path = Package->GetName(); FString ClosedScript = FText::Format(FTextFormat::FromString(SettingObject->GetStringField("OnPackageSaved")), Arguments).ToString() + FString(TEXT(" ")) + Path; GEngine->Exec(NULL, ClosedScript.GetCharArray().GetData()); }
|
通过上面的代码可以非常简单实现保存的时候调用相关的 Python 脚本的效果。
只是要确保 Content 目录下要有 settings.json 的配置文件,配置 OnPackageSaved
项来指定调用的脚本。
否则启动的时候会因为找不到 json 文件直接 引擎崩溃 (还是做个检测处理比较妥当(:з」∠))
Python Hook 实现
后来非常偶然地加了一个网友,在交流的过程中,他教会了我使用 subsystem 的 Hook 实现导入的时候触发对应的 Python 函数进行处理。
于是我在这个基础上我又仔细研究了 EditorValidatorSubsystem
也顺利利用纯 Python 来解决我最开始提到的保存触发函数的问题。
首先回到最开始的问题,保存的时候触发相应的函数。
可以利用 EditorValidatorSubsystem
来实现 Python文档
这个类提供了 add_validator
方法,可以给 Asset 添加相应的 Validate 功能
添加 validator 需要继承 EditorValidatorBase 这个类,然后重载相关的功能。
这种操作,具体可以参考我之前写的 Unreal Python ToolMenuEntryScript 使用研究 文章
利用 Unreal Python 继承一个 C++ 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import unreal
@unreal.uclass() class OnAssetSaveValidator(unreal.EditorValidatorBase):
@unreal.ufunction(override=True) def can_validate_asset(self,asset): msg = 'Save Hook Trigger' unreal.SystemLibrary.print_string(None,msg,text_color=[255,255,255,255]) return super(OnAssetSaveValidator,self).can_validate_asset(asset)
def main(): validator = OnAssetSaveValidator() validator.set_editor_property("is_enabled",True) validate_subsystem = unreal.get_editor_subsystem(unreal.EditorValidatorSubsystem) validate_subsystem.add_validator(validator)
if __name__ == "__main__": main()
|
在 unreal 里面执行上面的代码,保存的时候就会自动打印 Save Hook Trigger
的语句了。
经过我的测试,我发现只有 can_validate_asset
可以正常触发,其他的函数尝试 override 了,但是保存的时候没有起作用。
不过有 can_validate_asset
触发就足够了。
我们可以检测传入的 asset 类型,然后调用相应的保存处理函数。
这后面可以做的事情就很多了,资源的校验,配置选项的自动处理,自动生成配套资源 等等等。
我之前就做了一个根据当前材质实例的勾选自动切换母材质的功能。
同样地,借助 subsystem 还可以实现 import 的 HOOK,
具体用到了 ImportSubsystem
其中提供了四种不同情况的调用,用得多的是 on_asset_post_import
导入之后对导入的资源进行后处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import unreal
def import_log(factory, created_object): msg = "Import Hook Trigger" unreal.SystemLibrary.print_string(None, msg, text_color=[255, 255, 255, 255]) print(factory, created_object)
def main(): import_subsystem = unreal.get_editor_subsystem(unreal.ImportSubsystem) global on_asset_post_import_delegate on_asset_post_import_delegate = unreal.OnAssetPostImport_Dyn() on_asset_post_import_delegate.add_callable(import_log) import_subsystem.set_editor_property("on_asset_post_import",on_asset_post_import_delegate)
if __name__ == "__main__": main()
|
需要注意的是 delegate 需要用全局变量指定,否则使用一段时间之后 hook 会因为 gc 问题失效。
这个问题非常类似我当时做批量渲染工具时候遇到的问题 Unreal Python Sequencer 批量渲染总结
如果导入资源的命名有相应的规范,就可以自动把相关的属性勾选上,特别是 Texture 之类的处理会非常好用,美术也不会忘记勾选导致各种问题。
总结
纯 Python 的实现目前只能拘束在 Import 和 Save 两个应用场景。
如果需要额外的应用场景比如说 打开资源的时候触发 等,就需要依靠 C++ 来实现了。