使用Python脚本化运行编辑器

本页面介绍如何开始在虚幻编辑器中使用Python。

为何使用Python?

近年来,Python事实上已成为用于制作流程和3D应用程序之间的互操作性的首选语言,在媒体和娱乐行业中尤其流行。这部分要归功于它能够支持各种各样的应用程序。制作流程的复杂程度持续急剧上升,涉及到的应用程序的数量也在不断增多,拥有通用的脚本语言可以简化创建和维护大型资源管理系统的过程。

抛开这些外部原因,或与其他应用程序一起使用的需求,如果你希望在虚幻编辑器中自动化工作流程,Python也是绝佳的选择。对于刚接触编程的人员来说,它相对比较容易入门,通过PySide等模块,它可以提供创建复杂而功能全面的用户界面的能力,而且它还向社区提供很多其他非常有用的免费模块,有助于降低你的工作难度。

通过在虚幻编辑器中使用Python,你可以:

  • 构建可将虚幻编辑器与你在贵组织中使用的其他3D应用程序连接在一起的大型资源管理流程或工作流程。

  • 在虚幻编辑器中使耗时的资源管理任务实现自动化,例如,为静态网格体生成LOD。

  • 以程序化的方式将内容放置在关卡中。

  • 从你自己在Python中创建的UI控制虚幻编辑器。

将项目设置为使用Python

虚幻编辑器中的Python支持由Python编辑器脚本插件(Python Editor Script Plugin)提供。需要先为当前项目启用该插件,然后才能在编辑器中运行Python脚本。

当前,必须为每个项目分别启用该插件。

要启用该插件:

  1. 打开项目,然后从主菜单中选择 编辑(Edit) > 插件(Plugins)

  2. 插件(Plugins) 窗口中,转至 脚本(Scripting) 部分。
    在右侧面板中找到 Python编辑器脚本插件(Python Editor Script Plugin),然后选中其 启用(Enabled) 框。
    Enable the Python Editor Script Plugin
    也需要启用 编辑器脚本实用程序(Editor Scripting Utilities) 插件,它为许多常用的编辑器任务提供简化的API。有关详细信息,请参阅脚本化和自动化编辑器

  3. 重启编辑器。

Python 2.7

Python编辑器脚本插件(Python Editor Script Plugin)包含Python 2.7的嵌入式版本。

这意味着无需在计算机上单独安装Python。

默认情况下虚幻编辑器使用Python 2.7,因为它是当前VFX参考平台 的重要组成部分。如果参考平台转向3.x版本,我们也会随之改变。
在此期间,如果需要使用不同版本的Python(包括3.x版本),可以将UE_PYTHON_DIR环境变量设置为指向要嵌入的安装程序,然后从源重建虚幻引擎。

在虚幻编辑器中运行Python代码的不同方式

可以使用多种不同的方式在虚幻编辑器中运行Python脚本,每一种方式都针对略微不同的使用场景而设计。可以选择任何一种可满足你的需求的方式。

不同于蓝图,Python环境仅 在虚幻编辑器 中可用,当项目在虚幻引擎中以任何模式(包括“在编辑器中运行(Play In Editor)”、“独立游戏(Standalone Game)”、烘焙的可执行文件等)运行时不可用。这意味着可以在脚本化和自动化编辑器或构建资源制作流程时自由使用Python,但当前无法将其作为游戏性脚本语言使用。

“输出日志(Output Log)”中的Python控制台

可以将虚幻编辑器的控制台输入栏切换为接受Python代码而非虚幻控制台命令。

The Python console

如上图所示,可在 输出日志(Output Log) 面板中执行此操作,或者在按~键调出控制台输入栏时执行此操作。

当控制台处于Python模式时:

  • 可以在该控制台中输入多行Python代码,并让编辑器立即执行每一行代码,和在命令窗口中使用交互式Python控制台完全相同。这是逐行执行Python代码的唯一方式;以下列出的所有其他方法运行的都是指定的脚本文件。

  • 通过使用 Shift+Enter 来分隔每一行或者通过粘贴从文本编辑器复制的多行代码块可以一次运行多行代码。

  • 可以通过在控制台中输入文件名来执行Python脚本文件。如果你的Python脚本需要额外的命令行参数,请将它们添加在脚本名称后面。

内置的Python print函数的输出也将重定向到 输出日志(Output Log) 面板中。

py控制台命令

在标准控制台中,可以使用py命令来将行的剩余部分作为Python代码来运行,效果和在上述的Python控制台中输入完全相同。

例如,该命令可运行指定的脚本文件:

The py console command

启动编辑器时,我们建议不要使用ExecCmd命令行参数的值运行该命令。这可能会导致脚本在编辑器环境就绪前就运行,例如,在启动关卡完全加载完之前。请参阅下面的部分来了解更好的选项。

“文件(File)”菜单

虚幻编辑器主窗口中的 文件(File) 菜单为你提供了运行Python脚本文件的新选项。

  • 使用 执行Python脚本(Execute Python Script) 来浏览至计算机中你之前尚未运行过的新脚本文件。

  • 使用 最近使用的Python脚本(Recent Python scripts) 列表来再次运行你之前已运行过的脚本。将从磁盘重新读取文件,因此,如果在此期间你更改了脚本,将会运行新版本的脚本。

Execute Python from the File menu

命令行

如果从命令行或从脚本中启动虚幻编辑器,可以在命令行参数中指定Python脚本文件。如果你的Python脚本需要额外的命令行参数,请将它们添加在脚本名称后面。

可以通过两种不同的方式从命令行中运行Python脚本。在两种方法中,编辑器都会在运行完Python脚本后立即关闭。

选项1:完整编辑器

在该方法中,会启动完整虚幻编辑器,打开指定的项目,加载默认的启动关卡,然后在一切都加载就绪后立即运行Python脚本。需要让脚本与项目中或关卡中加载用时可能较长的内容交互时,该方法非常有用。

在命令行中添加ExecutePythonScript参数,并将其值设置为要运行的Python脚本。例如:

> UE4Editor-Cmd.exe MyProject.uproject -ExecutePythonScript="c:\my_script.py"

上述方法要求你为项目启用“编辑器脚本实用程序(Editor Scripting Utilities)”插件。

选项2:命令行开关

在该方法中,编辑器启动时环境最小,不包含UI或渲染。该方法执行起来非常快,但是加载脚本需要交互的关卡和其他种类资源时比较棘手。

在命令行中添加以下参数:-run=pythonscript -script=<script_file>

例如:

> UE4Editor-Cmd.exe -run=pythonscript -script="c:\\my_script.py"

init_unreal.py 文件

如果编辑器在任何已配置其使用的路径中检测到名称为init_unreal.py的脚本文件(请参阅下面的“虚幻编辑器中的Python路径”),编辑器会立即运行该脚本。

如果你参与了一个项目或插件的开发工作,且知道使用该内容的每个人都需要在每次编辑器启动时运行相同的初始化代码,该方法非常有效。可以将初始化代码放在具有该名称的脚本中,并将脚本放在该项目或插件的 Content/Python 文件夹中。

启动脚本

在“项目设置(Project Settings)”中,你可以指定任意数量的Python脚本以让其在每次你打开该项目时运行。编辑器会在默认启动关卡完全加载之后运行这些脚本。

选择 编辑(Edit) > 项目设置...(Project Settings...)。在 插件(Plugins) 列表下,选择 Python。然后,将脚本添加到 启动脚本(Startup scripts) 设置中:

Python startup scripts

完成后请重启虚幻编辑器。下一次编辑器加载项目时,会运行新的启动脚本。

虚幻编辑器中的Python路径

通过上述方法使用相对路径运行Python脚本时,或在脚本中使用import命令导入另一个脚本模块时,运行或导入的脚本可以位于Python环境的sys.path变量中列出的任意路径中。

虚幻编辑器会自动向该sys.path列表中添加几个路径:

  • 项目文件夹下的 Content/Python 子文件夹。

  • 主虚幻引擎安装文件夹中的 Content/Python 子文件夹。

  • 每个启用的插件的文件夹下的 Content/Python 子文件夹。

  • 用户目录中的 Documents/UnrealEngine/Python 文件夹。例如,在Windows 10中,该路径是C:/Users/Username/Documents/UnrealEngine/Python

也可以使用下述的任何一种方法将自己的路径添加到该列表中:

  • 在“项目设置(Project Settings)中,选择 编辑(Edit) > 项目设置...(Project Settings...)。在 插件(Plugins) 列表下,选择 Python。然后,将路径添加到 其他路径(Additional Paths) 设置中。完成后请重启虚幻编辑器。
    Additional Python paths

  • 将路径添加到PYTHONPATH环境变量的值中,然后重启虚幻编辑器。

  • 在Python脚本中或在Python控制台中直接将路径添加到sys.path列表中。

关于虚幻编辑器Python API

Python编辑器脚本插件(Python Editor Script Plugin)公开了各种各样的类和函数,你可以使用它们与虚幻编辑器、项目中的资源和关卡中的内容交互。该API包含在unreal模块中。要访问它,请在你在编辑器的Python环境中运行的任何Python脚本的开始位置导入该模块:

import unreal

unreal模块几乎会公开在编辑器环境中从C++公开给蓝图的一切。它不是预生成的,它可以自动反映在编辑器的蓝图中可用的一切。在虚幻编辑器中启用新的插件时,被插件公开给蓝图的一切在Python中也会变得可用。任何在项目中编写并公开给蓝图的C++代码也是如此。

Python API力求以对Python开发者尽可能友好的方式公开本地虚幻API。例如:

  • 必要时简单数据类型可以透明地在Python和本机类型之间来回转换。
    传入Python列表、集或字典时,会自动将它转换为虚幻数组、集或贴图。检索API函数返回的列表、集或字典时,实际上是在获取虚幻类的实例,但其API与基础Python列表、集或字典类型完全一致。

  • Python类保留与它们表示的本地类型相同的继承层级。例如,这意味着可以使用内置的Python函数isinstance()type()来测试一个对象是否派生自某给定的类或与某给定的类匹配。

  • API尝试平衡在虚幻中用于C++及蓝图的命名规范和Python命名规范。Python API中的类和对象具有和在蓝图中相同的名称。这通常和其本机类型相同,省略了前缀(例如,UT)。函数和属性名称自动以小写snake_case格式公开。例如,你通常会调用如unreal.StaticMeshActor.get_actor_transform()之类的函数。列举值名称自动以大写SNAKE_CASE格式公开。

  • 所有公开的函数都可以接受有序参数和采用任意顺序的命名参数。例如,以下两个函数调用完全等效:

    unreal.EditorLevelLibrary.join_static_mesh_actors(list_of_actors, my_options)
    unreal.EditorLevelLibrary.join_static_mesh_actors(join_options=my_options, actors_to_join=list_of_actors)

API参考

有关虚幻Python API公开的所有类和函数的详细信息,请参阅位于此处的API参考:

虚幻编辑器Python API参考

API参考并未详尽列出可能由插件公开给Python的一切。如果安装了该API参考中未包含的其他插件,并且需要查看其脚本功能公开给Python的方式,可以生成你自己的包含所需插件的文档的本地版本的API参考。有关指导说明,请参阅虚幻引擎安装文件夹中 Engine\Plugins\Experimental\PythonScriptPlugin\SphinxDocs 下的自述文件。

使用Python API的最佳实践

本部分将介绍使用Python API时需要谨记的重要事项。

处理资源

如果需要在项目中处理资源,请始终使用虚幻Python API中的函数来进行。绝不要使用Python中内置的文件管理模块来直接处理磁盘上的资源文件。例如,如果需要将资源移动到不同的文件夹中,不要使用Python函数,如os.rename or shutil.move。如果不遵守此规则,虚幻项目和资源中包含的内部内容引用可能会失效。

我们建议你使用编辑器脚本实用程序(Editor Scripting Utilities)插件提供的unreal.EditorAssetLibrary API,或虚幻Python API中内置的unreal.AssetTools类。

更改编辑器属性

可以使用Python来访问项目中的对象并以编程方式针对这些对象设置许多配置属性。例如,Python脚本可以访问当前关卡中的静态网格体Actor,并设置Actor是否可以被破坏或它们是否应该在游戏中被隐藏等属性。或者,可以检索其静态网格体组件并针对这些组件设置属性,例如其Lightmass设置,甚至它们链接到的静态网格体资源。

可以通过两种不同的方式将这些属性公开给Python:

  • 具有BlueprintReadOnly或BlueprintReadWrite标志的项目将作为对象上的简单属性公开。
    可以读取和修改这些属性,就像访问任何Python对象属性一样。

  • 具有ViewAnywhere或EditAnywhere标志的项目将作为“编辑器属性”公开。
    可以使用每个对象公开的一对特殊函数来对这些值进行读写:set_editor_property()get_editor_property()

在每个类的API参考中,你可以在紧挨着类描述的位置找到 编辑器属性(Editor Properties) 列表。这些是可以使用set_editor_property()get_editor_property()函数设置和获取的所有值。每当需要设置或获取某个对象的配置属性时,请先查看该列表以了解所需属性是否已在其中列出。

  • 需要读取同时作为对象属性和编辑器属性公开的值时,直接访问该属性的结果通常与调用get_editor_property()函数取得的结果相同。但是,get_editor_property()函数经常可以访问未直接在Python对象上公开的属性。

  • 需要设置同时作为对象属性和编辑器属性公开的值时,大部分情况下应该使用set_editor_property()函数来设置该值,而非在对象上直接设置该值。在UI中调整属性时,编辑器通常会于编辑更改前和编辑更改后在后台执行额外操作。这些操作通常在某种程度上对你做出的选择作出反应,并且使编辑器UI与对象在游戏世界中的状态保持同步。如果直接在Python对象上修改这些属性,编辑器代码不会自动运行。另外,调用set_editor_property()来设置属性的状态时,也会触发该编辑前和编辑后代码,与在编辑器UI的 细节(Details) 面板中更改该设置时相同。

例如,媒体播放器对象具有 打开时播放(Play on Open) 设置:

Details

这会在unreal.MediaPlayer类中的play_on_open类成员中公开:

import unreal
obj = unreal.MediaPlayer()
# 直接修改属性可能产生
# 与在编辑器UI中更改设置不同的结果。
# 通常需要避免直接设置这些值,如下所示:
obj.play_on_open = True
# 通过这种方式访问属性将产生和
# 在编辑器UI中更改该设置完全相同的结果:
obj.set_editor_property("play_on_open", True)
# 两种读取值的方式具有相同的效果。
# 如果某个类通过两种方式公开某个属性,你可以使用其中任一方式:
play_value = obj.play_on_open
play_value = obj.get_editor_property("play_on_open")

尽可能使用虚幻类型

如果需要虚幻Python API提供的效用,如用于数学运算或操纵3D坐标的类,我们建议你使用虚幻实用程序而非使用你自己的实现。我们对虚幻版本进行了优化,以确保引擎环境的最佳性能。

例如,需要在3D空间中操纵坐标时,请使用unreal.Vector类:

import unreal
v1 = unreal.Vector()
v1.x = 10
v2 = unreal.Vector(10, 20, 30)
v3 = (v1 + v2) * 2
print v3

日志记录和反馈

unreal对象通过与所有引擎及编辑器子系统所使用的相同的信息传递系统公开你可以在代码中用于发送日志、警告和错误消息的函数。无论何时你需要让脚本向用户发送消息,我们都建议你使用该标准化的日志框架。

  • unreal.log()用于信息性消息。为了方便起见,我们也实现了Python print()函数以在内部通过unreal.log()传递。

  • 使用unreal.log_warning()来使用户注意到可能存在的问题。

  • unreal.log_error()用于记录阻碍脚本按照预期运行的严重问题。

你的消息将与其他子系统发送的消息一起显示在 输出日志(Output Log) 面板中。

Python log messages

支持撤销和重复

你的脚本可以充分利用虚幻编辑器中内置的撤销/重复系统。

你定义的每一个 事务 中都可以包含任意数量的Python操作。使用这些事务,你可以将大型操作或多个不同对象上的操作捆绑为撤销/重复历史记录中的单个条目。通常,如果你打算让脚本在多个对象上执行某特定更改,但是不希望每次更改都在撤销/重复历史记录中存在单独的条目,而是希望通过一个条目就可以撤销对所有对象所作的该更改。

要定义事务,请使用unreal.ScopedEditorTransaction范围。例如,如果你运行该代码:

import unreal
obj = unreal.MediaPlayer()
with unreal.ScopedEditorTransaction("My Transaction Test") as trans:
    obj.set_editor_property(“play_on_open”, True)
    obj.set_editor_property(“vertical_field_of_view”, 60)

编辑器的 撤销历史记录(Undo History) 面板立即会按名称列出该事务:

Undo History

作为一般规则,限定了范围的事务可以包含在编辑器UI中也无法撤销的任何操作。但是,并非每个编辑器操作都是不可撤销的。例如,你无法在编辑器UI中撤销模型导入,因此尝试导入unreal.ScopedEditorTransaction中的模型将无法按照期待的方式工作。

缓慢操作的进度对话

如果你的脚本需要在同一个操作中处理多个资源或Actor,可能需要较长的时间才能完成。但是,当虚幻编辑器运行Python脚本时,其UI处于被封锁状态中,不允许其他的用户交互。为了向用户提供大型任务的进度信息,从而避免使编辑器让用户产生冻结或挂起的错觉,可以使用unreal.ScopedSlowTask范围。

例如:

import unreal
total_frames = 100
text_label = "Working!"
with unreal.ScopedSlowTask(total_frames, text_label) as slow_task:
    slow_task.make_dialog(True)               # 如果对话不可见,使其可见
    for i in range(total_frames):
        if slow_task.should_cancel():# 如果用户已在UI中按了“取消(Cancel)”则为True
            break
        slow_task.enter_progress_frame(1)     # 使进度前进一帧。
                                            # 如果希望,也可以更新本调用中的对话文本。
        ...# 现在在此处执行针对当前帧的工作!

Progress bar for a Scoped Slow Task