Startup Script Management

going beyond the init_unreal.py file

Covered in Organizing Startup Scripts, my init_unreal.py file usually looks something like this:

from recipe_book import startup
startup.run()

The startup module is where I organize everything I want to happen on Unreal startup, split into two main sections:

  1. Pre Startup - Python code that can is safe to run immediately the moment Python is loaded

    1. Examples might include:

      1. Initializing a custom Python logger

      2. Register metadata names

      3. Environment setup, such as fixing the sys.path if required as part of a pipeline

      4. Initialize Python classes other code might rely on

      5. Loading external config data or packages

  2. Post Startup - Python code to run after the Editor is fully loaded

    1. I consider this step ready once the Asset Registry has scanned the entire UE Project

    2. Examples might include:

      1. Initializing the Python-defined Editor Menus

      2. Opening Editor Utility Widget startup tools (such as a homepage tool listing all ShotGrid tasks)

      3. Running any tools that rely on the Asset Registry


Waiting for the Asset Registry

The main separator of my Pre and Post Startup logic is whether the Asset Registry has finished loading the Unreal Project.

Thankfully, there is an easy enough function to reference on the Asset Registry: is_loading_assets()

# the Asset Registry
asset_registry_helper = unreal.AssetRegistryHelpers()
asset_registry        = asset_registry_helper.get_asset_registry()

print(
    f"Is Asset Registry Ready? "
    f"{'no' if asset_registry.is_loading_assets() else 'yes'}"
)

We can use this with register_slate_post_tick_callback() to track the Asset Registry on startup to wait until it's ready:

asset_registry_helper = unreal.AssetRegistryHelpers()
asset_registry        = asset_registry_helper.get_asset_registry()

startup_id = None

def wait_for_asset_registry(ignore=None):
    """Wait until the Asset Registry is ready before continuing"""
    if asset_registry.is_loading_assets():
        print(f"Still waiting on the Asset Registry...")
        return
    
    print(f"Asset Registry is ready!")
    
    # We can now remove the callback and continue
    global startup_id 
    unreal.unregister_slate_post_tick_callback(startup_id)
    
    # do the thing or call the next function here
    pass


# start the callback if not already running
if not startup_id:
    startup_id = unreal.register_slate_post_tick_callback(wait_for_asset_registry)

And here's an example of running this during startup on a large project:

Note: The Asset Registry data does get cached on disk. Depending on PC specs, project size, and the last time the project was opened, the Asset Registry might be fully loaded by the time Python initializes


Full Startup Example

building off of the wait_for_asset_registry function, my startup module generally has the following in it:

  • A dedicated function that contains all Pre Startup commands

  • A dedicated function that contains all Post Startup commands

  • The Wait For Asset Registry callback function, which calls Post Startup when complete

  • A run() function that calls Pre Startup and sets up the Wait For Asset Registry callback

    • the run() function is the main entry point in this module, and is the only function that should be called externally

And here's what that code looks like:

asset_registry_helper = unreal.AssetRegistryHelpers()
asset_registry        = asset_registry_helper.get_asset_registry()

startup_id = None

def on_pre_startup():
    """Scripts that can run immediately on Editor Startup"""
    print("Running Pre Startup Scripts")
    # Import and run Pre Startup scripts here


def on_post_startup():
    """Scripts that should run after the Asset Registry is fully loaded"""
    print("Running Post Startup Scripts")
    # Import and run Post Startup scripts here


def wait_for_asset_registry(ignore=None):
    """Wait until the Asset Registry is ready before continuing"""
    if asset_registry.is_loading_assets():
        print(f"Still waiting on the Asset Registry...")
        return
    
    print(f"Asset Registry is ready!")
    
    # We can now remove the callback and continue
    global startup_id 
    unreal.unregister_slate_post_tick_callback(startup_id)
    
    # run Post Startup
    on_post_startup()


def run():
    """run Pre Startup and then schedule Post Startup"""
    
    # Pre Startup can run right away
    on_pre_startup()
    
    # Schedule Post Startup
    global startup_id
    if not startup_id:
        startup_id = unreal.register_slate_post_tick_callback(wait_for_asset_registry)
    else:
        unreal.log_warning("Unreal Startup has already been run!")

In this setup, the only thing that needs to be updated / maintained are the on_pre_startup() and on_post_startup() functions - we shouldn't need to ever touch run() or wait_for_asset_registry().

Last updated