Unreal Remote Control (TikTok Integration)

Introduction

In many of my previous blog posts, I discussed running Python scripts through the Unreal command line to achieve production automation. I also introduced how to build your own custom render farm and process render jobs remotely. Today, I want to focus on remote control, which is more closely tied to gameplay features and can enable remote or cross-application interactive experiences.

One of my initial inspirations for exploring this topic was TikTok’s live stream interactive gaming with audiences. While audience interaction with live streams has been around for a while on platforms like Twitch, it wasn’t until recently that I considered how this could be achieved.

tiktok

In the following post, I’ll create a prototype of a game running on a live stream. The game can be controlled by audience interactions, such as receiving gifts, gaining new followers, or simply monitoring the number of viewers currently watching the stream. (Of course, there are many more practical uses for remote control since it essentially opens up the Unreal application through a web interface that can be accessed and controlled by anything)

Remote Control Basics

Fortunately, Unreal has a system called Remote Control (previously known as Web Control) that makes this functionality possible. The Remote Control system works by running a web server inside the Unreal Engine that services WebSocket messages and HTTP requests made by remote web applications through a REST-like API. We can enable this feature simply by activating Unreal’s “RemoteControlAPI” plugin.

Blueprint Setup

Next, let’s set up a test blueprint. Here, I created one function and one event as examples, each with its own custom argument and type. This allows us to test the functionality of passing in custom values.

bpFunction

The idea behind this function is to set the current gameplay speed based on the number of viewers. The more viewers there are, the faster and harder the gameplay becomes.

bpEvent

This event acts as a one-time trigger, initiating a “burst fire” (with a variable passed in to enhance the effect). This could be linked to an on-stream event.

Remote Control Interface

rcInterface

You can easily test if these functionalities are working correctly by:

  1. Creating a Remote Control Unreal asset.
  2. Binding it to a blueprint actor and its event or function.
    • You can also take this time to configure the custom arguments.
  3. Running the game instance, opening the Remote Control interface, and clicking the “Call Function.”

You should see the desired result as if those events and functions were triggered.

Troubleshoot

If something isn’t working, check if the server has started. By default, the auto-start web server should be turned on.

rcplugin

You can also manually control the web server by accessing the game instance command console using the backtick key `

  • WebControl.StartServer
    • Starts the web server and begins listening for incoming requests on port 30010.
  • WebControl.StopServer
    • stops the web server, preventing any more requests to your Unreal Editor instance from being handled.

Testing

PIE Mode

When running the game in Editor, you need to add the prefix “UEDPIE_0_” before the level name when specifying it via a web request. This is not necessary if you’re using the Remote Control UI. source

Package build

For a packaged build, you need to run the executable with the “-RCWebControlEnable” flag. You can do this either through the command prompt or by creating a shortcut and adding “-RCWebControlEnable” afterward. source

shortcut

Http Request via Postman

Next, we will test using the super user-friendly Postman web service to send remote commands without relying on the built-in Unreal Remote Control interface.

I will not cover how to use Postman as there are many instructions out there, here I demonstrated read and write commands.

postmanSet

postmanGet

Http Request API via Python

The next step is to extend this further by using a Python script to replicate the HTTP requests. Fortunately, it’s quite easy and requires minimal setup.

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
import requests

url = r'http://localhost:30010/remote/object/call'


def sendBurst(multiplier=10):
d = {
"objectPath": "/Game/Main/Maps/UEDPIE_0_Test.Test:PersistentLevel.BP_ContinuousCannonTest_C_1",
"functionName": "Burst",
"parameters": {
"Multiplier": multiplier
},
"generateTransaction": True
}

r = requests.put(url, json=d)
return r.text


def updateSpeedByAudience(count):
d = {
"objectPath": "/Game/Main/Maps/UEDPIE_0_Test.Test:PersistentLevel.BP_ContinuousCannonTest_C_1",
"functionName": "Set Speed By Audience Count",
"parameters": {
"count": count + 1
},
"generateTransaction": True
}

r = requests.put(url, json=d)
return r.text


if __name__ == '__main__':
updateSpeedByAudience(3)

Here, we recreated the same functionality presented via Postman, but now we’re able to execute those commands via a Python script. This is useful if you have an external application that uses Python.

Next, let’s see how this works with TikTok’s Python API.

(Optional) TikTok Linking

For TikTok integration, you can use the following library: https://github.com/zerodytrash/TikTok-Live-Connector

You can input your TikTok ID using TikTokLiveClient(unique_id). Events on TikTok, such as user joining or commenting, will trigger events or functions in the Unreal instance.

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
from TikTokLive import TikTokLiveClient
from TikTokLive.events import CommentEvent, JoinEvent, RoomUserSeqEvent

# Create the client
client: TikTokLiveClient = TikTokLiveClient(unique_id="@xxx")


@client.on(RoomUserSeqEvent)
async def on_user_info(event: RoomUserSeqEvent):
print('something')
print(event.total_user)

async def on_comment(event: CommentEvent) -> None:
print('user commented {}'.format(event.content))
sendBurst(5)


async def on_join(event: JoinEvent):
print('welcome user {} joining'.format(event.user_id))
sendBurst(20)


client.add_listener(JoinEvent, on_join)
client.add_listener(CommentEvent, on_comment)


if __name__ == '__main__':
# Run the client and block the main thread
# await client.start() to run non-blocking
client.run()

References

Unreal Forum - Remote Control Quick Start

Unreal Forum - Remote Control API HTTP Reference