我正在尝试使用Mono.Cecil在Unity中修补我的自定义用户脚本。
我希望将代码注入Unity中的自定义用户脚本,以避免在项目中的每个MonoBehaviour中编写相同的代码行。 但是,当我这样做时:
using (AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(assembly.Location, new ReaderParameters() { ReadWrite = true }))
{
//Do some patching here
assemblyDefinition.Write();
}
然后我得到一个例外
IOException:Win32 IO返回1224
这显然意味着文件被锁定而不被写入。
如果我改为尝试使用:
File.Delete(sourceAssemblyPath);
File.Move(targetAssemblyPath, sourceAssemblyPath);
然后dll被正确修补,但是当我尝试播放应用程序时,我的场景中的脚本会丢失引用,就像更换文件导致他们认为场景对象上的脚本不再存在于项目中一样(我想这是有意义的,因为我DID删除了他们所用的dll以用新的替换它。)
有没有人知道如何在Unity中修补用户的项目程序集,同时保持当前项目的可用性? 或者我应该在构建期间仅使用修补或其他什么? 建议?
由于
答案 0 :(得分:2)
上次我尝试使用Cecil时,我可以使用单个var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
来读取和写入文件,而无需删除/复制。
如果你使用[InitializeOnLoad]
这样做,那么装配显然已经加载了,所以在那时修改它们不会有帮助;你需要加载程序集一次以触发Cecil而不是重新加载以在每次通常只重新加载一次时加载修改。您需要使用UnityEditor.AssemblyReloadEvents.beforeAssemblyReload
代替。
beforeAssemblyReload
。因此,您使用[InitializeOnLoad]
注册回调([DidReloadScripts]
在我尝试的每种情况下看起来都相同),这应该确保所有新编译的程序集都会得到处理。在某些情况下,这可能不会发生(例如,如果首次打开编辑器时需要编译脚本,因此尚未注册您的事件),因此您可能还需要在初始化时立即运行处理代码,如果使用UnityEditorInternal.InternalEditorUtility.RequestScriptReload();
或UnityEditor.AssetDatabase.Refresh();
更改了任何内容,则强制重新加载程序集。
我发现将程序集标记为已处理的最佳方法是注入属性定义并将其实例添加到程序集,然后按名称检查它并跳过程序集(如果存在)。如果没有办法,那么每次脚本重新编译时,您都要处理项目中的每个程序集,而不是仅处理已修改的程序集。
编辑:要处理构建的程序集,请尝试以下方法:
private static bool _HasProcessed;
[PostProcessScene]
private static void OnPostProcessScene()
{
if (_HasProcessed || !BuildPipeline.isBuildingPlayer)
return;
ProcessAssemblies(@"Library\PlayerDataCache");
_HasProcessed = true;
}
[PostProcessBuild]
private static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
{
_HasProcessed = false;
}