You’re used to mucking around in Unity, but suddenly you’re dropped into Unreal Engine. What’s what and where is it? Here’s a collection of rough – and I mean rough – analogs.
I know these aren’t exact matches, but conceptually it helped me find my way around and begin to recognize where the code goes, and what lives where.
Actor (or AActor) is the base class for an object that can be placed or spawned in the world. You can override the base class to create a custom Actor type with all the bells and whistles you need. You can kind of think of this as similar to a behavior on the root of a Unity object
You can attach multiple components to an Actor. These are similar to adding on behaviors to your Unity object. You can create and attach them to the blueprint (prefab) at design time, or to an active Actor at runtime. Components can be added in a hierarchy. You can also set up attachment data to glue them to locations in a skeleton.
You can think of blueprints like Unity’s prefabs or scriptable objects. They’re serialized objects, based on whatever parent class you need. For example, a duck creature would have an Actor based blueprint, to which you could add all your duck related components, skeleton mesh, etc. If you need to specialize the Actor class for your duck, make a ADuckActor derivative of AActor, and reparent the duck blueprint to it. These blueprints can be instanced in the level at design time, or loaded and spawned at runtime, similar to Unity prefabs. Plain old data objects can be derived from UDataAsset, and loaded at runtime like a scriptable object.
A common pattern I used in Unity projects was to create singleton manager objects, where the scene would always contain a single instance of the manager object. These would handle object pooling, or base UI functionality, or debug logging, or asset management*, or a dozen other core systems. While you could possibly achieve something similar in Unreal with singleton actors in the scene, UE provides Subsystems that you can derive from, that set up a single instance at the start of the game and exist throughout. Derive your subsystem from UGameInstanceSubsystem, and away you go.
The only downside I’ve found to using subsystems is that there isn’t an obvious way to have data directly within the subsystem, editable within the editor, as you would with a singleton manager prefab in Unity. I read a forum post about marking the subsystem derived class as Abstract and Blueprintable, which apparently will allow you to create a subsystem blueprint which is loaded and instanced at runtime. I need to try this out. The alternative is to create a data asset that the subsystem loads on Initialize().
* Asset management isn’t a great example here. Unreal has a UAssetManager class for handling asset management, and recommends you derive your own asset manager from this. Still, it stands as an example of singleton-y like system that you may have in your Unity project.
I’ve done a fair amount of in-game UI stuff with Unity, so I’ve ended up doing a fair amount of UI stuff in Unreal, to. In Unity, I’d typically get the UI component prefab from the artist, containing the hierarchy of panels, buttons, sliders, images, text, and all the usual UI gubbins. I’d then attach a custom behavior to the root object, with references to the fields I’d need to read/write during the operation of the UI, and the code for getting, setting and generally mucking about with those controls and their contents.
In Unreal, I got a blueprint from the artist, containing a hierarchy of panels, buttons, sliders, images, text and all the usual UI gubbins. I then created custom parent class, derived from UUserWidget, with references to the fields I’d need to read/write during the operation of the UI… and immediately came unstuck. It’s almost like it wants to work that way: putting a reference in that parent class to, say, a text field that exists somewhere down in the hierarchy and selecting that field in the editor initially appears to work, but it will not save that information. It’s as if it has no way of referencing that object in a way that will serialize and rebind on reload. The way around this is to use name binding. The pointer to the text field in the parent class needs have the same name as the object in the hierarchy:
UPROPERTY(meta = (BindWidget))
This will automatically bind to a UImage instance called ‘MyImage’. It’ll also complain if it can’t find MyImage. Use BindWidgetOptional, if you’re ok with there being nothing bound to it. Useful if you have several widget variations using the same parent class.