UE_Angelscript — Separating UI Blueprint presentation and logic#

A UI has all sorts of widgets. The UE editor is the best place to work on their appearance: what you see is what you get. Their logic usually lives in UI Blueprints, but with so many widgets, maintenance becomes a headache.

Since switching to AS for UI logic, I can put all that scattered logic into code files. It is much easier to manage, and presentation is separated from logic: the UI editor handles appearance, while scripts handle behavior.

The official documentation does not say much about this either.

A tiny architecture for separating UI Blueprint presentation and logic#

One sentence: every widget in the UE editor inherits from an AS class.

Usually they have a one-to-one relationship. A UI widget and an AS class are technically child and parent, but with binding, I think of them as companions.

Move inheritance between UI widgets into the AS classes where possible. In other words, avoid inheritance between UI Blueprints. Why? Because it breaks the one-to-one relationship and makes changes less direct. Enough of that and the complexity can grow considerably.

I will expand on the simple Example_Widget_UMG.as example below.

This tiny architecture naturally starts with a UI-related class inherited from cpp.

class UExampleWidget : UUserWidget

Matching UI Blueprint widgets to AS members#

Every UI Blueprint widget marked as a variable automatically creates a Blueprint variable, which AS can bind.

This code binds the UI Blueprint’s UTextBlock named MainText as an AS variable. Once bound, the variable disappears from the Blueprint variable list. The Blueprint can still use it, of course, if you want logic there too.

class UExampleWidget : UUserWidget
{
	// bind the Blueprint variable named MainText to this AS variable
    UPROPERTY(BindWidget)
    UTextBlock MainText;
}

An extension: UI Blueprints often have animations. Those can be bound too, with these parameters:

    UPROPERTY(BindWidgetAnim)
    UWidgetAnimation OnSpawned;

With the necessary variables bound, it is time to write the logic.

PreConstruct and Construct#

PreConstruct and Construct are used often, much as they are in UI Blueprints.

	UFUNCTION(BlueprintOverride)
	void PreConstruct(bool IsDesignTime)
	{
		AttributeNumber.Font.Size = 12;
	}

   UFUNCTION(BlueprintOverride)
    void Construct()
    {
    }

Binding UI events#

If the current UI class owns the event, override the corresponding function directly. Buttons, for example, have OnClicked, OnHovered and OnUnhovered.

For an event on another UI widget, use delegate binding.

Here is an example using CommonUI’s Button class:

// the current class is the subject whose events need binding, so just override the function
	UFUNCTION(BlueprintOverride)
	void OnClicked()
	{
	}

// the subject whose events need binding is a member of the class, so bind that member's delegate
YourBoundButton.OnButtonBaseClicked.AddUFunction(this,n"OnButtonBaseClicked")

Checking a UI Blueprint (optional)#

OnValidateCompiledWidgetTree is a cpp function for checking UI Blueprints. When a check fails, it can report a Blueprint compilation error.

A UI Blueprint can contain many widgets with special configuration requirements. For example, several copies of the same widget may need different settings as well as different names. Check that here.

The UI Blueprint error-reporting code has no Blueprint version, so AS cannot call it directly either. This needs some manual wrapping and binding. The final result looks like this:

#if EDITOR
	UFUNCTION(BlueprintOverride)
	void OnValidateCompiledWidgetTree(const UWidgetTree BlueprintWidgetTree) const
	{
		if (OptionsClass.IsNull())
		{
			ShowCompileLog_Error("OptionsClass is null! Check and Set it in the Editor!");
		}
		if (UserFacingExperience == nullptr)
		{
			ShowCompileLog_Error("UserFacingExperienceDefinition is null! Check and Set it in the Editor!");
		}
		if (QuitGameClass.IsNull())
		{
			ShowCompileLog_Error("QuitGameClass is null! Check and Set it in the Editor!");
		}
	}
#endif

ShowCompileLog_Error is a function I added in cpp to report errors.

This wrapping and binding is a little involved: inherit the commonly used UI Blueprint classes and add the functionality there. I will skip those details.

Decoupled interaction with other classes (optional)#

UI Blueprint interaction with other classes often means writing lots of references. Delegates can decouple some of that, but still need at least a one-way reference. Can two classes exchange information without knowing each other? Yes: the global broadcast system UGameplayMessageSubsystem.

This SubSystem has AS bindings. The official documentation provides the GameplayMessageRouter with Angelscript binding. Its usage is simple and explained in the Readme, so I will not repeat it.

It can decouple UI Blueprints from other code easily. After registering a listener, remember to unregister when it is no longer needed.

Summary#

Let presentation be presentation and logic be logic: the familiar separation of front end and back end. AS’s companion binding for UI Blueprints makes that easy.

It also solves another issue along the way: casts between UI Blueprints gradually expand asset references, while casts between code classes do not have that problem.

Decoupling UI Blueprint presentation and logic has plenty of benefits. A bright future for everyone!


×

Contact

Feedback

Send bug reports and feedback on GitHub Issues.

Cooperation

For cooperation enquiries, email contact@cuviss.com.

×

About

Terms of Service

See the Community page for each plugin's license.

You may reference site content with attribution.

The software and documentation are provided "as is" without warranty.

Unreal Engine and other third-party projects and names are trademarks of their respective owners. soloAS is not affiliated with Epic Games or Hazelight.

Attribution and showcase requests are non-binding, as described on the home page.

Updated 2026-10-24

Privacy Policy

This site uses no analytics, cookies or tracking, and has no forms or third-party scripts.

The site is hosted on GitHub Pages. GitHub may process standard access logs under its Privacy Statement.

What you send by email is shared with us; what you send through GitHub Issues is shared with GitHub.

Updated 2026-10-24

Contents

Pages

On this page