功能介绍
从Unreal Engine 4.23版本开始,新增了WebRemoteControl模块,在引擎中开启一个网络服务,通过类REST API发送http请求将指令发送到引擎中,从而实现远程控制。
这一模块可以用来制作第三方平台,比如GM工具,实时展示游戏参数和调试运行中的游戏;或者虚拟制片,如官方演示视频中展示的界面一样,将常用功能集成到网页或者APP面板中方便调用。
主要有两种调用方式:
- 获取/设置object的公开到蓝图和python的属性
- 调用公开到蓝图和python的函数
后面的功能演示使用了Unreal Engine 4.24版本,其他版本可能有差异。
功能开启
首先点击菜单Edit>Plugins打开插件窗口,启用Remote Control API插件。
启用后如果提示需要重启则重启引擎。
第二步,打开测试项目后,点击菜单Window>Developer Tool>Output Log打开输出日志窗口,确认左下角为CMD模式(还有一个python模式可以用来执行python命令),输入控制台命令WebControl.StartServer
,执行成功结果如下。
然后即可发送请求命令进行远程控制了。
需要注意的是网络服务器监听本地8080端口,需确保此端口不被防火墙拦截,控制端可以访问该端口。
另外要注意的一点是在4.24版本中,默认开启本地回环地址(127.0.0.1)和网络地址(本地连接ip)的8080端口,而在4.25版本中,默认只开启本地回环地址(127.0.0.1)的8080端口,会导致只能在本地调用而从其他电脑上无法远程调用网络服务。
解决的办法是修改一个配置文件,路径是Engine\Config\BaseEngine.ini
,在这个文件中加上下面的配置信息,IP改为本地连接的IP就可以在其他电脑上远程调用了。
[HTTPServer.Listeners] DefaultBindAddress=IP
不过这样写会导致本地回环地址反而不能使用该服务了,emmm。
除了开启网络服务的WebControl.StartServer
控制台命令,还有其他两条相关命令,分别是关闭网络服务WebControl.StopServer
和网络服务自动启动WebControl.EnableServerOnStartup
(保存到项目配置中,每次打开该项目时自动打开网络服务)。
API调用方式
使用专门用于测试API请求和响应的工具,例如Postman 或Insomnia ,我比较习惯使用Postman,这里就不详细说Postman的用法了,自行搜索即可。
获取Object的属性
上图所示为获取当前关卡中环境球(世界大纲视图中名为SkySphere的Actor)上的旋转(RelativeRotation)属性值。
需要注意的是,请求方式必须为PUT
,正文格式必须为JSON
(默认为Text),目标地址为http://ip:8080/remote/object/property
,发送结果为200 OK
并且得到返回的结果说明执行成功。
参数解释
请求正文中的三个参数分别是objectPath
(关卡中的物体)、access
(读写权限)、propertyName
(属性名)。
其中objectPath为虚幻引擎对加载到内存中的每个资源和Actor的唯一标识路径,遵循以下格式:
/path/PackageName.ObjectName:SubObjectName.SubObject
资产(uasset)的ObjectPath可以通过在内容浏览器窗口(Content Browser
)中右键该资产菜单中Copy Referrence
,即可获得。
对于关卡中的物体(Uobject),可以通过以下两种方法获取。
第一种是通过修改该物体,然后查看撤销历史窗口(Edit>Undo History)获得该路径,需要打开右下角的显示事务细节(Show transactions details)。
鼠标悬停在已修改对象和属性(Modified objects and propertyies)列的条目上,提示文本即为对应的ObjectPath。
这种方法既可以获取到关卡中的物体(Uobject)路径,也可以获取到资产(uasset)路径。
第二种方法是通过调用蓝图函数获得当前关卡中所有Actor的路径,后面再详细介绍。
第二个参数access有三种类型,READ_ACCESS
为读取物体上的属性,WRITE_ACCESS
和WRITE_TRANSACTION_ACCESS
为修改物体上的属性,两者的区别是WRITE_TRANSACTION_ACCESS
会在项目的事物历史中记录属性值的修改。
可以理解为WRITE_ACCESS
方法虽然修改了参数,但在不会立即影响当前关卡,而WRITE_TRANSACTION_ACCESS
可以立即看到参数修改的结果,与在细节面板(Details)中修改参数相同,会调用更多链接到该属性的更改前和更改后相关代码。
因此在实际使用中,更多使用WRITE_TRANSACTION_ACCESS
类型。
第三个参数为propertyName
(属性名),即想要读取或修改的属性名,当第二个参数access为READ_ACCESS
时,可以省略属性名参数,这样返回的结果是当前物体的所有属性列表。
当不知道要读取的属性的代码时,可以使用此方法列出全部属性查看。
当第二个参数access为WRITE_TRANSACTION_ACCESS
时,除了写上第三个参数propertyName
(属性名),还要加上第四个参数propertyValue
(属性值)。
如下图示为修改环境球旋转值的参数。
可以看到propertyValue
(属性值)的写法需要与读取到的格式相同。
调用蓝图函数
上面说过通过调用蓝图函数列出当前场景中的所有Actor,下面就是具体使用方法。
可以看到,最大的区别是访问目标地址变成了http://ip:8080/remote/object/call
,其次参数也有不同。
第一个参数依然是objectPath
,只不过这里写的不是物体的路径,而是要调用的蓝图脚本的路径。
第二个参数为functionName
,为要调用的蓝图函数的名称。
该脚本路径如下:
可以看到脚本是C++的库文件,但右侧函数代码中有BlueprintCallable
,表明可以在蓝图中调用。
除了这两个参数外,还有第三个参数parameters
向函数中传入的参数以及第四个参数generateTransaction
生成事务。
生成事务参数类似于WRITE_TRANSACTION_ACCESS
,默认为false,当调用函数修改关卡中物体时,必须设置为true时才能实时看到修改效果。
例如以下代码,可以实时修改灯光物体的旋转属性。
{ "objectPath" :"/Game/ThirdPersonBP/Maps/ThirdPersonExampleMap.ThirdPersonExampleMap:PersistentLevel.LightSource_0.LightComponent0", "functionName":"SetRelativeRotation", "parameters":{ "NewRotation":{ "Pitch":90, "Yaw":0, "Roll":0 } }, "generateTransaction":true }
同样可以在组件的库文件中可以找到该函数。
注意parameters
里面传入的参数名。
上面说过,该方法是基于向蓝图公开的函数的,因此当不知道想调用的函数的名称和参数时,可以先找到对应的物体的库文件,在库文件中搜索BlueprintCallable进行排查,就可以找到可用的函数以及传入的参数。
编辑状态与运行状态
在UE中,编辑状态与运行状态是不同的,上面的GetAllLevelActors函数,只能获取到编辑状态下所有的物体,如果在运行状态执行则返回结果为空。
同理在修改物体属性时,也要根据当前UE状态获取目标物体的相应ObjectPath。
例如同样一个HDRIBackdrop物体,在两种状态下的ObjectPath如下:
#编辑状态 /Game/VprodProject/Maps/Main.Main:PersistentLevel.HDRIBackdrop #运行状态 /Game/VprodProject/Maps/UEDPIE_0_Main.Main:PersistentLevel.HDRIBackdrop
那么在运行状态下如何获取到需要的Actor呢?
可以通过GetActorOfClass函数,获取到不同状态下的所选class的物体,而class种类可以通过GetObjectClass函数查看。
#查看物体的class { "objectPath" :"/Script/engine.Default__GameplayStatics", "functionName":"GetObjectClass", "parameters":{ "Object":"/Game/VprodProject/Maps/Main.Main:PersistentLevel.HDRIBackdrop" } } #根据物体class查找物体 { "objectPath" :"/Script/engine.Default__GameplayStatics", "functionName":"GetActorOfClass", "parameters":{ "WorldContextObject":"/Game/VprodProject/Maps/UEDPIE_0_Main.Main", "ActorClass":"/HDRIBackdrop/Blueprints/HDRIBackdrop.HDRIBackdrop_C" } }
常用API函数
下面是我整理的一些常用函数,对其进行封装并制作UI界面即可完成一个远程控制工具。
第一行为property(读取属性)或call(调用函数),后面为正文具体参数。
获取当前关卡编辑路径
库文件为Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorLevelLibrary.h
call { "objectPath" :"/Script/EditorScriptingUtilities.Default__EditorLevelLibrary", "functionName":"GetEditorWorld" }
获取当前关卡运行路径
库文件同上
call { "objectPath" :"/Script/EditorScriptingUtilities.Default__EditorLevelLibrary", "functionName":"GetGameWorld" }
获取所有Actor
库文件同上
call { "objectPath" :"/Script/EditorScriptingUtilities.Default__EditorLevelLibrary", "functionName":"GetAllLevelActors" }
获取当前Actor的component
库文件为Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h
call { "objectPath" :"/Game/SpringLandscape/Maps/Overview/Overview.Overview:PersistentLevel.SkySphere_3", "functionName":"K2_GetRootComponent" }
Actor操作
这部分也可以在Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h
中查找具体使用方法,略写如下。
call { #获取移动 "functionName""GetActorLocation" #设置移动 "functionName":"SetActorLocation","parameters" :{"NewPosition": {"X": 0,"Y": 0,"Z": 0}},"generateTransaction":true #获取旋转 "functionName":"GetActorRotation" #设置旋转 "functionName":"SetActorRotation","parameters" :{"NewRotation": {"Roll": 0,"Pitch": 0,"Yaw": 0}},"generateTransaction":true #获取缩放 "functionName":"GetActorScale3D" #设置缩放 "functionName":"SetActorScale3D","parameters" :{"NewScale3D": {"X": 1,"Y": 1,"Z": 1}},"generateTransaction":true #同时设置移动和旋转 "functionName":"SetActorLocationAndRotation" #获取和设置相对移动旋转缩放 "functionName":"SetActorRelativeLocation" "functionName":"SetActorRelativeRotation" "functionName":"SetActorRelativeScale3D" "functionName":"GetActorRelativeLocation" "functionName":"GetActorRelativeRotation" "functionName":"GetActorRelativeScale3D" #获取到某位置的距离 "functionName":"GetDistanceTo" #获取速度 "functionName":"GetVelocity" #隐藏 SetActorHiddenInGame "bNewHidden": false #碰撞 GetActorEnableCollision SetActorEnableCollision "bNewActorEnableCollision": false #销毁 DestroyActor }
获取组件材质
库文件为Engine/Source/Runtime/Engine/Classes/Components/StaticMeshComponent.h
call { "objectPath" :"/Game/SpringLandscape/Maps/Overview/Overview.Overview:PersistentLevel.SkySphere_3.StaticMeshComponent0", "functionName":"GetMaterial" }
设置组件材质
库文件同上
call { "objectPath" :"/Game/SpringLandscape/Maps/Overview/Overview2.Overview2:PersistentLevel.Sky_Sphere.StaticMeshComponent0", "functionName":"SetMaterial", "parameters":{ "ElementIndex":0, "Material":"/Game/SpringLandscape/Meshes/Background/Materials/MI_Background.MI_Background" }, "generateTransaction":true }
更改贴图
通过 :
可以访问到材质内部的贴图等节点
property { "objectPath" :"/Game/SpringLandscape/Meshes/Sky/Materials/test.test:MaterialExpressionTextureSample_0", "access":"WRITE_TRANSACTION_ACCESS", "propertyName":"Texture", "propertyValue": { "Texture":"/Game/StarterContent/Textures/T_Wood_Oak_D.T_Wood_Oak_D" } }
获取基础材质
库文件为Engine/Source/Runtime/Engine/Classes/Materials/MaterialInterface.h
call { "objectPath" :"/Game/SpringLandscape/Meshes/Sky/Materials/MI_Sky.MI_Sky", "functionName":"GetBaseMaterial" }
获取当前视角
库文件为Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorLevelLibrary.h
call { "objectPath" :"/Script/EditorScriptingUtilities.Default__EditorLevelLibrary", "functionName":"GetLevelViewportCameraInfo" }
设置当前视角
库文件同上
call { "objectPath" :"/Script/EditorScriptingUtilities.Default__EditorLevelLibrary", "functionName":"SetLevelViewportCameraInfo", "parameters":{ "CameraLocation": { "X": -186347, "Y": -338253, "Z": -18670 }, "CameraRotation": { "Pitch": -6.59973, "Yaw": 113.599, "Roll": 0 } }, "generateTransaction":true }
列出路径下的资产(uasset)
库文件为Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorAssetLibrary.h
call { "objectPath" :"/Script/EditorScriptingUtilities.Default__EditorAssetLibrary", "functionName":"ListAssets", "parameters":{ "DirectoryPath":"/Game/SpringLandscape/Meshes/Sky", "bRecursive":true, "bIncludeFolder":false } }
查询某个资产的详细信息
库文件同上
call { "objectPath" :"/Script/EditorScriptingUtilities.Default__EditorAssetLibrary", "functionName":"FindAssetData", "parameters":{ "AssetPath":"/Game/SpringLandscape/Meshes/Sky/SM_Sphere" } }
灯光组件操作
库文件为Engine/Source/Runtime/Engine/Classes/Components/LightComponent.h
call { #设置亮度 SetIntensity(float NewIntensity) #设置间接照明亮度 SetIndirectLightingIntensity(float NewIntensity) #设置灯光颜色 SetLightColor(FLinearColor NewLightColor, bool bSRGB = true) #设置色温 SetTemperature(float NewTemperature) #设置辉光 SetTransmission(bool bNewValue) #设置IES光域 SetIESTexture(UTextureLightProfile* NewValue); }
将texture资产导出为hdr
库文件为Engine/Source/Runtime/Engine/Classes/Kismet/KismetRenderingLibrary.h
call { "objectPath" :"/Script/engine.Default__KismetRenderingLibrary", "functionName":"ExportTexture2D", "parameters":{ "WorldContextObject":"/Game/SpringLandscape/Maps/Overview/Overview.Overview", "Texture":"/Game/SpringLandscape/Meshes/Sky/Textures/T_Sky_01_A.T_Sky_01_A", "FilePath":"C:/", "FileName":"test.hdr" } }
其他常用的库文件及包含功能如下,未完全测试:
#load、deleteLoaded、doesExist、rename、save、checkout、duplicate、delete、metadata Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorAssetLibrary.h #EditorPlaySimulate、select、spawnActor、NewLevel、loadLevel、saveLevel、replaceMeshComponentsMaterials Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorLevelLibrary.h #CurrentLanguage、CurrentLocale Engine/Source/Runtime/Engine/Classes/Kismet/KismetInternationalizationLibrary.h #ImportFileAsTexture2D Engine/Source/Runtime/Engine/Classes/Kismet/KismetRenderingLibrary.h #spawnObject、openLevel、SetGamePaused、isGamePaused、playSound2D、savegame、GetPlatformName、SetWorldOriginLocation Engine/Source/Runtime/Engine/Classes/Kismet/GameplayStatics.h #ExecuteConsoleCommand、GetConsoleVariableBoolValue、QuitGame、QuitEditor、SetIntPropertyByName、LaunchURL、SnapshotObject Engine/Source/Runtime/Engine/Classes/Kismet/KismetSystemLibrary.h
总结
上面只列举了比较常用的部分函数,可以看出WebRemoteControl模块的功能确实是极为强大的,经过开发者封装成带图形界面的工具后,能广泛应用在游戏调试和虚拟制片等领域。
绝对不要为你的一辈子做好计划,
因为人的变化在两三年内都是巨大的,
而且时刻会产生新的想法,
你真正可以做到的是,
想好现在要做什么。
《优秀的绵羊》
——威廉·德雷谢维奇
评论
465975 43513wonderful submit, very informative. 482318
485737 428991Im so happy to read this. This is the kind of manual that needs to be given and not the accidental misinformation thats at the other blogs. Appreciate your sharing this greatest doc. 791507