-
Notifications
You must be signed in to change notification settings - Fork 18
Unreal Engine 4.26 and up
Unreal is a bit of a different beast. It does support Python for editor related tasks, but seems to be starving any threads pretty quickly. That's why it's pretty much impossible to run the SkyHook server in Unreal like we're able to do so in other programs. However, as explained in the main outline, SkyHook clients don't have to necessarily connect to SkyHook servers. That's why we can use skyhook.client.UnrealClient
with Unreal Engine's built-in Remote Control API.
Make sure the Remote Control API is loaded from Edit > Plugins
Loading a SkyHook module in Unreal is done by just importing it like normal. Assuming the code for the SkyHook module is in a file called skyhook_commands
in the Python folder (/Game/Content/Python
), you can just do:
import skyhook_commands
If you want to make sure it's always available when you start the engine, add an init_unreal.py
file in the Python folder and import the module from there. I've had problems with having a __pycache__
folder on engine start up, so I just deleted it from that some init_unreal.py
file:
# making sure skyhook_commands are loaded and ready to go
import skyhook_commands
# adding this here because it's a pain in the ass to retype every time
from importlib import reload
# leaving a pycache folder lying around seems to crash the editor on exit in 4.26.1,
# so let's delete it
pycache_folder = os.path.join(os.path.dirname(__file__), "__pycache__")
if os.path.isdir(pycache_folder):
shutil.rmtree(pycache_folder)
Unreal has specific requirements to run Python code. So you have to keep that in mind when adding functionality to the SkyHook module. In order for it to be "seen" in Unreal you need to decorate your class and functions with specific Unreal decorators.
Decorating the class with unreal.uclass()
:
import unreal
@unreal.uclass()
class SkyHookCommands(unreal.BlueprintFunctionLibrary):
def __init__(self):
super(SkyHookCommands, self).__init__()
If you want your function to accept parameters, you need to add them in the decorator like this
import unreal
import os
@unreal.ufunction(params=[str, str], static=true)
def rename_asset(self, asset_path, new_name):
dirname = os.path.dirname(asset_path)
new_name = dirname + "/" + new_name
unreal.EditorAssetLibrary.rename_asset(asset_path, new_name)
unreal.log_warning("Renamed to %s" % new_name)
NOTE
You can not use Python's list
in the decorator for the Unreal functions. Use unreal.Array(type)
, eg: unreal.Array(float)
.
NOTE
You can not use Python's dict
in the decorator for the Unreal functions. Use unreal.Map(key type, value type)
, eg: `unreal.Map(str, int)
Whenever you want to return anything from Unreal back to your SkyHook client, add the ret
keyword in the function parameters:
@unreal.ufunction(params=[str], ret=bool, static=True)
def does_asset_exist(asset_path=""):
"""
Return True or False whether or not asset exists at given path
:return: *bool*
"""
return_value = unreal.EditorAssetLibrary.does_asset_exist(asset_path)
return return_value
Let's say you want to return a dictionary from Unreal that looks like this:
my_dict = {
"player_name": "Nisse",
"player_health": 98.5,
"player_lives": 3,
"is_player_alive": True
}
Unfortunately, due to how Unreal works, you can't specify your return value as a dictionary or list with mixed values. This can be quite problematic because sometimes that's exactly what you want. To solve this you can just stringify your return value and it will be decoded into a Python object on the client side.
So in the example above, you'd do something like
@unreal.ufunction(params=[str], ret=str, static=True)
def my_cool_function(player_name=""):
my_dict = {
"player_name": "Nisse",
"player_health": 98.5,
"player_lives": 3,
"is_player_alive": True
}
return str(my_dict)
Since "this will just work", you can basically always just return a string like that.