The ScreenControl module is a large module making UI development easier.
It allows you to create UI screens in a Canvas, with automatic navigation between screens, seperated in layers. Just call what screen you want to open, and all conflicting screens close themselves. When a screen opens, it's also possible other screens open instead of close in reaction to that. Additionally, it keeps track of the order of screens opened to get to the current screen. You can call the system to take a step back, opening the 'previous' screen.
Screens are Entities, which trigger events when they are opened and closed. Other modules make it easy to add effects to your screens, making each UI component pop in and out when the screen opens and closes.
Improved UI interaction by providing a script that catches UI input and fires events for other scripts to listen to. Hook in your own events, or add interaction scripts to it which provide basic functionality such as screen navigation, toggling on and off, opening an URL or triggering an Entity.
ScreenControl has a dependency on Entities, Singletons, UtilityCollection and DynamicVariables.
It contains addon scripts for AssetInstance, GameControl, ObjectPooling, SceneControl and SoundControl.
To get started with ScreenControl, follow these steps:
Create an empty GameObject in your scene, set it's position to (0, 0, 0), name it something like 'UI'.
Create a Camera object as child to the UI container object.
Set Camera [Projection] to [Orthographic], [Culling Mask] to [UI], [Clear Flags] to [Depth Only], and [Depth] to a high value (like 100).
Create a Canvas object as child to the UI container object.
Make sure the Canvas object's layer is set to UI.
Set [Canvas Render Mode] to [Screen Space - Camera], drag & drop the Camera into the [Render Camera] field.
Add CanvasController.cs and ScreenEditorOrganisor.cs to the canvas.
Set ScreenEditorOrganisor's 'Organise' toggle to true.
You're all set!
Using a dedicated UI camera is not a hard requirement for all features, so you may choose to skip steps 1 to 6. However, a few uncommon features will break. Those that require a camera are noted in the documentation below.
You can also see the DemoScene for an example implementation.
The ScreenEditorOrganisor.cs makes sure the screens never conflict while editing them. It activates / deactivates screens to match your actions, making editing screens a little bit easier.
To create a screen, simply add an empty GameObject to your class to act as the container of that screen. Add the ScreenBase.cs script to it.
Determine the layer of the ScreenBase. The rule is; there can be only one screen active at a time per layer. When a screen is opened, all other screens of the same layer are closed. Recommended are the following layers, but you are free to do whatever you want:
Layer -10: Backgrounds.
Layer 0: Main Screens, such as Main Menu or Options.
Layer 10: Overlaying toolbars or notifications.
Layer 20: Popups.
It's recommended to keep some unused layer values, in case you need to add more layers inbetween at a later time during development.
You can add whatever UI you want within the screen, all of which is activated and deactivated when the screen opens and closes.
You can easily add simple or complicated animations to your Screens.
Add an EntitySequence.cs to your Screen object. This will automatically be triggered to appear when your screen opens, or disappear when the screen closes.
Then, add Entities that play out effects on every panel or button or other UI element you want to pop in and out.
You're recommended to check out the ObjectEffects module, which provides an Entity that triggers effects. That way, you can drag & drop any amount and type of effects you wish to have on your UI elements.
If you don't have any module that provides Entities that play out effects, you can create your own. See Entities for how to do so.
When a screen is called to open, other screens need to close first. While closing, you can have the Screen [Wait For Entity] to await the completion of the entity animation. This prevents other screens from popping in while it's still going away.
For a more controlled and simpler approach, you can also set [Delay Close Complete], which does the same, but with a set time delay.
You can add the ScreenLink.cs script to a Screen to make it open or close based on the events of a different layer.
[Link To Layer] determines what layer it is linked to.
[Link Type] determines what happens when something in that layer opens or closes:
Linked To Any. If anything changes in the target layer, this screen closes.
Linked To All. If there are no screens of the target layer open, this screen closes.
Linked To All Auto Open. If there is a screen open in the target layer, this screen opens. If there are none, this screen closes. It mimicks the open state of a layer.
With a ScreenLink set to [Linked To All Auto Open], you can create a 'faded' layer which partially obscures the layers behind it.
Set the target layer to the popup layer, and this screen will always be open when a popup is open.
Make it faded black, give it a CanvasGroup, and fade it in / out when it opens or closes.
With a ScreenLink set to [Linked To All Auto Open], you can create a background layer which is always open as long there is a main screen open.
Set the target layer to the main layer, and this screen will always be open.
To create a button, or anything that catches player input, add a ScreenInteractable.cs to your GameObject. This will catch the default Unity UI input events and fire actions based on them. It also tracks whether input is held down, hovering over, or has selected the interactable with the properties:
isInteracting
isHoverOver
isSelected
You can toggle the interactivity of the interactable with the property:
Interactable
You can determine whether the ScreenInteractable can be selected and deselected by the global Unity UI selection system by toggling:
GlobalSelectable.
However, note that only one object can be selected at a time by Unity's selection system. If you have multiple lists that each have their own selection, this is useless. It is useful however for things like input fields, taking the focus of the current user interaction.
The ScreenInteractable fires the following events:
onInteractStart
onInteractRelease // the input ends while hovering over
onInteractEnd
onHoverOn
onHoverOff
onSelected
onDeselected
onLocked
onUnlocked
If attached to a ScreenBase, the ScreenInteractable will only fire input events if the screen is actually open. This prevents users from interacting with screens that are in the process of a closing animation.
The base ScreenInteraction.cs class hooks into the events of a ScreenInteractable on the same object or its parents, and fires virtual methods that can be overriden by implementing classes. When creating your own simple screen interactions, try to base it off this class to make your job a little easier.
There are many interactions already available:
ScreenInteraction Color. Changes between a Pressed Down Color, Selected Color and Locked Color for any UI Graphic.
ScreenInteraction Entity. Can Show, Hide or Toggle a target Entity.
ScreenInteraction Navigation. Can Close the current screen, Open a target screen, Go Back to the previous screen, or open the Next screen.
ScreenInteraction Quit. Instantly quits the application, or stops playing the editor.
ScreenInteraction SelectEntity. Toggles a target Entity's visibility status based on the ScreenInteractables selection status.
ScreenInteraction Toggle. Toggles a boolean value between true and false on interaction.
ScreenInteraction URL. Opens target URL.
There are a variety of ScreenElements available in the ScreenControl module. Many of them are currently a work in progress, and need a rework.
See the ModeSetters and variables of RuntimeVariables. The ScreenModeSetter sets an IntReference to a mode when the screen it is attached to opens.
Other elements can then react to it. For example, moving 3D elements in the background to the correct position.
You can create your own Lists by using the ListController as base. There are some variations to them.
Inherit the ListController<E> if you want a ListController that handles selection of list elements in a static list, created manually in the scene. Alternatively, you can just use ListControllerBasic without writing your own controller, if you don't have unique data in the ListElements.
Inherit the ListControllerFiller<E, T> to create a ListController which automatically instantiates ListElements with the type <T>, once given a List<T> to setup the list with.
You can add ScreenWorldPoints to your UI, and connect them to a ScreenWorldPointAsset.
Other scripts can reference the ScreenWorldPointAsset without ever having a reference to the Canvas or UI, and get a point in 3D space that overlaps the 2D Canvas position.
Use this to make coins, pickups or other objects move in 3D space to the location of the coins-counter or a UI button of the inventory bag.
Requires the Canvas to be setup properly with a Camera!
Contains a ScreenInteraction which can pause or resume the GameController, pausing and resuming gameplay through UI.
Contains a ScreenInteraction which can open a target scene, with SceneTransition animations.
Contains a ScreenInteraction which can mute or unmute an AudioGroup, using the SoundControl volume manager. Also correctly displays the mute status as an UI icon.
Don't forget to add using DuskModules.ScreenControl; to any script using the module.