油管uepython教程
在UE中的python初始配置
可以看看这个文章https://www.yumefx.com/?p=3211
插件的选择
找到scripting启用这三个
项目设置
第一个 startup scripts 中填写的是,当启动UE时自动加载的脚本的路径 第二个Additional paths 中填写的是 当在UE中使用import模块时 import的额外搜索路径
使用python导入资产
导入贴图和音频
用到的类:https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/AssetImportTask.html https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/AssetTools.html 首先先了解一下引擎中的文件路径:这里内容的路径为/Game,因此假如要在内容下创建一个新的Tex文件夹,那么Tex文件夹的路径就是/Game/Tex 在ue的编辑界面中按shitf+回车可以输入多行代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import unrealsoundPath = 'D:/ZhangRuiChen/UETest/TestFile/Sounds/soundTest.mp3' texPath = 'D:/ZhangRuiChen/UETest/TestFile/Texture/test_ORM3.png' def importMyAssets (): texture_task = buildImportTask(texPath, '/Game/Textures' ) sound_task = buildImportTask(soundPath, '/Game/Sounds' ) executeImportTasks([texture_task, sound_task]) def buildImportTask (filename, destination_path ): task = unreal.AssetImportTask() task.set_editor_property('automated' , True ) task.set_editor_property('destination_name' , '' ) task.set_editor_property('destination_path' , destination_path) task.set_editor_property('filename' , filename) task.set_editor_property('replace_existing' , True ) task.set_editor_property('save' , True ) return task def executeImportTasks (tasks ): unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks(tasks) for task in tasks: for path in task.get_editor_property('imported_object_paths' ): print ('Imported: %s' % path)
导入静态网格体和骨骼网格体
用到的类:https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/FbxImportUI.html https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/FbxMeshImportData.html https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/FbxStaticMeshImportData.html https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/FbxSkeletalMeshImportData.html 我用的4.26版本的UE,使用这个代码没有办法导入骨骼体,代码已经反复检查几遍了没发现问题。教程中用的是4.21版本的UE。 后来测试后发现是可以的,应该是之前的资产有问题,后来测试用的小白人fbx2018的是可以导入进来的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 import unrealsoundPath = 'D:/ZhangRuiChen/UETest/TestFile/Sounds/soundTest.mp3' texPath = 'D:/ZhangRuiChen/UETest/TestFile/Texture/test_ORM3.png' static_mesh_fbx = 'D:/ZhangRuiChen/UETest/TestFile/StaticMeshes/sphere.fbx' skeletal_mesh_fbx = 'D:/ZhangRuiChen/UETest/TestFile/Skeletal/woman.fbx' def importMyAssets (): static_mesh_task = buildImportTask(static_mesh_fbx, '/Game/StaticMeshes' , buildStaticMeshImportOptions()) skeletal_mesh_task = buildImportTask(skeletal_mesh_fbx, '/Game/SkeletalMeshes' , buildSkeletalMeshImportOptions()) executeImportTasks([static_mesh_task, skeletal_mesh_task]) def buildImportTask (filename, destination_path, options=None ): task = unreal.AssetImportTask() task.set_editor_property('automated' , True ) task.set_editor_property('destination_name' , '' ) task.set_editor_property('destination_path' , destination_path) task.set_editor_property('filename' , filename) task.set_editor_property('replace_existing' , True ) task.set_editor_property('save' , True ) task.set_editor_property('options' , options) return task def executeImportTasks (tasks ): unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks(tasks) for task in tasks: for path in task.get_editor_property('imported_object_paths' ): print ('Imported: %s' % path) def buildStaticMeshImportOptions (): options = unreal.FbxImportUI() options.set_editor_property('import_mesh' , True ) options.set_editor_property('import_textures' , False ) options.set_editor_property('import_materials' , False ) options.set_editor_property('import_as_skeletal' , False ) options.static_mesh_import_data.set_editor_property('import_translation' , unreal.Vector(0.0 , 0.0 , 0.0 )) options.static_mesh_import_data.set_editor_property('import_rotation' , unreal.Rotator(0.0 , 0.0 , 0.0 )) options.static_mesh_import_data.set_editor_property('import_uniform_scale' , 1.0 ) options.static_mesh_import_data.set_editor_property('combine_meshes' , True ) options.static_mesh_import_data.set_editor_property('generate_lightmap_u_vs' , True ) options.static_mesh_import_data.set_editor_property('auto_generate_collision' , True ) return options def buildSkeletalMeshImportOptions (): options = unreal.FbxImportUI() options.set_editor_property('import_mesh' , True ) options.set_editor_property('import_textures' , True ) options.set_editor_property('import_materials' , True ) options.set_editor_property('import_as_skeletal' , True ) options.skeletal_mesh_import_data.set_editor_property('import_translation' , unreal.Vector(0.0 , 0.0 , 0.0 )) options.skeletal_mesh_import_data.set_editor_property('import_rotation' , unreal.Rotator(0.0 , 0.0 , 0.0 )) options.skeletal_mesh_import_data.set_editor_property('import_uniform_scale' , 1.0 ) options.skeletal_mesh_import_data.set_editor_property('import_morph_targets' , True ) options.skeletal_mesh_import_data.set_editor_property('update_skeleton_reference_pose' , False ) return options
导入动画
跟导入静态网格体差不多,就是导入动画时的额外设置略有不同,并且创建任务需要使用导入动画的设置时需要指定动画依赖的骨骼的路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 def buildAnimationImportOptions (skeleton_path ): options = unreal.FbxImportUI() options.set_editor_property('import_animations' , True ) options.skeleton = unreal.load_asset(skeleton_path) options.anim_sequence_import_data.set_editor_property('import_translation' , unreal.Vector(0.0 , 0.0 , 0.0 )) options.anim_sequence_import_data.set_editor_property('import_rotation' , unreal.Rotator(0.0 , 0.0 , 0.0 )) options.anim_sequence_import_data.set_editor_property('import_uniform_scale' , 1.0 ) options.anim_sequence_import_data.set_editor_property('animation_length' , unreal.FBXAnimationLengthImportType.FBXALIT_EXPORTED_TIME) options.anim_sequence_import_data.set_editor_property('remove_redundant_keys' , False ) return options def executeImportTasks (tasks ): unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks(tasks) for task in tasks: for path in task.get_editor_property('imported_object_paths' ): print ('Imported: %s' % path) def importMyAssets (): animation_fbx = 'D:/ZhangRuiChen/UETest/TestFile/Animation/Animation.fbx' animation_fbx_task = buildImportTask(animation_fbx, '/Game/Animations' , buildStaticMeshImportOptions('/Game/SkeletalMeshes/man_Skeleton' )) executeImportTasks([animation_fbx_task])
创建、复制、删除、重命名资产和文件夹
这些东西可以直接复制到UE的代码编辑框中执行看看效果 针对资产编辑器中的内容都要使用unreal.EditorAssetLibrary中的方法,例如创建复制删除重命名文件夹,复制删除重命名资产,判段资产是否存在。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 import unrealdef createDirectory (): unreal.EditorAssetLibrary.make_directory('/Game/MyAsset/MyNewDirectory' ) def duplicateDirectory (): return unreal.EditorAssetLibrary.duplicate_directory('/Game/MyAsset/MyNewDirectory' , '/Game/MyAsset/MyNewDirectory_Duplicated' ) def deleteDirectory (): unreal.EditorAssetLibrary.delete_directory('/Game/MyAsset/MyNewDirectory' ) def renameDirectory (): return unreal.EditorAssetLibrary.rename_directory('/Game/MyAsset/MyNewDirectory_Duplicated' , '/Game/MyAsset/MyNewDirectory_Renamed' ) def duplicateAsset (): return unreal.EditorAssetLibrary.duplicate_asset('/Game/MyAsset/Textures/test_ORM3' , '/Game/MyAsset/Textures/test_ORM3_duplicate' ) def deleteAsset (): unreal.EditorAssetLibrary.delete_asset('/Game/MyAsset/Textures/test_ORM3' ) def assetExist (): print (unreal.EditorAssetLibrary.does_asset_exist('/Game/MyAsset/Textures/test_ORM3' )) print (unreal.EditorAssetLibrary.does_asset_exist('/Game/MyAsset/Textures/test_ORM3_duplicate' )) def renameAsset (): unreal.EditorAssetLibrary.rename_asset('/Game/MyAsset/Textures/test_ORM3_duplicate' , '/Game/MyAsset/Textures/test_ORM3_Renamed' ) def duplicateAssetDialog (show_dialog=True ): if show_dialog: unreal.AssetToolsHelpers.get_asset_tools().duplicate_asset_with_dialog('test_ORM3_Duplicated' , '/Game/MyAsset/Textures' , unreal.load_asset( '/Game/MyAsset/Textures/test_ORM3_Renamed' )) else : unreal.AssetToolsHelpers.get_asset_tools().duplicate_asset('test_ORM3_Duplicated' , '/Game/MyAsset/Textures' , unreal.load_asset( '/Game/MyAsset/Textures/test_ORM3_Renamed' )) def renameAssetDialog (show_dialog=True ): first_rename_data = unreal.AssetRenameData(unreal.load_asset('/Game/MyAsset/Textures/test_ORM3_Renamed' ), '/Game/MyAsset/Textures' , 'test_ORM3_Renamed_2' ) second_rename_data = unreal.AssetRenameData(unreal.load_asset('/Game/MyAsset/Textures/test_ORM3_Duplicated' ), '/Game/MyAsset/Textures' , 'test_ORM3_Duplicated_Renamed' ) if show_dialog: unreal.AssetToolsHelpers.get_asset_tools().rename_assets_with_dialog([first_rename_data, second_rename_data]) else : unreal.AssetToolsHelpers.get_asset_tools().rename_assets([first_rename_data, second_rename_data])
使用python调用C++函数
取名字为ZFunctions 创建这个类以后,UE会自动编译打开VisualStudio。 ZFunctions.h文件内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "ZFunctions.generated.h" UCLASS ()class CPLUSPLUSTEST_API UZFunctions : public UBlueprintFunctionLibrary{ GENERATED_BODY () public : UFUNCTION (BlueprintCallable) static void CalledFromPython (FString InputString) ; };
ZFunctions.cpp文件内容:
1 2 3 4 5 6 7 #include "ZFunctions.h" void UZFunctions::CalledFromPython (FString InputString) { UE_LOG (LogTemp, Error, TEXT ("%s" ), *InputString); }
内容写好后就可以 然后我们可以在编辑器中通过
1 2 for x in sorted (dir(unreal)) : print (x)
打印出目前UE项目中所有的类。 可以在输出中找到 那就可以使用了。 然后我们可以在编辑器中通过
1 2 for x in sorted (dir(unreal.ZFunctions)) : print (x)
打印出ZFunctions中的所有方法。 可以找到刚才定义的方法: 可以发现我们明明写的是CalledFromPython,但是UE会自动将命名更改为called_from_python 然后我们就可以在UE中使用这个方法了 通过代码 unreal.ZFunctions.called_from_python(‘11111’) 会使用这个方法的功能,让UE打印一个UE_LOG UE_LOG(LogTemp, Error, TEXT(“%s”), *InputString)
改变文件夹的颜色
要想修改文件夹的颜色依然需要使用C++,只不过可以通过python语句调用C++函数来实现简单的批量操作… 因此依然通过继承Blueprint Fuction Library 类来创建我们的自定义类。取名为CppLib
CppLib.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "CppLib.generated.h" UCLASS ()class CPLUSPLUSTEST_API UCppLib : public UBlueprintFunctionLibrary{ GENERATED_BODY () public : UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void SetFolderColor (FString FolderPath, FLinearColor Color) ; };
CppLib.cpp
1 2 3 4 5 6 7 8 #include "CppLib.h" void UCppLib::SetFolderColor (FString FolderPath, FLinearColor Color) { GConfig->SetString (TEXT ("PathColor" ), *FolderPath, *Color.ToString (), GEditorPerProjectIni); }
写完c++后可以通过蓝图调用函数
勾选自定义事件的 Call In Editor 就可以在外面手动点击触发自定义事件了。 如果是已经存在的文件夹路径,那么点击按钮后不会立刻改变文件夹的颜色,需要重新启动才能够发现改变。不存在的文件夹路径,当创建好对应的文件夹后,文件夹会自动改变颜色。
python调用函数实现批量更改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 # coding: utf-8 import unreal# import folderColor as fc # from imp import reload # reload(fc) # fc.generateColoredDirectories() def generateColoredDirectories () : for x in range(100 , 400 ): dir_path = '/Game/PythonGenerated/' + str (x) linear_color = getGradientColor (x) unreal.CppLib.set_folder_color (dir_path, linear_color) unreal.EditorAssetLibrary.make_directory (dir_path) def getGradientColor (x): x = x - 100 if x < 100 : r = 1.0 - x / 100.0 g = 0.0 + x / 100.0 b = 0.0 elif x < 200 : r = 0.0 g = 1.0 - (x - 100 ) / 100.0 b = 0.0 + (x - 100 ) / 100.0 else : r = 0.0 + (x - 200 ) / 100.0 g = 0.0 b = 1.0 - (x - 200 ) / 100.0 return unreal.LinearColor (r, g, b, 1 )
效果:
如何使用c++和Python打开和关闭资产
python 只能打开资产,关闭资产需要通过C++来实现。
先看python如何打开资产
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import unrealdef openAssets (): assets = [unreal.load_asset('/Game/MyAsset/Sounds/soundTest' ), unreal.load_asset('/Game/MyAsset/Textures/light_setAOVs' ), unreal.load_asset('/Game/MyAsset/Textures/light_createShot' ), unreal.load_asset('/Game/MyAsset/Textures/light_importLight' )] unreal.AssetToolsHelpers.get_asset_tools().open_editor_for_assets(assets) def getAllOpenedAssets (): return unreal.CppLib.get_assets_opened_in_editor() def closeAssets (): assets = getAllOpenedAssets() unreal.CppLib.close_editor_for_assets(assets)
UE中模块修改
这个.Build.cs格式的是当前项目的模块 进入这个模块需要修改: 主要是增加了一项:“UnrealEd”
CPlusPlusTest.Build.cs
首先说明一下这里的CPlusPlusTest是ue4.27中创建的项目名 在原有的基础上新增了"UnrealEd"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 using UnrealBuildTool;public class CPlusPlusTest : ModuleRules{ public CPlusPlusTest (ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange (new string[] { "Core" , "CoreUObject" , "Engine" , "InputCore" , "HeadMountedDisplay" , "UnrealEd" }); } }
CppLib.h
在上一节的基础上 新增了两个函数: CloseEditorForAssets GetAssetsOpendInEditor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "CppLib.generated.h" UCLASS ()class CPLUSPLUSTEST_API UCppLib : public UBlueprintFunctionLibrary{ GENERATED_BODY () public : UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void SetFolderColor (FString FolderPath, FLinearColor Color) ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void CloseEditorForAssets (TArray<UObject*> Assets) ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static TArray<UObject*> GetAssetsOpenedInEditor () ; };
CppLib.cpp
这里除了定义函数功能还有个要注意的是要include “Editor.h”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include "CppLib.h" #include "Editor.h" #include "Editor/UnrealEd/Public/Toolkits/AssetEditorManager.h" void UCppLib::SetFolderColor (FString FolderPath, FLinearColor Color) { GConfig->SetString (TEXT ("PathColor" ), *FolderPath, *Color.ToString (), GEditorPerProjectIni); } void UCppLib::CloseEditorForAssets (TArray<UObject*> Assets) { UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem <UAssetEditorSubsystem>(); for (UObject* Asset : Assets) { AssetEditorSubsystem->CloseAllEditorsForAsset (Asset); } } TArray<UObject*> UCppLib::GetAssetsOpenedInEditor () { UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem <UAssetEditorSubsystem>(); TArray<UObject*> EditedAssets = AssetEditorSubsystem->GetAllEditedAssets (); return EditedAssets; }
在UE中进行调用
from imp import reload import openAssets_10 as oa reload(oa) oa.openAssets()# 打开资产 oa.closeAssets()# 关闭资产 oa.getAllOpenedAssets()#得到当前打开的资产的信息(需要用一个变量来接受它,是一个列表)
使用Python和C ++选择内容浏览器的资产
python
使用python来选择有弊端,就是它不能选择文件夹以及关卡,因此还要介绍通过C++如何选择
1 2 3 4 5 6 7 8 9 import unrealdef showAssetsInContentBrowser (): paths = ['/Game/MyAsset/Sounds/SoundTest' , '/Game/MyAsset/Textures/light_setAOVs' ] unreal.EditorAssetLibrary.sync_browser_to_objects(paths)
CPlusPlusTest.Build.cs
又新增了这三个,因为.cpp用到了Module 最后的EditorWidgets不添加的话编译不了,教程中没有加这个,UE4.27,VS2019需要加EditorWidgets,加了以后就可以用了
CppLib.h
到现在我才发现在头文件里面写了构造函数后可以通过左边的螺丝刀图标快速创建在源文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "CppLib.generated.h" UCLASS ()class CPLUSPLUSTEST_API UCppLib : public UBlueprintFunctionLibrary{ GENERATED_BODY () public : UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void SetFolderColor (FString FolderPath, FLinearColor Color) ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void CloseEditorForAssets (TArray<UObject*> Assets) ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static TArray<UObject*> GetAssetsOpenedInEditor () ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static TArray<FString> GetSelectedAssets () ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static TArray<FString> GetSelectedFolders () ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void SetSelectedAssets (TArray<FString> Paths) ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void SetSelectedFolders (TArray<FString> Paths) ; };
CppLib.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include "CppLib.h" #include "Editor.h" #include "Editor/UnrealEd/Public/Toolkits/AssetEditorManager.h" #include "Editor/ContentBrowser/Public/ContentBrowserModule.h" #include "Editor/ContentBrowser/Private/ScontentBrowser.h" #include "Runtime/AssetRegistry/Public/AssetRegistryModule.h" void UCppLib::SetFolderColor (FString FolderPath, FLinearColor Color) { GConfig->SetString (TEXT ("PathColor" ), *FolderPath, *Color.ToString (), GEditorPerProjectIni); } void UCppLib::CloseEditorForAssets (TArray<UObject*> Assets) { UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem <UAssetEditorSubsystem>(); for (UObject* Asset : Assets) { AssetEditorSubsystem->CloseAllEditorsForAsset (Asset); } } TArray<UObject*> UCppLib::GetAssetsOpenedInEditor () { UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem <UAssetEditorSubsystem>(); TArray<UObject*> EditedAssets = AssetEditorSubsystem->GetAllEditedAssets (); return EditedAssets; } TArray<FString> UCppLib::GetSelectedAssets () { FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked <FContentBrowserModule>("ContentBrowser" ); TArray<FAssetData> SelectedAssets; ContentBrowserModule.Get ().GetSelectedAssets (SelectedAssets); TArray<FString> Result; for (FAssetData& AssetData : SelectedAssets) { Result.Add (AssetData.PackageName.ToString ()); } return Result; } TArray<FString> UCppLib::GetSelectedFolders () { FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked <FContentBrowserModule>("ContentBrowser" ); TArray<FString> SelectedFolders; ContentBrowserModule.Get ().GetSelectedFolders (SelectedFolders); return SelectedFolders; } void UCppLib::SetSelectedAssets (TArray<FString> Paths) { FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked <FContentBrowserModule>("ContentBrowser" ); FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked <FAssetRegistryModule>("AssetRegistry" ); TArray<FName> PathsName; for (FString Path : Paths) { PathsName.Add (*Path); } FARFilter AssetFilter; AssetFilter.PackageNames = PathsName; TArray<FAssetData> AssetDatas; AssetRegistryModule.Get ().GetAssets (AssetFilter, AssetDatas); ContentBrowserModule.Get ().SyncBrowserToAssets (AssetDatas); } void UCppLib::SetSelectedFolders (TArray<FString> Paths) { FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked <FContentBrowserModule>("ContentBrowser" ); TArray<FString> SelectedFolders; ContentBrowserModule.Get ().SyncBrowserToFolders (Paths); }
如何用Python显示缓慢的任务进度
执行循环的过程的同时显示进度:
python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import unrealimport randomdef executeSlowTask (): quantity_steps_in_slow_task = 10000 with unreal.ScopedSlowTask(quantity_steps_in_slow_task, 'My Slow Task Text ...' ) as slow_task: slow_task.make_dialog(True ) for x in range (quantity_steps_in_slow_task): if slow_task.should_cancel(): break slow_task.enter_progress_frame(1 , 'My Slow Task Text ...' + str (x) + '/' + str (quantity_steps_in_slow_task)) deferredSpawnActor() def deferredSpawnActor (): world = unreal.EditorLevelLibrary.get_editor_world() actor_class = unreal.EditorAssetLibrary.load_blueprint_class('/Game/MyBluePrint/BPActor' ) actor_location = unreal.Vector(random.uniform(0.0 , 2000.0 ), random.uniform(0.0 , 2000.0 ), 0.0 ) actor_rotation = unreal.Rotator(random.uniform(0.0 , 360.0 ), random.uniform(0.0 , 360.0 ), random.uniform(0.0 , 360.0 )) actor_scale = unreal.Vector(random.uniform(0.1 , 2.0 ), random.uniform(0.1 , 2.0 ), random.uniform(0.1 , 2.0 )) actor_transform = unreal.Transform(actor_location, actor_rotation, actor_scale) actor = unreal.CppLib.get_actor(world, actor_class, actor_transform) unreal.CppLib.set_actor(actor,actor_transform)
CppLib.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "CppLib.generated.h" UCLASS () class CPLUSPLUSTEST_API UCppLib : public UBlueprintFunctionLibrary { GENERATED_BODY () public : UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void SetFolderColor (FString FolderPath, FLinearColor Color) ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void CloseEditorForAssets (TArray<UObject*> Assets) ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static TArray<UObject*> GetAssetsOpenedInEditor () ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static TArray<FString> GetSelectedAssets () ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static TArray<FString> GetSelectedFolders () ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void SetSelectedAssets (TArray<FString> Paths) ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void SetSelectedFolders (TArray<FString> Paths) ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static AActor* GetActor (const UObject* WorldContextObject,TSubclassOf< AActor > ActorClass,const FTransform& SpawnTransform) ; UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void setActor (class AActor* Actor,const FTransform& SpawnTransform) ; };
CppLib.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #include "CppLib.h" #include "Editor.h" #include "Editor/UnrealEd/Public/Toolkits/AssetEditorManager.h" #include "Editor/ContentBrowser/Public/ContentBrowserModule.h" #include "Editor/ContentBrowser/Private/ScontentBrowser.h" #include "Runtime/AssetRegistry/Public/AssetRegistryModule.h" #include "Runtime/Engine/Classes/Kismet/GameplayStatics.h" void UCppLib::SetFolderColor (FString FolderPath, FLinearColor Color) { GConfig->SetString (TEXT ("PathColor" ), *FolderPath, *Color.ToString (), GEditorPerProjectIni); } void UCppLib::CloseEditorForAssets (TArray<UObject*> Assets) { UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem <UAssetEditorSubsystem>(); for (UObject* Asset : Assets) { AssetEditorSubsystem->CloseAllEditorsForAsset (Asset); } } TArray<UObject*> UCppLib::GetAssetsOpenedInEditor () { UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem <UAssetEditorSubsystem>(); TArray<UObject*> EditedAssets = AssetEditorSubsystem->GetAllEditedAssets (); return EditedAssets; } TArray<FString> UCppLib::GetSelectedAssets () { FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked <FContentBrowserModule>("ContentBrowser" ); TArray<FAssetData> SelectedAssets; ContentBrowserModule.Get ().GetSelectedAssets (SelectedAssets); TArray<FString> Result; for (FAssetData& AssetData : SelectedAssets) { Result.Add (AssetData.PackageName.ToString ()); } return Result; } TArray<FString> UCppLib::GetSelectedFolders () { FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked <FContentBrowserModule>("ContentBrowser" ); TArray<FString> SelectedFolders; ContentBrowserModule.Get ().GetSelectedFolders (SelectedFolders); return SelectedFolders; } void UCppLib::SetSelectedAssets (TArray<FString> Paths) { FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked <FContentBrowserModule>("ContentBrowser" ); FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked <FAssetRegistryModule>("AssetRegistry" ); TArray<FName> PathsName; for (FString Path : Paths) { PathsName.Add (*Path); } FARFilter AssetFilter; AssetFilter.PackageNames = PathsName; TArray<FAssetData> AssetDatas; AssetRegistryModule.Get ().GetAssets (AssetFilter, AssetDatas); ContentBrowserModule.Get ().SyncBrowserToAssets (AssetDatas); } void UCppLib::SetSelectedFolders (TArray<FString> Paths) { FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked <FContentBrowserModule>("ContentBrowser" ); TArray<FString> SelectedFolders; ContentBrowserModule.Get ().SyncBrowserToFolders (Paths); } AActor* UCppLib::GetActor (const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, const FTransform& SpawnTransform) { return UGameplayStatics::BeginDeferredActorSpawnFromClass (WorldContextObject, ActorClass, SpawnTransform); } void UCppLib::setActor (AActor* Actor, const FTransform& SpawnTransform) { UGameplayStatics::FinishSpawningActor (Actor, SpawnTransform); }
打印类的所有属性
Cpplib.h
1 2 UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static TArray<FString>GetAllProperties (UClass* Class);
Cpplib.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 TArray<FString> UCppLib::GetAllProperties (UClass* Class) { TArray<FString> Ret; if (Class != nullptr ) { for (TFieldIterator<UProperty> It (Class); It; ++It) { UProperty* Property = *It; if (Property->HasAnyPropertyFlags (EPropertyFlags::CPF_Edit)) { Ret.Add (Property->GetName ()); } } } return Ret; }
python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import unrealdef getAllProperties (object_class ): return unreal.CppLib.get_all_properties(object_class) def printAllProperties (): obj = unreal.Actor() object_class = obj.get_class() for x in getAllProperties(object_class): y = x while len (y) < 42 : y = ' ' + y print (y + ':' + str (obj.get_editor_property(x)))
执行效果
一次性执行多个cmd命令
CppLib.h
1 2 UFUNCTION (BlueprintCallable, Category = "Unreal Python" ) static void ExecuteConsoleCommand (FString ConsoleCmd) ;
CppLib.cpp
需要#include “Editor.h”
1 2 3 4 5 6 7 8 9 void UCppLib::ExecuteConsoleCommand (FString ConsoleCmd) { if (GEditor) { UWorld* World = GEditor->GetEditorWorldContext ().World (); if (World) { GEditor->Exec (World, *ConsoleCmd, *GLog); } } }
python
1 2 3 4 5 6 7 8 9 10 11 import unrealimport randomdef executeConsoleCommand (): console_cmd = ['r.ScreenPercentage 0.1' , 'r.Color.Max 6' , 'stat fps' , 'stat unit' ] for x in console_cmd: unreal.CppLib.execute_console_command(x)
选择场景中的actor与清除选择
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import unrealdef get_selected_actors (): return unreal.EditorLevelLibrary.get_selected_level_actors() def get_selected_actors_EXAMPLE (): for x in get_selected_actors(): print (x) def select_actors (actors_to_select=[] ): unreal.EditorLevelLibrary.set_selected_level_actors(actors_to_select) def select_actors_EXAMPLE (): all_actors = unreal.EditorLevelLibrary.get_all_level_actors() actors_to_select = [] for x in range (len (all_actors)): actors_to_select.append(all_actors[x]) select_actors(actors_to_select) def clear_actor_selection_EXAMPLE (): select_actors()
生成默认资源(材质粒子关卡等)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 def create_generic_asset (asset_path='' , unique_name=True , asset_class=None , asset_factory=None ): if unique_name: asset_path, asset_name = unreal.AssetToolsHelpers.get_asset_tools().create_unique_asset_name( base_package_name=asset_path, suffix='' ) if not unreal.EditorAssetLibrary.does_asset_exist(asset_path=asset_path): path = asset_path.rsplit('/' , 1 )[0 ] name = asset_path.rsplit('/' , 1 )[1 ] return unreal.AssetToolsHelpers.get_asset_tools().create_asset( asset_name=name, package_path=path, asset_class=asset_class, factory=asset_factory ) return unreal.load_asset(asset_path) def create_generic_asset_EXAMPLE (): base_path = '/Game/GenericAssets/' generic_assets = [ [ base_path + 'sequence' , unreal.LevelSequence, unreal.LevelSequenceFactoryNew() ], [ base_path + 'material' , unreal.Material, unreal.MaterialFactoryNew() ], [ base_path + 'world' , unreal.World, unreal.WorldFactory() ], [ base_path + 'particle_system' , unreal.ParticleSystem, unreal.ParticleSystemFactoryNew() ], [ base_path + 'paper_flipbook' , unreal.PaperFlipbook, unreal.PaperFlipbookFactory() ] ] for asset in generic_assets: print (create_generic_asset(asset[0 ], True , asset[1 ], asset[2 ]))
在蓝图中使用python
在4.25版本之前的ue中需要用c++添加蓝图节点但是在新版本已经内置了能在蓝图中使用python的节点。但是无法在运行时可用的蓝图类(例如直接从 Actor 派生的类)中使用此方法。
B站搬运Udemy教程
链接:https://www.bilibili.com/video/BV1GY4y1s7qM?p=8&spm_id_from=pageDriver&vd_source=b1de3fe38e887eb40fc55a5485724480 https://www.bilibili.com/video/BV18q4y1X7DH?p=11&vd_source=b1de3fe38e887eb40fc55a5485724480 这两个教程链接内容是一样的,但是有的教程链接中的内容可能有点损坏,互补一下。
P10工厂和资产创造
什么是工厂:当我们需要创建一个资产的时候(蓝图,声音,关卡等),我们就需要去工厂去取(factory),因此我们需要先找到对应的工厂:LevelFactory ,BlueprintFactory 等。 以创建蓝图举例: 创建工厂对象相当于点击这些:
创建完工厂后(点击后)需要选择继承的类 初始设置完成后开始创造资产,创造资产需要使用unreal.AssetToolsHelpers.get_asset_tools() 利用这个工具中的create_asset方法创建。 创建完后用unreal.EditorAssetLibrary.save_loaded_asset进行保存 代码:
1 2 3 4 5 6 7 8 9 10 11 12 import unrealblueprintName = "python_create_BP" blueprintPath = "/Game/MyBluePrint" factory = unreal.BlueprintFactory() factory.set_editor_property("ParentClass" , unreal.Actor) assetTools = unreal.AssetToolsHelpers.get_asset_tools() myFancyNewAssetFile = assetTools.create_asset(blueprintName, blueprintPath, None , factory) unreal.EditorAssetLibrary.save_loaded_asset(myFancyNewAssetFile)
P11制作任务进度条:
1 2 3 4 5 6 7 8 9 10 11 12 13 import unrealtotalFrames = 1000000 textDisplay = "i love python, an i guess i'll be using this for a while!" with unreal.ScopedSlowTask(totalFrames, textDisplay) as ST: ST.make_dialog(True ) for i in range (totalFrames): if ST.should_cancel(): break unreal.log("one step!!!!" ) ST.enter_progress_frame(1 )
P12综合案例:批量创建继承自actor的蓝图资产(带进度条)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import unrealtotalRequiredBlueprints = 20 newAssetName = "BP_pythonMade_%d" createdAssetsPath = "/Game/TestStuff" slowTaskDisplayText = "Createing new assets....." factory = unreal.BlueprintFactory() factory.set_editor_property("ParentClass" , unreal.Actor) assetTools = unreal.AssetToolsHelpers.get_asset_tools() with unreal.ScopedSlowTask(totalRequiredBlueprints, slowTaskDisplayText) as ST: ST.make_dialog(True ) for x in range (totalRequiredBlueprints): if ST.should_cancel(): break newAsset = assetTools.create_asset(newAssetName % (x), createdAssetsPath, None , factory) unreal.EditorAssetLibrary.save_loaded_asset(newAsset) unreal.log("Just created an asset BP_PythonMade_%d via PYTHON API" % (x)) ST.enter_progress_frame(1 )
P13 根据选择的资产创建Actor(在关卡中的都叫actor)
教程上的获取选择的资产是这样: @unreal.uclass() class MyEditorUtility(unreal.GlobalEditorUtilityBase): pass
selectedAssets = MyEditorUtility().get_selected_assets() 但是官网教程有教可以通过这样获取选择的资产: selectedAssets = unreal.EditorUtilityLibrary.get_selected_assets()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import unrealactorsCount = 50 slowTaskDisplayText = "Spawning actors in the level....." selectedAssets = unreal.EditorUtilityLibrary.get_selected_assets() with unreal.ScopedSlowTask(actorsCount, slowTaskDisplayText) as ST: ST.make_dialog(True ) for x in range (actorsCount): if ST.should_cancel(): break unreal.EditorLevelLibrary.spawn_actor_from_object(selectedAssets[0 ], unreal.Vector(1.0 +x*100 , 1.0 +x*100 , 30.0 ), unreal.Rotator(0.0 , 0.0 , 10.0 *x)) unreal.log("Just added an actor to the level!" ) ST.enter_progress_frame(1 )
P15修改选择的资产或者actor的属性
例如如果我们要修改这些属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import unrealselectedAssets = unreal.EditorUtilityLibrary.get_selected_assets() for selectedAsset in selectedAssets: selectedAsset.set_editor_property("BlueprintDisplayName" , "Some BP" ) selectedAsset.set_editor_property("BlueprintCategory" , "Collectable" ) selectedAsset.set_editor_property("BlueprintDescription" , "This is a blueprint generated by Python or something" ) selectedActors = unreal.EditorLevelLibrary.get_selected_level_actors() for actor in selectedActors: actor.set_editor_property("canRotate" , True ) actor.set_editor_property("bHidden" , 1 ) actor.set_editor_property("SpriteScale" , 7.5 )
其中bHidden和SpriteScale对应的是ActorHiddenInGame和EditorBillboardScale 之所以使用的不是ue编辑器显示的界面名字是因为ue源码中设置的这些是编辑器中显示的名字,实际上源码中使用的变量名字并不是这些,并且bHidden在ue中显示的是名字是ActorHiddenInGame而且看起来是布尔变量,但是源码中实际上是int类型的名字叫bHidden。 那么如何在源码中查找真正的变量名字与类型呢? 例如这里设置actor的属性,我们就可以去Actor.h文件中搜索ue界面编辑器中显示的名字,然后就可以查找出来真正的名字与类型。
P16 python与蓝图的结合
把P12的功能做成一个带UI的工具: 其中滑块与文本之间可以通过函数进行绑定: 在UI编辑器中选择文本控件: 在text处进行绑定: 参考: python在蓝图节点中使用: 步骤:使用FormatText节点,节点中使用花括号框住的变量会自动产生引脚,为引脚传递数值配合python代码使用executePythonCommand节点执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import unrealactorsCount = int ({actorsCount}) rotationStep = int ({rotationStep}) positionOffset = {positionOffset} slowTaskDisplayText = "Spawning actors in the level...." selectedAssets = unreal.EditorUtilityLibrary.get_selected_assets() with unreal.ScopedSlowTask(actorsCount, slowTaskDisplayText) as ST: ST.make_dialog(True ) for i in range (actorsCount): if ST.should_cancel(): break unreal.EditorLevelLibrary.spawn_actor_from_object(selectedAssets[0 ],unreal.Vector(positionOffset * i, positionOffset * i, 30.0 ), unreal.Rotator(0.0 , 0.0 , rotationStep * i)) unreal.log("Just added an actor to the level!" ) ST.enter_progress_frame(1 )
P19 清理动画序列的修改
清理动画序列的这些:
1 2 3 4 5 6 7 8 import unrealselectedAssets = unreal.EditorUtilityLibrary.get_selected_assets() for asset in selectedAssets: asset.modify(True ) unreal.AnimationLibrary.remove_all_animation_notify_tracks(asset)
教程中使用的方式:
P20 清理未使用的资产
使用后将无法撤回,看看就好,并且如果资产过多会直接崩溃。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import unrealworkingPath = "/Game" allAssets = unreal.EditorAssetLibrary.list_assets (workingPath) processingAssetPath = "" allAssetsCount = len (allAssets) if (allAssetsCount > 0 ): with unreal.ScopedSlowTask (allAssetsCount, processingAssetPath) as ST: ST.make_dialog (True) for asset in allAssets: processingAssetPath = asset deps = unreal.EditorAssetLibrary.find_package_referencers_for_asset (asset) # 寻找资产的依赖项 if (len (deps)<=0 ): unreal.log (">>>>>> Deleteing >>>>> %s" % asset) unreal.EditorAssetLibrary.delete_asset (asset) if ST.should_cancel (): break ST.enter_progress_frame (1 , processingAssetPath)
P21 批量创建材质实例
这里值得注意的是,使用get_path_name得到的是path.name格式的字符串,因此如果仅仅需要path则需要替换字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import unrealtotalRequiredInstances = 15 newAssetName = "" sourceAssetPath = "" createdAssetsPath = "" selectedAssets = unreal.EditorUtilityLibrary.get_selected_assets () factor = unreal.MaterialInstanceConstantFactoryNew () assetTools = unreal.AssetToolsHelpers.get_asset_tools () for selectedAsset in selectedAssets: newAssetName = selectedAsset.get_name () + "_%s_%d" sourceAssetPath = selectedAsset.get_path_name () # /Game/MyTools/NewMaterial.NewMaterial createdAssetsPath = sourceAssetPath.replace (selectedAsset.get_name (), "-" ) # /Game/MyTools/-.- createdAssetsPath = createdAssetsPath.replace ("-.-" , "" ) # /Game/MyTools/ for x in range (totalRequiredInstances): newAsset = assetTools.create_asset (newAssetName % ("inst" , x + 1 ), createdAssetsPath, None, factor) unreal.MaterialEditingLibrary.set_material_instance_parent (newAsset, selectedAsset)