功能介绍

从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请求和响应的工具,例如PostmanInsomnia ,我比较习惯使用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_ACCESSWRITE_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模块的功能确实是极为强大的,经过开发者封装成带图形界面的工具后,能广泛应用在游戏调试和虚拟制片等领域。

 


绝对不要为你的一辈子做好计划,

因为人的变化在两三年内都是巨大的,

而且时刻会产生新的想法,

你真正可以做到的是,

想好现在要做什么。

《优秀的绵羊》

——威廉·德雷谢维奇