A GUIInsert is a virtual LED on the playfield or backbox screen. The API is very similar to LEDs.
See the LEDs section of the SDK Guide for related information.
A GUIInsertScript contains the commands to determine what color a single GUIInsert should be over time. A Mode creates a separate GUIInsertScript for each GUIInsert it controls and adds these scripts to GUIInsertControllerMode. The controller orchestrates which scripts are running: for each GUIInsert, the GUIInsertScript with the highest priority is the one that runs. This way, a Mode with higher priority overrides a Mode with lower priority.
The GUIInsert class is never used and can be ignored. The real workhorse is GUIInsertScript.
A GUIInsertScript is a collection of GUIInsertCommands for one GUIInsert and the code to run the commands. The class derives directly from object. Do not confuse it with a Unity Script. To create a GUIInsertScript, call the constructor with the GUIInsert name and the priority of the Mode:
GUIInsertScript script = new GUIInsertScript("insertName", priority);
You can assign a scriptName to the script. This is optional but it can be useful for debugging because the scriptName will appear in some log messages. This can help distinguish multiple scripts for the same insert, for example by including the mode name in the script name. P3SampleApp never bothers to do this, and that’s fine too.
script.scriptName = "ModeName-InsertName";
A GUIInsertScript starts empty. You add new commands by calling AddCommand():
script.AddCommand("new text", textColors, colors, alpha, fadeTime, duration);
Internally, that creates a GUIInsertCommand which is a data object with no behavior. That’s an implementation detail because you never interact with the data type GUIInsertCommand (unless you implement your own receiver).
The only two commands available are: change a text string and/or linear interpolate the color of an object. Remember the alpha is part of the color, so you can make objects appear and disappear.
You can add multiple commands to a script by calling AddCommand() multiple times. This can create blinking effects.
By default, the script is not removed when it finishes execution. This means the GUIInsert will stay the way it is after the last command. The script can be configured to automatically remove itself when execution finishes, like this:
script.autoRemove = true;
When the script is removed, the next highest priority script is executed and it will likely change the GUIInsert.
To add the script to the GUIInsertControllerMode, post an event:
EventManager.Post("Evt_AddGUIInsertScript", script);
<appcode>BaseGameMode handles the event (in its base class BaseAppMode) and calls the GUIInsertControllerMode instance to add the script. The new script will start executing immediately if it has the highest priority for that GUIInsert (stopping the previous script if there was one).
I’m not sure why GUIInsertControllerMode does not handle the event directly. In any case, the Post is needed because the caller Mode does not have the reference to GUIInsertControllerMode.
Nothing stops a mode to add multiple scripts for the same insert at the same priority at the same time. The result is unpredictable and likely a bug in the application.
A GUIInsertScript is meant to be reused. The procedure is simple: clear the existing commands, add new ones, then add the script to the controller again. The script will reinitialize itself if it was currently executing.
script.Clear();
script.AddCommand("new text", textColors, colors, alpha, fadeTime, duration);
EventManager.Post("Evt_AddGUIInsertScript", script);
When the Mode no longer wants to control the GUIInsert, it must remove the script from GUIInsertControllerMode by posting an event:
EventManager.Post("Evt_RemoveGUIInsertScript", script);
<appcode>BaseGameMode handles the event (in its base class BaseAppMode) and calls the GUIInsertControllerMode instance to remove the script. The script will stop executing immediately if it had the highest priority for that GUIInsert (and the script with the next highest priority will execute instead). The script will no longer be considered for that GUIInsert.
Always remember to remove a script added to GUIInsertControllerMode when no longer needed. In particular, make sure to remove all remaining scripts in mode_stopped() when the Mode ends.
A script that was removed can be added later as is, or it can be reused with different commands.
GUIInsertHelpers keeps a list of all GUIInsert names. The list is populated in the RegisterGUIInserts method of <appcode>BaseGameMode. These are the GUIInserts in P3SampleApp:
private void RegisterGUIInserts()
{
base.RegisterGUIInserts();
GUIInsertHelpers.AddGUIInsert("InfoPopup");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight0");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight1");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight2");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight3");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight4");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight5");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight6");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight7");
}
In P3SampleApp, the InfoPopup GUIInsert is used in PopupMode. The SideTargetHighlightN GUIInserts are used in SideTargetMode.
Whenever a Mode is created, the P3Mode base class creates a new GUIInsertScript for each registered GUIInsert. These scripts are for the exclusive use of this mode. All the scripts are empty and at the same priority as the Mode. The list of scripts is stored in the field GUIInsertScripts. The same scripts are also stored in the field GUIInsertScriptsDict which is a Dictionary mapping the GUIInsert name to the corresponding script.
protected List<GUIInsertScript> GUIInsertScripts;
protected Dictionary<string, GUIInsertScript> GUIInsertScriptsDict;
This is not mandatory, but it’s simpler to reuse the scripts that are already created by the base class. For the GUIInsert named insertName, the mode can access the corresponding script with
GUIInsertScriptsDict["insertName"]
The scripts are not automatically added to GUIInsertControllerMode. The Mode must configure the scripts for the GUIInserts it controls. After the Mode is started, it can add these scripts to GUIInsertControllerMode explicitly.
When the Mode ends, the SDK automatically removes the scripts from GUIInsertControllerMode. It can do this because it knows the list of all the scripts in the field GUIInsertScripts. This might try to remove some scripts that were never added, but that’s not a problem.
The RegisterGUIInserts() method in P3SABaseGameMode is declared like this:
private void RegisterGUIInserts()
This is a bug. The private method is never called by the base class. Instead, the method must use the override keyword to override the protected virtual method in the base class:
protected override void RegisterGUIInserts()
This point is moot though, as we discuss next.
If no GUIInsert names have been registered yet, GUIInsertHelpers returns this list:
"Lite",
"lIte",
"liTe",
"litE",
"Lock",
"lOck",
"loCk",
"locK",
"InfoPopup",
"SideTargetHighlight0",
"SideTargetHighlight1",
"SideTargetHighlight2",
"SideTargetHighlight3",
"SideTargetHighlight4",
"SideTargetHighlight5",
"SideTargetHighlight6",
"SideTargetHighlight7"
The GUIInserts must be registered before the Mode base class can create the correct GUIInsertScripts. RegisterGUIInserts() is called in Start() which is after almost all the modes have been created (including game specific modes like HomeMode).
The GUIInserts in P3SampleApp are a subset of the default list, so that still works. The problem will appear if the app defines GUIInserts that are not in the default list.
Registering the GUIInserts in the P3SABaseGameMode constructor is too late. We need to register the GUIInserts before the base constructor is called. This can be done with a static constructor because that code is executed when the class is loaded, before any instance can be created.
static P3SABaseGameMode()
{
GUIInsertHelpers.AddGUIInsert("InfoPopup");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight0");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight1");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight2");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight3");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight4");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight5");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight6");
GUIInsertHelpers.AddGUIInsert("SideTargetHighlight7");
}
To avoid duplicates in GUIInsertHelpers, the RegisterGUIInserts() method in P3SABaseGameMode must be removed. The default implementation of the method adds no GUIInserts which is exactly what we want.
P3SABaseGameMode is the first mode created when the app starts. This will load the class and execute the static constructor before creating any mode. With this approach, all modes have the correct list of GUIInserts.
GUIInsertHelpers got its name because it contains many helper functions. A typical helper function takes an existing GUIInsertScript, clears it, adds some commands to achieve a specific effect and finally adds the newly configured script to GUIInsertControllerMode.
static GUIInsertScript OnInsert(P3Controller controller, GUIInsertScript script, ushort[] color)
static GUIInsertScript OnInsert(P3Controller controller, GUIInsertScript script, string text, ushort[] textColor, ushort[] color)
static GUIInsertScript PulseInsert(P3Controller controller, GUIInsertScript script, ushort[] pulseColor, ushort[] endColor)
static GUIInsertScript FadeOutInsert(P3Controller controller, GUIInsertScript script, ushort[] color, double fadeTime)
static GUIInsertScript FadeOutInsert(P3Controller controller, GUIInsertScript script, string text, ushort[] textColor, ushort[] color, double fadeTime)
static GUIInsertScript BlinkInsert(P3Controller controller, GUIInsertScript script, ushort[] color)
static GUIInsertScript BlinkInsert(P3Controller controller, GUIInsertScript script, ushort[] color, double fadeOnTime, double onTime, double fadeOffTime, double offTime)
static GUIInsertScript BlinkInsert(P3Controller controller, GUIInsertScript script, string text, ushort[] textColor, ushort[] color)
static GUIInsertScript BlinkInsert(P3Controller controller, GUIInsertScript script, string text, ushort[] textColor, ushort[] color, double fadeOnTime, double onTime, double fadeOffTime, double offTime)
The return value is the GUIInsertScript passed as argument.
Since these helper methods add the script to GUIInsertControllerMode, make sure you call them only when the mode is already started. In particular, do not initialize the scripts in the mode constructor with these methods.
You will often see this pattern, where the return value is assigned back to the Dictionary:
GUIInsertScriptsDict["insertName"] = GUIInsertHelpers.OnInsert(p3,
GUIInsertScriptsDict["insertName"], color);
Unfortunately, this code forgets to update the list in the field GUIInsertScripts. If the return value was a different script, that would break the automatic removal of the scripts when the mode ends. Luckily, the script returned is always the script passed in. We can simplify the statement by removing the assignment:
GUIInsertHelpers.OnInsert(p3, GUIInsertScriptsDict["insertName"], color);
GUIInsertHelpers also has this helper method to add or remove a script:
static void AddorRemoveScript(GUIInsertScript script, bool run)
Call GUIInsertHelpers.AddOrRemoveScript(script, true) to post the Evt_AddGUIInsertScript event and thus add the script to GUIInsertControllerMode.
Call GUIInsertHelpers.AddOrRemoveScript(script, false) to post the Evt_RemoveGUIInsertScript event and thus remove the script from GUIInsertControllerMode.
The AdjustColor() method in GUIInsertHelpers reduces the color values to 1/4 the original values if the name contains the keyword "flasher". This method is a carry-over from LEDHelpers where it makes more sense.
static ushort[] AdjustColor(string name, ushort[] startingColor)
A synchronizer is an object that can ensure multiple GUIInsertScripts start at the same time. GUIInsertHelpers has two methods to create a synchronizer:
static void AddSynchronizerGroup(int index, Double interval)
static void AddSynchronizerGroup(int index, Double interval, int dependentIndex)
The index is the id of the synchronizer. The scripts reference the synchronizers by their index. The interval is the period between synchronizer firings. The dependentIndex is the index of another synchronizer that must fire for this synchronizer to fire.
The value of the synchronizer index is arbitrary. The application can choose any integer as long as it does not collide with an existing synchronizer. It is common to use very small integers.
Synchronizers cannot be removed once they are created. Make sure you don’t continuously create new synchronizers because that will cause a leak.
GUIInsertHelpers.AddSynchronizerGroup() is implemented by posting the Evt_AddGUIInsertSynchronizerGroup event with an array of 3 doubles corresponding to the 3 arguments of AddSynchronizerGroup(). BaseAppMode handles this event and calls AddSynchronizer(index, interval, dependentIndex) directly on the GUIInsertControllerMode instance.
To enroll the GUIInsertScript in a synchronizer group, assign the synchronizer index to the script:
script.synchronizerIndex = index;
The GUIInsertScript documentation says the default value of synchronizerIndex is 0, but the real default value is -1. This means the script is not controlled by any synchronizer unless its synchronizerIndex is assigned.
Think of it this way for the case without a dependentIndex. The synchronizer defines a set of GUIInsertScripts. The synchronizer fires at regular intervals. When the synchronizer fires, it adds all its GUIInsertScripts to GUIInsertControllerMode. If the scripts were currently executing (because they took longer than the interval), they get reinitialized to the beginning. The controller sorts all the scripts by GUIInsert and priority. For each GUIInsert, the script with the highest priority is the one that runs (not necessarily what was just added by the synchronizer).
delayedGUIInsertScript is an internal data object used by GUIInsertControllerMode to manage the execution delay caused by a Synchronizer interval. This is an implementation detail because you never interact with that class.
When a GUIInsertScript is selected for execution, it runs its commands one after the other.
To execute a GUIInsertCommand, the script posts the mode to GUI event Evt_RunGUIInsertCommand<insertName> with the GUIInsertCommand as the event data. Notice how the insertName is appended to the event name. This way the event goes directly to the intended GUI Component without bothering other objects.
The GUI components that can handle the event are: ColorReceiver and TextReceiver.
GUIInsertColorChanger can also handle the event but it is obsolete. Use ColorReceiver and/or TextReceiver instead.
The ColorReceiver component can be added to a GameObject to handle the color changing requests coming from GUIInsertScripts. This component expects a Renderer, Image, RawImage and/or Button on the same GameObject. It can also use an optional Material component directly on the game object to override the Renderer's Material.
In the Inspector, set the ColorReceiver id to the GUIInsert name.
ColorReceiver handles the Evt_RunGUIInsertCommand<id> event expecting a GUIInsertCommand in the event data. It executes the command by doing the linear interpolation of the color.
ColorReceiver handles the Evt_State<id> event expecting a bool as event data. Pass true to enable the Renderer, false to disable it. LanesMode and HUDMode are examples of modes posting the Evt_State<id> event.
The TextReceiver component can be added to a GameObject to handle text string and color changing requests coming from GUIInsertScripts. This component expects a Text or TextMesh on the same GameObject.
In the Inspector, set the TextReceiver id to the GUIInsert name. Also specify whether to updateMaterialColor, the defaultFontSize, whether to adjustFontSizePerBounds, and the bounds.
TextReceiver handles the Evt_RunGUIInsertCommand<id> and Evt_State<id> events like ColorReceiver.
TextReceiver also handles the Evt_Text<id> event expecting a text string as event data. The TextReceiver reacts by changing the text on the Text or TextMesh component.
There is no GUIInsert Prefab. You will have to create your own if you have multiple GUIInserts in your scenes. Take clues from the two examples below.
You can see a ColorReceiver in the HomeScene at the path Home/SideTargets/Highlights/SideTargetHighlight0
You can see a TextReceiver in the HomeScene at the path Home/ApronDisplay/BallNumber
Here is a list of modes that use GUIInserts one way or another.
GUIInsertControllerMode orchestrates the execution of the scripts.
BaseAppMode creates the BaseGUIInserts mode and the GUIInsertControllerMode. It handles the Evt_AddGUIInsertScript, Evt_RemoveGUIInsertScript, Evt_AddGUIInsertSynchronizerGroup by making the corresponding call directly on the GUIInsertControllerMode instance.
BaseGUIInserts mode blackens all GUIInserts when it starts.
BonusMode blackens all GUIInserts when it starts.
PopupMode handles these events:
Many modes send these events to show a diagnostic or error message.
SideTargetMode controls the SideTargetHighlights, blinking when unhit, steady on when hit, off when all targets are complete.
RGBFadeMode in LightShows.cs turns a list of GUIInserts red, green, blue and black in sequence.
HomeMode, P3SAAttractMode and SceneMode blacken all GUIInserts when the scene goes live.
GUIInsert | LEDs | |
Script Class | GUIInsertScripts | LEDScripts |
Declaration | RegisterGUIInserts() | Base machine definition and module definition file |
Dictionary | GUIInsertScriptsDict["name"] | p3.LEDs["name"] |
default autoRemove | false | true |
Controller Mode | GUIInsertControllerMode | LEDControllerMode |
Add | GUIInsertHelpers.AddOrRemoveScript(script, true) | p3.LEDController.AddScript(script, -1, 0) |
Remove | GUIInsertHelpers.AddOrRemoveScript(script, false) | p3.LEDController.RemoveScript(script) |
Synchronizer | GUIInsertHelpers.AddSynchronizerGroup( index, interval, dependentIndex) | p3.LEDController.AddSynchronizer( index, interval, dependentIndex) |