Skip to content

Scripting

Your dialog's script will be placed in a different location this time, outside of the asset bundles. Your project might have its own scripts folder and in there a subfolder UI for all your UI related scripts such as this one that you are about to make.

scripting_01.png

Make a folder and create your script so we can get started!

namespace WHS  
{  
    public class SampleScreen : ACloseableDialog  
    {  
    }  
}

The simplest form of a dialog will look like the above, either inheriting from the classes below but the usages is most part the same.

ACloseableDialog: a UI View that appears that doesn't cover the entire screen and that can be closed by external factors

or

ADialog: a UI view that is meant to take over the screen and is not replaceable by other views.

By inheriting the above, you will then have to assign the values of these fields to itself and then there's _uiGroup which can be selected from ScriptableObjects created in the project.

// INSIDE AClosableDialog.cs
[Header("Base Dialog Properties")]  
[SerializeField, NotNull]  
protected Canvas _canvas = null!;  

[SerializeField, NotNull]  
protected CanvasGroup _canvasGroup = null!;  

[SerializeField, NotNull]  
protected AScriptableUiGroup _uiGroup = null!;

scripting_02.png scripting_03.png

From there then this dialog needs to be spawned from somewhere, most likely via a SystemBase that is listening for a Tag Component that would toggle this dialog to show up. You could have a SampleDialogSpawnSystem that is responsible for listening to this system and has access to your new dialog's prefab, using:

  • ScriptableConfigSingleton - Use this Scriptable Object to store your prefab then access it via SystemAPI

Your system will need an async method to instantiate the screen and await for the dialog to close.

private async void SimpleScreen(SingletonReferenceContainer singletonReferenceContainer, SomeConfigStuff otherConfig) {  
    try {  
        Log.Debug("Opening dialog.");  
        IUiService uiService = SystemAPI.ManagedAPI.GetSingleton<UiService>();  
        using IUiViewReferenceDisposer<SampleScreen> referenceDisposer =  
            await uiService.CreateUiViewAsync(singletonReferenceContainer.SampleScreenPrefab);  
        referenceDisposer.Instance.Setup(otherConfig);  
        await referenceDisposer.Instance.AwaitUntilClosedAsync();  
    } catch (System.Exception e) {  
        Log.Error(e.Message);  
}}
Here we required to pull the UiService that is responsible for moderating the dialogs showing on screen, arranging their order and pooling if required.

We create the screen via this service and hold to its disposer which holds reference to our dialog's instance. You can use this reference to pass some parameters to your dialog so you could add a Setup() method call to do this. Like this, if there is any data that is attainable within ECS, this would be the past spot to do so.

How do I react to data changes?

If you want to react to certain changes inside your game state, for example, a system is keeping track of when a building is possible to be built.

Read data from EntityManager via each ECS frame

There is an interface that your dialog could inherit IUpdateBySystem that will provide this method to be implemented: public void OnUpdate(EntityManager entityManager)

The UiService will be aware that this dialog has this method and thus have a system iterate through any visible dialogs with this interface implemented and call OnUpdate. This will let you read data from inside your ECS World on each ECS tick.

Have a system with the instance reference do it for you

You could have a system that only runs when your dialog is visible, perhaps a RequireForUpdate<SampleScreenRef>() and call an update method with the data model you want to provide to your Dialog. This way you can restrict all ECS related calls from inside a system and let your “view script” handle the sorting of information on the screen.