使用 Python 导入资产
导入贴图和音频
# coding: utf-8
import os
import unreal
# import AssetFunction_1 as af
# reload(af)
# af.importMyAssets()
asset_folder = 'D:/ue4/test/asset'
texture_jpg = os.path.join(asset_folder, 'dear.jpg').replace('\\','/')
sound_mp3 = os.path.join(asset_folder, 'easy.mp3').replace('\\','/')
def importMyAssets():
texture_task = bulidImportTask(texture_jpg, '/Game/MyAsset/Textures')
sound_task = bulidImportTask(sound_mp3, '/Game/MyAsset/Sounds')
executeImportTasks([texture_task, sound_task])
# ! 设置导入资产属性
def bulidImportTask(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 {}'.format(path)
导入fbx
# coding: utf-8
import os
import unreal
# import AssetFunction_2 as af
# reload(af)
# af.importMyAssets()
asset_folder = 'D:/ue4/test/asset'
static_mesh_fbx = os.path.join(asset_folder, 'static_fbx.fbx').replace('\\','/')
skeletal_mesh_fbx = os.path.join(asset_folder, 'skeletal_fbx.fbx').replace('\\','/')
def importMyAssets():
# ! 静态网格
static_mesh_task = bulidImportTask(static_mesh_fbx, '/Game/MyAsset/StaticMeshes', buildStaticMeshImportOptions())
# ! 带骨骼的网格
skeletal_mesh_task = bulidImportTask(skeletal_mesh_fbx, '/Game/MyAsset/SkeletalMeshes', buildSkeletalMeshImportOptions())
executeImportTasks([static_mesh_task, skeletal_mesh_task])
def bulidImportTask(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 {}'.format(path)
def buildStaticMeshImportOptions():
options = unreal.FbxImportUI()
# unreal.FbxImportUI
options.set_editor_property('import_mesh', True)
options.set_editor_property('import_textures', False)
options.set_editor_property('import_materials', True)
options.set_editor_property('import_as_skeletal', False) # Static Mesh
# unreal.FbxMeshImportData
options.static_mesh_import_data.set_editor_property('import_translation', unreal.Vector(50.0, 0.0, 0.0))
options.static_mesh_import_data.set_editor_property('import_rotation', unreal.Rotator(0.0, 110.0, 0.0))
options.static_mesh_import_data.set_editor_property('import_uniform_scale', 1.0)
# unreal.FbxStaticMeshImportData
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()
# 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) # Skeletal Mesh
# unreal.FbxMeshImportData
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)
# unreal.FbxSkeletalMeshImportData
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
创建、复制、删除、重命名资产和文件夹
# coding: utf-8
import os
import unreal
# import AssetFunction_3 as af
# reload(af)
# af.createDirectory()
# ! 创建文件夹 ~/MyNewDirectory
def createDirectory():
unreal.EditorAssetLibrary.make_directory('/Game/MyAsset/MyNewDirectory')
# ! 复制文件夹 ~/MyNewDirectory -> ~/MyNewDirectory_Duplicated
def duplicateDirectory():
return unreal.EditorAssetLibrary.duplicate_directory('/Game/MyAsset/MyNewDirectory', '/Game/MyAsset/MyNewDirectory_Duplicated')
# ! 删除文件夹 ~/MyNewDirectory
def deleteDirectory():
unreal.EditorAssetLibrary.delete_directory('/Game/MyAsset/MyNewDirectory')
# ! 重命名文件夹 ~/MyNewDirectory_Duplicated -> ~/MyNewDirectory_Renamed
def renameDirectory():
return unreal.EditorAssetLibrary.rename_directory('/Game/MyAsset/MyNewDirectory_Duplicated', '/Game/MyAsset/MyNewDirectory_Renamed')
# ! 复制资产 ~/dear -> ~/dear_Duplicated
def duplicateAsset():
return unreal.EditorAssetLibrary.duplicate_asset('/Game/MyAsset/Textures/dear', '/Game/MyAsset/Textures/dear_Duplicated')
# ! 删除资产 ~/dear
def deleteAsset():
unreal.EditorAssetLibrary.delete_asset('/Game/MyAsset/Textures/dear')
# ! 判断资产是否存在
def assetExist():
print unreal.EditorAssetLibrary.does_asset_exist('/Game/MyAsset/Textures/dear')
print unreal.EditorAssetLibrary.does_asset_exist('/Game/MyAsset/Textures/dear_Duplicated')
# ! 重命名资产 ~/dear_Duplicated -> ~/dear_Renamed
def renameAsset():
unreal.EditorAssetLibrary.rename_asset('/Game/MyAsset/Textures/dear_Duplicated', '/Game/MyAsset/Textures/dear_Renamed')
# ! 显示复制资产提示框 ~/dear_Renamed -> ~/dear_Duplicated
def duplicateAssetDialog(show_dialog=True):
if show_dialog:
unreal.AssetToolsHelpers.get_asset_tools().duplicate_asset_with_dialog('dear_Duplicated', '/Game/MyAsset/Textures', unreal.load_asset('/Game/MyAsset/Textures/dear_Renamed'))
else:
unreal.AssetToolsHelpers.get_asset_tools().duplicate_asset('dear_Duplicated', '/Game/MyAsset/Textures', unreal.load_asset('/Game/MyAsset/Textures/dear_Renamed'))
# ! 显示重命名提示框
# ! ~/dear_Renamed -> ~/dear_Renamed_2
# ! ~/dear_Duplicated -> ~/dear_Duplicated_Renamed
def renameAssetDialog(show_dialog=True):
first_renmae_data = unreal.AssetRenameData(unreal.load_asset('/Game/MyAsset/Textures/dear_Renamed'), '/Game/MyAsset/Textures', 'dear_Renamed_2')
second_rename_data = unreal.AssetRenameData(unreal.load_asset('/Game/MyAsset/Textures/dear_Duplicated'), '/Game/MyAsset/Textures', 'dear_Duplicated_Renamed')
if show_dialog:
unreal.AssetToolsHelpers.get_asset_tools().rename_assets_with_dialog([first_renmae_data, second_rename_data])
else:
unreal.AssetToolsHelpers.get_asset_tools().rename_assets([first_renmae_data, second_rename_data])
获取世界中的指定Actor
有三种方法筛选Actor:
- 获取选择的Actor:unreal.EditorLevelLibrary.get_selected_level_actors()
- 通过类型获取: unreal.GameplayStatics.get_all_actors_of_class() 通过 tag 获取:
- unreal.GameplayStatics.get_all_actors_of_class()
# coding: utf-8
import unreal
def getSelectedActors():
# ! Selected
selected_actors = unreal.EditorLevelLibrary.get_selected_level_actors()
return selected_actors
def getClassActors(actor_class):
# ! Class
world = unreal.EditorLevelLibrary.get_editor_world()
class_actors = unreal.GameplayStatics.get_all_actors_of_class(world, actor_class)
return class_actors
def getTagActors(actor_tag):
# ! Tag
world = unreal.EditorLevelLibrary.get_editor_world()
tag_actors = unreal.GameplayStatics.get_all_actors_with_tag(world, actor_tag)
return tag_actors
def getAllActors():
# ! All
world = unreal.EditorLevelLibrary.get_editor_world()
all_actors = unreal.GameplayStatics.get_all_actors_of_class(world, unreal.Actor)
return all_actors
def sortActors(use_selection = False, actor_class = None, actor_tag = None):
"""如果有指定,则筛选指定 Actors。否则返回全部 Actors
"""
# ! return all actors
if not use_selection and not actor_class and not actor_tag:
return getAllActors()
# ! get sort actors
selected_actors, class_actors, tag_actors = [], [], []
if use_selection:
selected_actors = list(getSelectedActors())
if actor_class:
class_actors = list(getClassActors(actor_class))
if actor_tag:
tag_actors = list(getTagActors(actor_tag))
final_actors = selected_actors + class_actors + tag_actors
for actor in final_actors:
if use_selection and actor in selected_actors:
pass
else:
final_actors.remove(actor)
continue
if actor_class and actor in class_actors:
pass
else:
final_actors.remove(actor)
continue
if actor_tag and actor in tag_actors:
pass
else:
final_actors.remove(actor)
continue
if final_actors:
return final_actors
else:
return getAllActors()
def cast(object_to_cast, object_class):
try:
return object_class.cast(object_to_cast)
except:
return getAllActors()
写这个的时候,发现获取出来的 Actors 存储都是用的 数组 array,虽然方法有些和列表 List 相同,但是使用起来效果不一样,最终打印结果数组显示和数组内元素显示有差异。
使用 Qt 进行界面开发
主函数 QtFunctions
# coding: utf-8
import unreal
import sys
sys.path.append('C:/Python27/Lib/site-packages')
from PySide import QtGui
def __QtAppTick__(delta_seconds):
for window in opened_windows:
window.eventTick(delta_seconds)
def __QtAppQuit__():
unreal.unregister_slate_post_tick_callback(tick_handle)
def __QtWindowClosed__(window=None):
if window in opened_windows:
opened_windows.remove(window)
unreal_app = QtGui.QApplication.instance()
if not unreal_app:
unreal_app = QtGui.QApplication(sys.argv)
tick_handle = unreal.register_slate_post_tick_callback(__QtAppTick__)
unreal_app.aboutToQuit.connect(__QtAppQuit__)
existing_windows = {}
opened_windows = []
def spawnQtWindow(desired_window_class=None):
window = existing_windows.get(desired_window_class, None)
if not window:
window = desired_window_class()
existing_windows[desired_window_class] = window
window.aboutToClose = __QtWindowClosed__
if window not in opened_windows:
opened_windows.append(window)
window.show()
window.activateWindow()
实现位移函数 QtWindowOne
# coding: utf-8
import unreal
import os
import sys
sys.path.append('C:/Python27/Lib/site-packages')
from PySide.QtGui import *
from PySide import QtUiTools
WINDOW_NAME = 'Qt Window One'
UI_FILE_FULLNAME = os.path.join(os.path.dirname(__file__), 'ui', 'window_move.ui').replace('\\','/')
class QtWindowOne(QWidget):
def __init__(self, parent=None):
super(QtWindowOne, self).__init__(parent)
self.aboutToClose = None
self.widget = QtUiTools.QUiLoader().load(UI_FILE_FULLNAME)
self.widget.setParent(self)
self.setWindowTitle(WINDOW_NAME)
self.setGeometry(100, 100, self.widget.width(),self.widget.height())
self.initialiseWidget()
def clossEvent(self, event):
if self.aboutToClose:
self.aboutToClose(self)
event.accept()
def eventTick(self, delta_seconds):
self.myTick(delta_seconds)
def initialiseWidget(self):
self.time_while_this_window_is_open = 0.0
self.random_actor = None
self.random_actor_is_going_up = True
self.widget.pushButton.clicked.connect(self.moveRandomActorInScene)
def moveRandomActorInScene(self):
import random
import WorldFunctions_2
all_actors = WorldFunctions_2.sortActors(use_selection=False, actor_class=unreal.StaticMeshActor, actor_tag=None)
rand = random.randrange(0, len(all_actors))
self.random_actor = all_actors[rand]
def myTick(self, delta_seconds):
self.time_while_this_window_is_open += delta_seconds
self.widget.label.setText("{} Seconds".format(self.time_while_this_window_is_open))
if self.random_actor:
actor_location = self.random_actor.get_actor_location()
speed = 300.0 * delta_seconds
if self.random_actor_is_going_up:
if actor_location.z > 1000.0:
self.random_actor_is_going_up = False
else:
speed = -speed
if actor_location.z < 0.0:
self.random_actor_is_going_up = True
self.random_actor.add_actor_world_offset(unreal.Vector(0.0, 0.0, speed), False, False)
实现旋转函数(部分) QtWindowTwo
def myTick(self, delta_seconds):
self.time_while_this_window_is_open += delta_seconds
self.widget.label.setText("{} Seconds".format(self.time_while_this_window_is_open))
if self.random_actor:
speed = 90.0 * delta_seconds
self.random_actor.add_actor_world_rotation(unreal.Rotator(0.0, 0.0, speed), False, False)
实现缩放函数(部分) QtWindowThree
def myTick(self, delta_seconds):
self.time_while_this_window_is_open += delta_seconds
self.widget.label.setText("{} Seconds".format(self.time_while_this_window_is_open))
if self.random_actor:
actor_scale = self.random_actor.get_actor_scale3d()
speed = 3.0 * delta_seconds
if self.random_actor_is_going_up:
if actor_scale.z > 2.0:
self.random_actor_is_going_up = False
else:
speed = -speed
if actor_scale.z < 0.5:
self.random_actor_is_going_up = True
self.random_actor.set_actor_scale3d(unreal.Vector(actor_scale.x + speed, actor_scale.y + speed, actor_scale.z + speed))
在世界中选择和取消选择物体
获取选择物体:unreal.EditorLevelLibrary.get_selected_level_actors()
设置选择物体:unreal.EditorLevelLibrary.set_selected_level_actors(actors_to_select)
WorldFunctions_3.py文件
# coding: utf-8
import unreal
# return: obj List unreal.Actor : The selected actors in the world
def getSelectedActors():
return unreal.EditorLevelLibrary.get_selected_level_actors()
# Note: Will always clear the selection before selecting.
# actors_to_select: obj List unreal.Actor : The actors to select.
def selectActors(actors_to_select=[]):
unreal.EditorLevelLibrary.set_selected_level_actors(actors_to_select)
def selectActors_EXAMPLE():
import WorldFunctions_2
all_actors = WorldFunctions_2.sortActors()
actors_to_select = []
for x in range(len(all_actors)):
if x % 2:
actors_to_select.append(all_actors[x])
selectActors(actors_to_select)
def clearActorSelection_EXAMPLE():
selectActors()
在视口中聚焦物体
# coding: utf-8
import unreal
import random
# active_viewport_only: bool : If True, will only affect the active viewport
# actor: obj unreal.Actor : The actor you want to snap to
def focusViewportOnActor(active_viewport_only=True, actor=None):
# ! focus command
command = 'CAMERA ALIGN'
if active_viewport_only:
command += ' ACTIVEVIEWPORTONLY'
if actor:
command += ' NAME=' + actor.get_name()
unreal.CppLib.execute_console_command(command)
def focusAllViewportsOnSelectedActors_EXAMPLE():
focusViewportOnActor(False)
def focusActiveViewportOnRandomActor_EXAMPLE():
actors_in_world = unreal.GameplayStatics.get_all_actors_of_class(unreal.EditorLevelLibrary.get_editor_world(), unreal.Actor)
random_actor_in_world = actors_in_world[random.randrange(len(actors_in_world))]
focusViewportOnActor(True, random_actor_in_world)
创建 generic 资产
# coding: utf-8
import unreal
def createGenericAsset(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 createGenericAsset_EXAMPLE():
base_path = '/Game/MyAsset/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()],
[base_path + 'data_table', unreal.DataTable, unreal.DataTableFactory()], # Will not work
]
for asset in generic_assets:
print createGenericAsset(asset[0], True, asset[1], asset[2])
利用 Blueprint 运行Python代码
原理和利用 Blueprint 运行 cmd 代码相同。
build.cs 加上 “Python”, “PythonScriptPlugin”
C++ .h(部分)
UFUNCTION(BlueprintCallable, Category = "Unreal Python")
static void ExecutePythonScript(FString PythonScript);
C++ .cpp(部分)
#include "../Plugins/Experimental/PythonScriptPlugin/Source/PythonScriptPlugin/Private/PythonScriptPlugin.h"
void UCppLib::ExecutePythonScript(FString PythonScript) {
FPythonScriptPlugin::Get()->ExecPythonCommand(*PythonScript);
}