如何在python脚本删除unreal下的某个文件夹(包括下面的资源文件)

重现步骤
我需要使用python脚本在unreal项目下删除某个文件夹和下面的资源文件,目前的做法是先找到所有的资源先删除,然后再删除空目录。

但是这种方法可能会导致unreal奔溃,请问这是为什么,以及是否有安全的删除文件夹的方法。

备注:在手动删除的时候,如果打开目录下的关卡会提示错误;所以我在运行脚本的时候已经确保当前打开的关卡不是该目录下的,但是还是会奔溃

备注1:以下是代码​

[Image Removed]

备注2:以下是奔溃截图

[Image Removed]

您好,我试了一下您的代码,并没有出现您所说的问题。

看代码也没有什么问题,注意到您是在5.5上测试的,是否有升级5.6的计划,或者测试看一下

[Image Removed]

我重新测试了代码,发现可能是unreal认为该文件夹下的关卡还打开的原因;

虽然我在其他地方已经切换了关卡,但是可能存在延时,导致实际上在删除的时候并没有;

所以我在删除之前强制加上了切换的代码就正常了。

另外建议框架在处理这个问题的时候优化一下,给出相应的异常提示,而不是奔溃

​ [Image Removed]

您好,我试了一下在关卡打开情况下确实是有这个问题。

如果您的工程包含代码,可以在Module Startup的时候添加一个 FEditorDelegates::OnAssetsCanDelete,在里面调用ContainsWorldInUse

这样就会在删除前进行检测

这份代码会导致内存爆满,机器卡死,而且最终还没有删除成功,不知道是不是使用的问题,以下是我的代码;

我想达到的目的是删除文件夹和下面的资源,而不只是让unreal不奔溃。。

bool ContainsWorldInUse(const TArray< UObject* >& ObjectsToDelete)

{

TArray<const UWorld*> WorldsToDelete;

for (const UObject* ObjectToDelete : ObjectsToDelete)

{

if (const UWorld\* World \= Cast\<UWorld\>(ObjectToDelete))

{

  WorldsToDelete.AddUnique(World);

}

}

if (WorldsToDelete.Num() == 0)

{

return false;

}

auto GetCombinedWorldNames = (const TArray<const UWorld*>& Worlds) -> FString

{

  return FString::JoinBy(Worlds, TEXT(", "),

    \[](const UWorld\* World) \-\> FString

    {

      return World\-\>GetPathName();

    });

};

//UE_LOG(LogAssetAnalyzer, Log, TEXT(“Deleting %d worlds: %s”), WorldsToDelete.Num(), *GetCombinedWorldNames(WorldsToDelete));

TArray<const UWorld*> ActiveWorlds;

for (const FWorldContext& WorldContext : GEditor->GetWorldContexts())

{

if (const UWorld\* World \= WorldContext.World())

{

  ActiveWorlds.AddUnique(World);



  for (const ULevelStreaming\* StreamingLevel : World\-\>GetStreamingLevels())

  {

    if (StreamingLevel \&\& StreamingLevel\-\>GetLoadedLevel() \&\& StreamingLevel\-\>GetLoadedLevel()\-\>GetOuter())

    {

      if (const UWorld\* StreamingWorld \= Cast\<UWorld\>(StreamingLevel\-\>GetLoadedLevel()\-\>GetOuter()))

      {

        ActiveWorlds.AddUnique(StreamingWorld);

      }

    }

  }

}

}

//UE_LOG(LogAssetAnalyzer, Log, TEXT(“Currently %d active worlds: %s”), ActiveWorlds.Num(), *GetCombinedWorldNames(ActiveWorlds));

for (const UWorld* World : WorldsToDelete)

{

if (ActiveWorlds.Contains(World))

{

  return true;

}

}

return false;

}

void FAssetAnalyzerModule::OnAssetsCanDelete(const TArray<UObject*>& Assets /*InObjectToDelete*/, FCanDeleteAssetResult& result)

{

// 保证 CanDelete 长度和 Assets 一致

for (int32 i = 0; i < Assets.Num(); ++i)

{

bool bInUse \= false;

if (const UWorld\* World \= Cast\<UWorld\>(Assets\[i]))

{

  bInUse \= ContainsWorldInUse({ Assets\[i] });

  result.Set(!bInUse);

}

}

}

void FAssetAnalyzerModule::StartupModule()

{

// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module

FEditorDelegates::OnAssetPostImport.AddRaw(this, &FAssetAnalyzerModule::OnAssetPostImport);

FEditorDelegates::OnAssetsCanDelete.AddRaw(this, &FAssetAnalyzerModule::OnAssetsCanDelete);

}

这个函数改成这样,我这边是正常的

void FAssetAnalyzerModule::OnAssetsCanDelete(const TArray<UObject*>& Assets, FCanDeleteAssetResult& result)

{

result.Set(!ContainsWorldInUse(Assets));

}

其实您前面的脚本里已经做了切换一个地图的操作,已经可以实现这个需求了

其实代码里是有处理 【删除正在使用的地图】的一些相关逻辑代码的,但是目前失效了,我给相关的同事提一下

跟了一下代码,ShownObjectsToDelete里面的对象在打开临时地图的时候已经被GC了,所以出错了

如果你们使用的是源码版本引擎可以按照下面修改一下,这个问题应该要等到5.8才能修复

否则只能先通过OnAssetsCanDelete先跳过

[Image Removed]

好的,我先用切换关卡的办法处理这个问题,不在C++里修改了。

感谢耐心解答。