The three virtues of a programmer: Laziness, Impatience, and Hubris. – Larry Wall
This page is reserved for concepts behind and examples of complex behaviour using systems of Triggers or other actors capable of triggering Events. There are large differences in the available Trigger classes between UT and UT200x.
For the UT version of this page, see Trigger Systems (UT).
A Trigger System is any collection of actors that produce results (Lights, Effects, Movers, Damage, game points, etc.) from some stimulus or stimuli. (Events, Actor Proximity, Mover action, game condition, etc.) This is an open-ended definition by design. The label "Trigger System" can include something as simple as a lift Mover activated by a Trigger actor, or something as complex as a lever Puzzle, like the example linked to from the bottom of this page. Although these methods can be complex, utilizing Trigger Systems is a rudimentary way for the level designer to achieve complex behavior from stock actors and without coding in UScript.
- 1 Trigger Systems as a kind of AI
- 2 Managing Trigger Systems
- 2.1 Construction workflow
- 2.2 Testing / Debugging methods
- 2.3 Triggers
- 2.4 Trigger
- 2.5 Instigators
- 2.6 ScriptedTriggers
- 2.7 TriggeredConditions & Action_IfCondition
- 2.8 Using various actors within Trigger Systems
- 3 Examples
- 4 External Links
- 5 Related Topics
- 6 Discussion
Trigger Systems as a kind of AI
Artificial Intelligence, as it pertains to games, can be defined as designed behavior. Trigger Systems could be thought of as the very simplest form of AI in games, because although they may do very little decision-making, they do recieve stimuli and react to it. And as the complexity of the Trigger System grows so does the complexity of the behavior that can result.
What's the difference between a Trigger and a trigger?
No, this is not a trick question. Often in discussing Trigger Systems, there will be a problem in communicating precisely because of the distinction between these two words. The actors Triggers or Trigger are classes that are meant to handle the firing of Events and/or behave in a certain way according to stimuli they have recieved. However, trigger is a word that can be used as a verb (describing the act of firing an Event) or noun (describing the general mechanism that is meant to react to stimuli). This can get confusing quickly. When describing the details of Trigger Systems, be aware of this distinction and try to avoid the trapping of using a phrase like, "How can I trigger a Trigger to trigger another Trigger after one Trigger is triggered by a Trigger?" (A limrick about a woodchuck comes to mind. :)) Instead use words like, "Activate", "Fire", "Turn on", etc. to replace the verb form of "trigger" and use words like, "Initiator", "Activator", "Mechanism", etc. to replace the noun form of the word.
Activation & Detection
Depending on the type of actor that is used, they will be activated according to different stimuli recieved. A normal Trigger, for example, can either be Touched by a player (or valid actor class) colliding with it's CollisionCylinder, or it can itself be triggered, by firing an Event that matches it's Tag. Additionally, different actors will have unique reactions to stimuli. While a Trigger actor will simply fire it's Event, a Mover set to InitialState -> TriggerOpenTimed will move. Also, some actors that can fire an Event will keep track of and pass along the identity of the Pawn who is responsible (if any), called the Instigator, while some actors will not pass this along to the next element in the Trigger System. It is up to the level designer to familiarize themselves with the nuances of each actor that can be used in their desired Trigger System design.
Beyond simply reacting to stimuli, Trigger Systems can actually make more complex decisions. Instead of simply, "If the player is here, do that", a Trigger System can go steps beyond and check, "only do that if this condition is true. Otherwise, do something else". The point is, by using some of the more sophisticated actors available, more complex behavior is possible using Trigger Systems. Level Designers are only limited by the amount of time and available will power to meticulously configure extremely complex Trigger Systems.
Managing Trigger Systems
Developing robust Trigger Systems can be difficult to the impatient or careless. Trigger Systems are built by matching Tag and Event names, sometimes following complex logic and keeping track of a lot of variables. The only "trick" to successful Trigger System development is to be meticulous. Go one step at a time and test each step as it's developed.
A level designer's first step in Trigger System development is the design. A logical schematic, flow chart or list of notes should be drafted that will produce the intended results amidst any stimuli the system may recieve. Start with the basic idea of the system and think of examples in real-life that corralate. Then, think of how such a system would react in the game environment. You should have a clear idea of the necessary actors and their roles in the system before you proceed to construction.
During construction, you'll want to vigorously test each element of the Trigger System as it's put in place. The worst situation that can happen results from spending a lot of time putting many elements together only to find that the system doesn't work as intended and the one or more elements responsible for the malfunction must be tracked down. This can often take much longer than the original construction.
Testing / Debugging methods
The easiest and most reliable way to test the Trigger System is to use it as it is intended to be used in the game. If that option isn't available, temporary measures can be taken to stimulate the Trigger System elements or detect their reactions to stimuli.
Often times a Trigger System requires a specific Event to be fired. To test this, level designers can take advantage of the console command, "causeevent <EventName>". This is an instant brute force way to fire an Event at any time. Sometimes a temporary Trigger can be placed and set to the appropriate Event so that the level designer can test elements of the Trigger System. Other times, a Trigger System will require a player Pawn or other class actor to be present at a particular location, like within the CollisionCylinder of a Trigger actor. The level designer can either simply playtest the level and travel to that location or they may have to spawn a particular actor at that location, using the console command, "summon <PackageName>.<ClassName>", or by placing or otherwise spawning the actor class required to test.
Detecting subtle elements of a Trigger System can be a problem at times, but temporary measures can be taken to help test elements of the Trigger System. Sometimes level designers can make use of a TriggerLight for a visual indication of the Event fire by that element. A ScriptedTrigger can be set up to ACTION_WaitForEvent and execute an ACTION_PlaySound. Even particle Emitters or Movers set to be triggered can be used for this temporary purpose. The level designer will want to make sure their temporary measures are set up correctly before testing and removed completely after testing.
Triggers is the parent class for all triggers, included those not intended to be activated by the Touch of players or other actors. Trigger is a specific subclass that can expect to be Touched (and UnTouched) to activate and deactivate.
Some of the many useful Triggers classes in Trigger Systems are:
- Counter - Listens for a specific number of Events before activating
- UseTrigger - Can be activated only by a Pawn within it's CollisionCylinder who hits the Use key, or performs a Use function (as Bots often do).
- VolumeTrigger - Listens for an Event and in turn will activate/deactivate Volumes (as in PhysicsVolume bPainCausing property)
Triggers (those actors designed to be Touched by actors), have a set of propeties that allow a very wide range of functionality. These classes are also able to "UnTrigger", when UnTouched by the appropriate actors. UnTriggering can effect some actors such as Movers set to InitialState->TriggerControlled or TriggerAdvance, or TriggerLights set to TriggerControlled, etc.
Under Object->InitialState, Trigger classes have a few settings that allow them to act in different ways. They may be become active or inactive, waiting for an Event to either turn on, turn off or toggle.
Proximity types, TT_Shoot and TT_ClassProximity
Although Trigger classes can always be activated by an Event that matches their Tag (triggering the Trigger), Trigger classes can also be set to a TriggerType enum that will define which stimuli they will respond to. If set to TT_Shoot, they will expect to be activated via damage. Otherwise, the Trigger class will be activated when an actor enters it's CollisionCylinder. The type of actor it responds to will depend on the Proximity type set. If set to TT_ClassProximity, the Trigger class will only respond to the actors of the class (or subclass) defined in the property Trigger->ClassProximityType.
Adjusting the CollisionCylinder or other Collision properties is a common way to make the Trigger class perform the intended function for the Trigger System. Often a stock Trigger will not cover the entire area a player can occupy without some editing of CollisionRadius. Keep in mind the CollisionCylinder can be as large as it needs to be to detect the actor it listens for.
ReTriggerDelay vs RepeatTriggerTime
Sometimes these two propeties can confuse even veteran level designers. Keep in mind that the "Re" in "ReTriggerDelay", means the Trigger class must be deactivated (or UnTriggered) for this to have any bearing on the situation. It is the time the Trigger class will wait before checking to see if it should again switch from deactivated to activated. RepeatTriggerTime is the time the Trigger class will wait before firing it's Event again, even though it is already active.
Most elements of a Trigger System, whether Triggers class or Trigger class, will keep track of the Pawn who initiated the activation, if any. This Pawn is called the Instigator. If a player walks into the CollisionCylinder of a stock Trigger, that player's Pawn is the Instigator. If a Trigger class is set to TT_Shoot, the player Pawn who shot it is the Instigator. This distinction is important for a number of situations where Trigger Systems are used. Sometimes a Trigger System is designed to act upon that Pawn, as in: damage it, heal it, give inventory, award points, send a discreet message, assign it as an Enemy to an AI agent, etc. Technically, the Instigator is an arguement of the TriggerEvent() Actor function. As one element of the TriggerSystem triggers another, the original Instigator is normally passed along. Several Trigger System elements can pass along the Instigator identity to the element that needs it. But, depending on how the Trigger System is designed, the Instigator can be lost. It is important for the level designer to try and maintain the Instigator through Trigger Systems which will need to know the Instigator as part of the result.
AIScript classes (ScriptedSequences, ScriptedTriggers, UnrealScriptedSequences) are meant to execute a series of ScriptedActions, small commands that can perform a wide variety of tasks. While ScriptedSequence is meant largely for controling Pawns, the subclass ScriptedTrigger is meant for more general purposes and are often very useful in Trigger Systems. They are capable of executing many of the ScriptedActions that ScriptedSequences are, but are restricted from executing those ScriptedActions that control Pawns, like ACTION_PlayAnim, ACTION_SetViewTarget, ACTION_DestroyPawn, etc.
It's important to note that ScriptedTriggers are not Triggers. They do not by default check to see if an appropriate actor has entered it's CollisionCylinder to execute an Event. In fact, they do not do anything by default. ScriptedTriggers, like all AIScript classes, must be set up with a series of ScriptedActions to do anything. These actors are merely named "trigger", but do not share the same functionality by default.
Using ScriptedActions within TriggerSystems
The value of using ScriptedActions stems from the modular nature. A series of ScriptedActions (sometimes called a "script", not to be confused by UScript coding) can be in any order and can be of any length. Some ScriptedActions are active and some are known as latent, meaning that they wait for a stimulus of some kind before proceeding. An example of an active ScriptedAction is ACTION_TriggerEvent, which simply fires an Event immediately upon execution. An example of a latent ScriptedAction would be ACTION_WaitForEvent, which halts all execution until the Event it is listening for is fired. There is a wide selection of ScriptedActions to choose from and custom ones are fairly easy to code. For those used to UScript, making custom ScriptedActions are a very easy way to set up complex custom code that any level designer can use.
The series of ScriptedActions normally execute in a linear way, going through each step from the first to the next and so on, ("do this, then do that, then do the next thing...") but they can also execute non-linearly ("do this, then go back and do this again, do that, then go back and do this again"). The main way of altering the order in which the series of ScriptedActions run (aka, the script flow), is by using ACTION_GotoAction. This simply directs the AIScript to begin executing from the step specified in the ACTION_GotoAction properties and go on from there. Warning: If you set up an AIScript of any kind to execute an ACTION_GotoAction that loops back and eventually returns to the same ACTION_GotoAction without inserting any kind of delay, by using ACTION_WaitForTimer or some other latent ScriptedAction, this will crash the engine with an "Infinite Script Recursion" error.
Some ScriptedActions can be grouped into a conditional block, a subset of actions that may or may not execute, depending on a particular condition. These ScriptedActions are sometimes called "conditionals" and will start with the keyword, "If", as in, ACTION_IfCondition or ACTION_IfRandomPct. The conditional ScriptedAction will mark the beginnnig of the conditional block, while a special ScriptedAction called ACTION_EndSection, marks the end. If the condition is true, the block will be executed, if the condition is false, the entire block will be skipped and the next ScriptedAction to be executed will be the one directly after the ACTION_EndSection step.
TriggeredConditions & Action_IfCondition
TriggeredCondition is a special subclass of Triggers. It will listen for an Event which matches it's Tag property, like many other Triggers classes. The difference is, TriggerConditions do not do anything with that stimuli. Instead, TriggerConditions are placeholders for that information, so that a ScriptedSequence of some kind, using an ACTION_IfCondition, can "read" it. ACTION_IfCondition and TriggeredCondition are used in tandem to allow ScriptedTriggers (and the like) to be able to read various conditions in game and then execute a conditional block if that condition is true.
Managing conditional script flow
Managing the script flow with respect to TriggeredConditions the at the heart of all complex ScriptedSequences. While a ScriptedSequence can be designed with flexibility using the modular functionality of ScriptedActions, it is a static series of commands that does not change. However, by recieving and reacting to variable stimuli via TriggeredConditions and ACTION_IfCondition, the script can become itself flexible and can react to changing conditions in game.
TriggeredConditions can be set to bTriggerControlled True. This means, if the Event is triggered, it will register and the TriggeredCondition is active. But as soon as the Event is untriggered, it will become inactive again. Movers set to Object->InitialState TriggerControlled act the same way; they will begin to open when triggered, but immediately begin to close when untriggered. A normal Trigger can send both trigger and untrigger Events.
An example use for a Trigger-controlled IfCondition would be a situation where the script is doing some repetitive behavior and has to react immediately to both a stimuli and to the absencce of that stimuli. Say a script is making the sounds of an internal combustion engine. While idling the sound, "putt", is regular and repetitive. However when the accelerator is pressed, the script should play a different sound instead of the regular idling sound: "vroom". And as soon as the accelerator is released, the idling "putt" sound should be resumed each loop. By setting up the accelerator to trigger the Event matched by a TriggeredCondition (set to TriggerControlled), an IfCondition that checks for it every "putt" loop can divert the flow of the script to instead play the "vroom" sound before continuing the loop as normal. Here's schematic of how that script might look:
 ACTION_WaitForTimer (0.2)  ACTION_IfCondition ("accel")  ACTION_GotoAction (6)  ACTION_EndSection  ACTION_PlaySound ("putt")  ACTION_GotoAction (0)  ACTION_PlaySound ("vroom")  ACTION_GotoAction (0)
Here the script is listening for an Event "accel" to be active while it loops. When it is, the script plays the "vroom" sound instead of playing the "putt" sound, as it does normally.
Toggled conditions: Your TriggerSystem having "memory"
TriggeredConditions can also be set to bToggled True. This means they will toggle between activated and deactivated each time an Event matching their Tag is fired. As opposed to bTriggerControlled, TriggerConditions set to bToggled True have the unique ability of retaining the condition in memory. Because it will stay in either the activated or deactivated state after the Event, a TriggeredCondition set in this way is able act like one Bit of memory, retaining either an on or off state. An ACTION_IfCondition acts the same way towards this TriggeredCondition, but now this can serve a very different purpose.
An example use for a TriggeredCondition set to bToggled would be any instance where a script will need to act one way if an Event has been fired, but act a different way if the Event hasn't. Say a script is used as the "brains" of a control panel. When the player activates a button, this control panel is meant to flash a green TriggerLight if the area has been undisturbed, or a red TriggerLight if an enemy has ever entered the area. A simple Trigger can be used to detect the presence of an enemy in the area. A TriggeredCondition (set to bToggled) will listen for the Event that Trigger fires and switch from deactivated to activated. Now, whenever the player hits the button on the control panel, a ScriptedTrigger will run a short script that checks the condition and either lights up the red or green light temporarily. The script might look like this:
 ACTION_WaitForEvent ("ButtonPushed")  ACTION_IfCondition ("EnemyDetected")  ACTION_GotoAction (6)  ACTION_EndSection  ACTION_TriggerEvent ("GreenLight")  ACTION_GotoAction (0)  ACTION_TriggerEvent ("RedLight")  ACTION_GotoAction (0)
In this case, the script listens for the button to be pushed, then checks the TriggeredCondition with the Tag, "EnemyDetected". As long as the Trigger set up to detect the enemy hasn't fired, this should cause the TriggerLight with the Tag, "GreenLight", to be turned on. After either case, the script loops back to wait for the next button push.
Using various actors within Trigger Systems
Complex Trigger Systems are rarely comprised of just Trigger classes. Often times, other actors are used if they are capable of either listening for Events and reacting or firing Events based on certain factors.
Several actors will indicate to the level designer that they may be used as part of a Trigger System by their name, such as TriggerLight. But, sometimes an actor's ability to be triggered or to trigger another actor is not obvious. It is up to the level designer to research actors to see if they can be used as part of a Trigger System. Looking for certain functions within the code helps; such as Trigger() or TriggerEvent().
One very important aspect of using various actors within a Trigger System is keeping track of how the Instigator is handled. Some actors are capable of relaying the Instigator to the next actor, some are not. One clear indication of this is whether a Pawn was able to initiate the Event chain. If a ScriptedTrigger is simply looping at a regular interval and fires an Event according to a random condition, there is no Instigator. Some cases are not so clear. A Matinee may be initiated by a player, however the Matinee's SubAction_Trigger will not be able to relay that player Pawn as the Instigator to other actors. Level designers must playtest and use trial and error to make their Trigger Systems reliable in that respect.
Movers are often used in Trigger Systems. Level designers are encouraged to use the Mover Events group of properties to trigger various Events during the Mover operation. Also, Movers set to TriggerOpenTimed, TriggerAdvance, etc. can themselves be triggered. Buttons, Valves and Levers are examples of "lead in" Movers in a Trigger System, which will fire Events for other actors. Doors, Lifts and Trap items are examples of "lead out" Movers, which will listen for Events and move. If a player's Pawn initiates the Mover, as in a lift set to StandOpenTimed, the Mover will relay that Pawn as the Instigator to other actors.
Movers have an extra ability that make them particularly useful in some very complex Trigger Systems. Movers have the ability to move in and out of a Trigger actor's CollisionCylinder, enabling them to indirectly cause both trigger and untrigger Events. This is a unique feature. In some cases where the Trigger System needs an element to deliver both trigger and untrigger Events, using a Trigger actor set to TT_ClassProximity and ClassProximityType Mover in conjunction with a small, hidden Mover with two keys (one outside the CollisionCylinder, one inside), is the only way to achieve the desired results.
Pawns (player pawns, bot pawns, vehicles, turrets, monsters, NPCs, etc.) are controlled by players or AI agents and are often an integral part of Trigger Systems. A Pawn who dies (or is destroyed) will fire it's Event. If another Pawn has actually killed (or destroyed) that Pawn, it is considered the Instigator of that Event. Also, an AI-controlled Pawn, such as a Bot, NPC or Monster, will listen for Events that match it's Tag and make any valid Instigator of the Event their enemy. This is useful in several situations related to singleplayer mapping.
PlayerStarts will fire their Event whenever a player is spawned or respawned at it. The player Pawn becomes the Instigator of that Event. TriggeredPlayerStarts are a special subclass that are capable of being toggled from enabled to disabled by listening for an Event that matches it's Tag.
Emitters can be triggered by setting Trigger->bTriggerDisabled to False and matching the Emitter Tag to the Event in question. Other trigger-related properties can be found under Emitter->Trigger, Emitter->Local, Emitter->Spawning, etc.
Matinees have a special SubAction (an action within a Matinee Action), called SubAction_Trigger, which will simply fire an Event at a specified time. Although an Event fired in this way has no Instigator, this SubAction is very useful for various starting various effects within a Matinee sequence.
Warning: Matinees do not run on Servers (online, over a LAN or even locally). So, if your Trigger System should run properly whether the Matinee runs or not, make sure you instead use a method other than SubAction_Trigger. Example: Use a separate ScriptedTrigger that simply waits for the same Event as the Matinee, then waits the appropriate time before firing the desired Event via ACTION_TriggerEvent.
GameObjectives and gametype-specific Events
Many GameObjectives are capable of firing Events. CTF FlagBases will fire their Event when a flag is picked up. A BombingRun BombdDelivery will fire it's Event when a score is made.
Many gametypes trigger various Events related to the state of the match being played. All gametypes fire an "EndGame" Event at the end of the match. Assault fires a few different Events, depending on whether an intro cutscene is being played, at the end of the match, etc.
This section is reserved for posting specific examples of Trigger Systems.
Note: For the sake of helping as many other wiki users as possible, please limit examples to those that are most generalized and applicable to many situations. A specific, idiosynchratic Trigger System that only helped in one or a unique situation map (and can't be useful to others) should be omitted from posting.
Triggered Lift (Triggered Elevator)
This example will show a simple two-part Trigger System where a lift is controlled by a trigger the player can hit.
Two elements are used in this Trigger System: the lift (a Mover) and the trigger actor. The lift can be set up with a couple Keys, one at the lower position and one at the higher position. Other Mover properties such as MoveTime, StayOpenTime, etc. are set. The Mover is then set to Object -> InitialState = TriggerOpenTimed to make sure this lift expects to be triggered in order to move. The Mover is given a unique Tag that will be matched with our Trigger actor's Event property. A Trigger actor is placed at the location of some obvious "lift button" decoration, where its collision cylinder is accessible by a player standing on the lift.
That's pretty much it. The Mover here does most of the work, being set to TriggerOpenTimed. The Trigger actor set to NormalTrigger simply activates the Mover and sends it on its way.
For this variation, the only difference is how the Trigger actor is activated. Instead of expecting to be Touched, as in when the player Pawn enters its collision cylinder, we would like our Trigger actor to activate when the player shoots it. We place the Trigger actor in the location of our "shoot me"-looking decoration and set it to Trigger -> TriggerType = TT_Shoot and set the DamageThreshold, the amount of damage needed to be delivered at one time in order for this Trigger to activate.
Now, when the player shoots into the collision cylinder of our Trigger actor, it will activate our Mover as normal.
This example will show how a button can control an alarm with flashing lights and sound.
Three elements will come together to make this Trigger System: a Mover, a TriggerLight and a ScriptedTrigger.
A button will be made with a Mover set to InitialState->BumpButton, making it move when the player bumps into it. It will also be set to bTriggerOnceOnly = True and bToggleDirection = False, just as a measure to make the button press once (key 1) and stay in, as opposed to returning to key 0 after a certain time. The MoverEvent->OpenedEvent will be set to "Alarm".
A flashing light will be made with a TriggerLight. It will be set to Object->InitialState = TriggerToggle, to make it toggle on/off when triggered, LightColor->LightSaturation = 64, to make it a deep red, and Lighting->LightType = LT_Pulse, just to make it flash a little. The TriggerLight's Tag will be set to "AlarmLight".
The whole Alarm will be controlled by a ScriptedTrigger. The ScriptedTrigger will listen for the Event fired by the button Mover ("Alarm"), then it will trigger the TriggerLight and make a buzzing sound to complete the alarm effect. The buzzing sound will be made by the ScriptedTrigger itself, using a looping series of ScriptedActions to play a one second sound, wait, play the sound again, etc. The ScriptedTrigger's script will look like this:
 ACTION_WaitForEvent ("Alarm")  ACTION_TriggerEvent ("AlarmLight")  ACTION_PlaySound ("buzz")  ACTION_WaitForTimer (2.0)  ACTION_GotoAction (2)
To make a variation of the above Trigger System that will allow the player to press the button to toggle the Alarm on and off, the same elements are used with some minor tweaks and one addition: a TriggeredCondition.
The Mover will be kept at the default bTriggerOnceOnly = False and bToggleDirection = True.
The ScriptedTrigger will have some variation in its script to accomodate a check for the TriggeredCondition's status:
 ACTION_WaitForEvent ("Alarm")  ACTION_TriggerEvent ("AlarmLight")  ACTION_PlaySound ("buzz")  ACTION_WaitForTimer (2.0)  ACTION_IfCondition ("Alarm")  ACTION_GotoAction (2)  ACTION_EndSection  ACTION_TriggerEvent ("AlarmLight")  ACTION_GotoAction (0)
The crucial addition to this variation is the TriggerCondition, which will be given the Tag "Alarm" and set to bToggled = True. This TriggeredCondition will also hear the "Alarm" Event, along with the ScriptedTrigger. The TriggeredCondition will be toggled on and keep the loop going to make the buzz sound. When the button is pressed a second time and the Event "Alarm" is fired again, the TriggeredCondition will be toggled off and the script loop will instead toggle the TriggerLight off and wait for the next "Alarm" Event.
Clue / Hint Decoration
This example will show how to set up a decoration that a player can approach, hit the Use key, and see an image overlay. This is a common device in singleplayer games, showing hints or clues from books, papers, computer readouts, etc.
Aside from the decoration that invites the player to take a closer look (a book, a computer monitor, etc.), two elements are used to make this Trigger System work: a UseTrigger and a ScriptedTrigger.
The UseTrigger is given a unique Event name ("Clue") and placed at the decoration.
The ScriptedTrigger handles the remainder of the Trigger System. It listens for the UseTrigger Event before playing a sound relevant to the decoration (paper rustling for a book or letter, a "beep" for a computer display, etc.), then it uses ACTION_DrawHUDMaterial to display the clue image for specified time. When that time has elapsed, the image is automatically cleared and the ScriptedTrigger continues by playing a second sound (same as before to "bookend" the effect) and looping back to again wait for the player to hit the Use key.
The ScriptedTrigger's script would look like this:
 ACTION_WaitForEvent ("Clue")  ACTION_PlaySound ("beep")  ACTION_DrawHUDMaterial (the clue image material, screen location, image scale, time duration)  ACTION_PlaySound ("beep", lower pitch)  ACTION_GotoAction (0)
This variation allows the user to either toggle the image or leave the vicinity of the decoration and allow the image to disappear automatically. Note: This variation is not perfect, as there is some "flicker" visible as the image is removed momentarily during the loop in this revised script. However, it is more important to show how this variation is constructed for academic purposes.
This variation is more complex, adding two TriggeredConditions and a Trigger actor. These added elements will allow the script to detect both the player proximity and the clue toggle status.
A normal Trigger is given an Event name of "PlayerPresent" and placed at the same location as the UseTrigger.
Two TriggeredConditions are used: one set to bToggled and given the Tag "Clue", the other set to bTriggerControlled and given the Tag "PlayerPresent".
The ScriptedTrigger's script is altered drastically to allow two conditional checks and control the script flow to keep activating the ACTION_DrawHUDMaterial as long as the player is both in proximity and has toggled the clue on.
 ACTION_WaitForEvent ("Clue")  ACTION_PlaySound ("beep")  ACTION_IfCondition ("PlayerPresent")  ACTION_GotoAction (7)  ACTION_EndSection  ACTION_TriggerEvent ("Clue")  ACTION_GotoAction (11)  ACTION_IfCondition ("Clue")  ACTION_DrawHUDMaterial (the clue image material, screen location, image scale and a 1 second time duration)  ACTION_GotoAction (2)  ACTION_EndSection  ACTION_PlaySound ("beep", lower pitch)  ACTION_WaitForTimer (1.0)  ACTION_GotoAction (0)
This script handles two consecutive conditions, making sure both are true in order for there to yeild a positive result that causes the ACTION_DrawHUDMaterial to loop and appear to stay on beyond its 1 second duration. After the initial "Clue" Event, which is fired by the player via the UseTrigger, the initial sound is played to start this script off. The first conditional, "PlayerPresent", if true will lead to the second. Otherwise, this script assumes the player has left, resets the "Clue" toggled condition, plays the ending sound and loops back to the beginning to wait for the next time a player activates the UseTrigger. The second conditional, "Clue", if true will execute the ACTION_DrawHUDMaterial step for 1 second before looping back to the first conditional check. If false, the second conditional is simply skipped, so the ending sound will play before the script loops back to the beginning to wait for another "Clue" Event to be fired by the UseTrigger. The ACTION_WaitForTimer step (step #12) is there to avoid an infinite script recursion error.
- An Example Puzzle Trigger System - posted on UnrealPlayground forums by SuperApe
- Another ScriptedAction Explanation - posted on UnrealPlayground forums by SuperApe
- Event, Tag and Match These Tags
- Types of Trigger
- Artificial Intelligence
- Creating and Using ScriptedActions
- RandomTrigger (custom)
SuperApe: Done. What do you think?
Kedren: Not just done but really well done. Thank you =) As an almost-total newbie, though, could you write a tutorial for a triggered lift? I'm thinking of the ones you used to get in Duke Nukem 3D where you had to shoot a button to make the lift move... surely that's feasible in Unreal? I'd be really grateful. But thank you for all your work: it's really good =)
SuperApe: Thanks for the compliment. :) A shoot-triggered lift tutorial? Well, perhaps this can go under the Examples section above. (...) That should do it. It turns out to be a good "starter" Example. Thanks for the suggestion.
Kedren: Thank you. That's just what I needed. :) *applause*