One of the next-gen world building technologies we’re embracing in Unreal Engine 5 is the new One File Per Actor workflow. Instead of the old sublevel workflow that saved bundles of actors together in a single asset, it saves each actor in a level as its own asset, bringing improvements to the world building workflow:
- Reduce the potential for conflicts when multiple people are working on a single level level. No more waiting for someone to unlock
/Content/Levels/Map01/Sublevels/L_EnvProps
when you just need to tweak some crate placements. - Better visibility on what assets changed in a given commit.
- Better visibility on how a single asset in a level has changed over time.
However, one large drawback is in the implementation details of how it works - each actor is saved in a special Content/__ExternalActors__
folder with an opaque GUID filename. This makes it very difficult to determine what files correspond to which in-level assets. They look like:
Content/__ExternalActors__/Developers/ReubenDunnington/TestMap/0/13/2K3MW38V1S1IU7N4RLO7TZ.uasset
Content/__ExternalActors__/Developers/ReubenDunnington/TestMap/0/71/1EAF65SPYD5QU9Z60HJ68N.uasset
Content/__ExternalActors__/Developers/ReubenDunnington/TestMap/0/7F/QI4QLK838WQ26FOIL9E3XF.uasset
Content/__ExternalActors__/Developers/ReubenDunnington/TestMap/1/1W/H1UHF5JF5R4MAEYDWIVHS5.uasset
While Unreal Engine has the ability to render human-readable friendly names for these actors, it is completely opaque from the perspective of source control. It was important for us to ensure Friendshipper has similar support for these types of files to help our developers understand what assets are being modified in source control.
Here’s what it looks like in action. Note the tooltip that shows the original filename when you hover a given OFPA path.
The task of keeping track of GUIDs associated with a friendly name can get quite complicated if you decide to get a remote database involved that guarantees the persistence of the data. However, local-first development tends to keep the solves simple, and while it may not be 100% bulletproof, it is often good enough, at least for a first pass to get off the ground.
How Friendshipper handles OFPA
Friendshipper takes a local-first approach and fetches friendly names from Unreal Engine directly via two methods:
- If an instance of Unreal Editor is running, it makes a HTTP request to translate a list of filenames. The Friendshipper UE plugin has a routing handler that does the translation and serves up the corresponding friendly names.
- If no instance of the editor is running, it runs a commandlet (Unreal in commandline program mode) that does the translation and logs the results. Friendshipper parses the logs and finds the friendly names in them. Note that this approach is quite slow because Unreal startup can take several seconds, but this is a fallback approach that is explicitly user-initiated. Most of the time we expect users to have Unreal running.
These translations are stored in both an in-memory and LRU disk cache. This local-first approach has limitations where it can only know about IDs the local instance of Unreal has seen - so it can’t know how to translate GUIDs for new files that aren’t local yet, or for files that have been deleted whose IDs have been purged from the cache. However, this approach was quick and easy to stand up, and covers the 80% case.
If you’re curious to know more or have specific questions, feel free to join our OSS Discord server and ask! See you there. :)
Reuben