Learn how to effectively handle object identification for standalone (non-editor) games and applications built on Unreal Engine using HierarchyPath.
In the article about HPath in Unreal, we provide guidance on the different ways you can identify objects. You may need to reference that article after reading this one, specifically on the difference between standalone and in-editor object names in Unreal.
Standalone vs Editor Builds
When running a standalone build of an Unreal game, all the static object names will change from the names used in the editor version, rendering any HPaths reliant on specific names invalid. There are several ways to write tests to prevent this issue, and some techniques to overcome it when it happens.
To illustrate this issue, we will use the cropout game. There, we see in Editor that we can identify the town center using the relative HPath (that uses the name of the object).
//BPC_TownCenter_C_0
However, when we create a standalone build (for Windows, Android, or other platforms), that name will have a uniquely generated suffix in its name, and the HPath that was valid in the editor will fail!
How to Fix a Broken HPath in a Standalone Game.
The first step in fixing HPaths that differ in the standalone game is to identify the object you want using TA or GetObjectList("//*").
Using the Test Assistant
With TA, you can connect to the standalone game, and the Object Tree will display all the objects in the scene. You can then filter the list to identify objects of interest. In this case, if we search for ‘town’, we see one object matching, and its HPath is:
/*[@name='BPC_TownCenter_C_2147482313']
Note that you can also use the shorthand notation with names, so it’s equivalent to
/BPC_TownCenter_C_2147482313
Using GetObjectList
If you don’t have the TA or feel more powerful using the API, you can write a script to retrieve and print out all the objects in the scene by calling GetObjectList("/*", true) and printing the results. The information is the same.
List<LiteGameObject> lists = api.GetObjectList("/*", true);
foreach(LiteGameObject item in lists){
Console.Write(item.TypeFullName + ":");
Console.WriteLine(item.HierarchyPath);
}
Now that you have the new name, you could simply use that in the standalone tests. However, we encourage you to try and write HPath identifiers that work in all versions of your game to amplify the power of your tests.
How to write HPaths that don’t break between game versions.
Although a workaround is useful for those who are modifying existing tests, it’s far more powerful to write tests using HPaths that work in both standalone and the editor. This means either using a pattern to match in the name, or building the HPath around tags or classname, which will remain the same between editor and standalone builds.
Matching a pattern
One of the easiest ways to find objects is to take advantage of the patterns used to generate names. Unreal generates unique numeric identifiers and appends them to a name identifier that’s based on the class. Using contains, starts-with, or match functions built into GameDriver’s HPath, you can write selectors that work across all game versions.
Option 1: using the contains() function
//*[contains(@name, 'BPC_TownCenter_C_')]
This will find any object whose name contains the string "BPC_TownCenter_C_", which would match both:
- BPC_TownCenter_C_0
- BPC_TownCenter_C_2147482313
Option 2: Using the starts-with() function
//*[starts-with(@name, 'BPC_TownCenter_C_')]
This will find any object whose name starts with "BPC_TownCenter_C_", matching both.
Option 3: Using regular expressions with match()
//*[match(@name, 'BPC_TownCenter_C_\d+')]
This uses a regex pattern where \d+ matches one or more digits at the end, matching both. When you need powerful regex pattern matching, this is the function to use
Use Tags
In the TA tool, we can see that the town center has two tags. That means we could identify it with an HPath that searches for those tags as follows:
/*[@tag='CollectionPoint']
/*[@tag='All Resources']
In this case, there are no other objects with those tags, so either query works (in both standalone and editor!). Sometimes there may be multiple objects with the same tag, or no tags. In that case, we might look at the class.
Use the Class
In the TA, you can see the class name, labelled as the TypeFullName. This can also be found in the LiteGameObject returned by GetObjectList. In this case, the class is BPC_TownCenter_C, so I can easily retrieve all objects of that class with
//*[@class='BPC_TownCenter_C']
Putting it all together
Often, the basic techniques described above are sufficient. However, in more complex games, you may need to combine the name, class, and tags together to get an HPath.
Example 1: Multiple Objects of the Same Type
Imagine you have several town centers, all using the same base class but with different names. Combine class, name and tag selectors to identify the objects you need:
//*[@class='BPC_TownCenter_C' and contains(@name, 'Main')]
This finds a town center object with a name containing “Main”. You might also combine with tags:
//*[@class='BPC_TownCenter_C' and contains(@name, 'Main') and @tag='CollectionPoint']
This would find a town tagged as being a collection point that has the name “Main” in it.
The key principle is building from general to specific - start with the broadest criteria (like class) and then add more specific filters (name patterns, properties, parent/child relationships) until you have a unique identifier that works reliably across different builds and scenarios.
This approach is especially valuable in complex scenes where you might have dozens of similar objects that need to be distinguished by their context, properties, or relationships to other objects.
Using HPathDebugger
You can test out Hpaths quickly using the HPathDebugger Tool. Here we pasted the HPath from the Object Inspector, and we can see that one item is matching. By changing the HPath and running it here, you can immediately see if it’s valid from outside the editor and how many items in the scene match it.