Proximity- And Damage-Triggered Doors
This tutorial will show you a way to create doors that can open when shot or when a player approaches it. The door will close after some time if no player is near it anymore.
Using this type of door might help a lot when porting Quake maps. It might add a new tactical twist to your map since players now can't rely on the opponent being near a door when it opens. On the other hand, closing doors will not be as harmful for a fire fight anymore because the players can simply shoot it to instantly reopen it, even if the door hasn't finished closing yet.
Prerequisites
In this tutorial we will be creating a multi-part door. When any of the door parts is hit, the whole door should open, i.e. all parts should start moving at the same time. Unfortunalety, regular movers don't provide the neccessary functionality, so we need to use the custom DamageTriggerMover. Single-part doors with similar functionality are probably possible with a regular Mover as well, but will require a different trigger setup then.
Our door mechanism will consist of:
- two DamageTriggerMovers as the door parts (You can add more movers to your door, if you like. This trigger/mover system can be extended quite easily.)
- one Trigger to detect player proximity
- one TriggeredCondition to store the Trigger's state for...
- one ScriptedTrigger (1) that closes the door after some time when no player is nearby
- another ScriptedTrigger (2) that opens the door when a player touches the Trigger
Door Movers
Place your DamageTriggerMovers like you would place any regular mover. Set their Object → InitialState to TriggerToggle, match their ReturnGroup → ReturnGroup names and give them the same MoverEvents → DamageEvent names. It's probably also a good idea to set their Mover → MoverEncroachType to ME_CrushWhenEncroach or ME_IgnoreWhenEncroach, so they don't get out of sync when something blocks one of them.
Now you need to decide, which of your movers will be the leader of your door's ReturnGroup. This "leading mover" will receive the events for opening or closing the door. Set its Events → Tag to the event you want to control the door with. That event will be fired by the ScriptedTriggers and should be unique for each door. You also need to specify an event name in MoverEvents → OpenedEvent, that will be used by ScriptedTrigger (1) to find out, when to start checking whether the door should close again.
Example of mover properties so far:
Property | Leading Mover | Other Door Parts | |
Events → Tag | MyDoor | — | |
Mover → MoverEncroachType | ME_CrushWhenEncroach | ME_CrushWhenEncroach | You could also use ME_IgnoreWhenEncraoch here. |
MoverEvents → DamageEvent | MyDoorHit | MyDoorHit | |
MoverEvents → OpenedEvent | MyDoorOpened | — | |
Object → InitialState | TriggerToggle | TriggerToggle | |
ReturnGroup → bIsLeader | True | False | |
ReturnGroup → ReturnGroup | MyDoor | MyDoor | This is not associated with Tags/Events in any way, it's just convenient to use the same name as the leader's Tag. |
After setting up the movers as described above, you can playtest your map and the movers will do exactly... nothing.
If your movers activate when you run into them or shoot them, something is wrong! Make sure they are really DamageTriggerMovers and that their initial state really is TriggerToggle.
Door Controls
Let's add some functionality to the door. The basic door control will be handled by a ScriptedTrigger that opens the door when triggered and closes it again after some time. Player proximity is detected by a Trigger.
Open, Wait, Close
Add an Actor >> Keypoint >> AIScript >> ScriptedSequence >> ScriptedTrigger. It will control the door movers with a script that works like this:
door is closed or closing Wait for event MyDoorHit Trigger event MyDoor (to open the door) Wait for event MyDoorOpened door is open Wait 2 seconds If player is near door (starts a new subsection) Go to "door is open" (to wait a little longer) End subsection Trigger event MyDoor (to close the door) Go to "door is closed or closing"
The lines starting with bold words in the above algorithm can be translated directly into ScriptedActions for ScriptedTrigger (1):
Action | Parameter Name | Parameter Value | Comment | |
0 | ACTION_WaitForEvent? | ExternalEvent | MyDoorHit | waits for the door to be hit |
1 | ACTION_TriggerEvent? | Event | MyDoor | opens the door |
2 | ACTION_WaitForEvent? | ExternalEvent | MyDoorOpened | waits for the door to finish opening |
3 | ACTION_WaitForTimer? | PauseTime | 2 | wait 2 seconds |
4 | ACTION_IfCondition? | TriggeredConditionTag | MyDoorProximity | check, if player is near the door (see next section of this tutorial) |
5 | ACTION_GotoAction? | ActionNumber | 3 | go to action 3 ("wait 2 seconds") |
6 | ACTION_EndSection? | — | — | end of the IfCondition section |
7 | ACTION_TriggerEvent? | Event | MyDoor | close the door |
8 | ACTION_GotoAction? | ActionNumber | 0 | go to action 0 ("wait for the door to be hit") |
You can playtest the map again and the doors should already open when you shoot them, even though the If Condition in the script will probably throw an error because we haven't placed the specified TriggeredCondition yet. The door doesn't yet react to player proximity, so you can get crushed it you stand between the movers when they close.
Don't hurt me!
To tell the door that a player is still nearby, we add an actor >> Triggers >> Trigger to the doorway and adjust its collision size. If a cylinder doesn't really fit the area you need, add a volume with the desired shape and match its Volume → AssociatedActorTag with the Trigger's Events → Tag. Specify the Trigger's Events → Event (in our example "MyDoorProximity") and check that its Object → InitialState is NormalTrigger.
Add an actor >> Triggers >> TriggeredCondition and set its Events → Tag to the same as the Trigger's event (in our case "MyDoorProximity", again). Match its Events → Tag with the Trigger's Events → Event and set its TriggeredCondition → bTriggerControlled to True and the other properties in that group to false.
Summary of properties:
Property | TriggeredCondition | Trigger |
Events → Event | — | MyDoorProximity |
Events → Tag | MyDoorProximity | MyDoorProximityVolume (only when using a volume as described above) |
Object → InitialState | — | NormalTrigger |
TriggeredCondition → bEnabled | False | |
TriggeredCondition → bToggled | False | |
TriggeredCondition → bTriggerControlled | True |
After adding these two actors your damage-triggered door should already stay open when a player is nearby, but it still only opens when you shoot it.
Open up wide!
Our door is only missing a single feature: It doesn't open automatically when a player walks up close to it. We already have a Trigger that fires an event when a player touches it, so let's just use it to open the door.
Add another ScriptedTrigger and set it up to translate the player proximity event into the door hit event, which already opens the door properly:
Action | Parameter Name | Parameter Value | Comment | |
0 | ACTION_WaitForEvent? | ExternalEvent | MyDoorProximity | waits for a player to touch the trigger |
1 | ACTION_WaitForTimer? | PauseTime | 0.01 | In my tests the script managed to crash the game with an Infinite Recursion message – this prevented it from happening. |
2 | ACTION_TriggerEvent? | Event | MyDoorHit | triggers the other ScriptedTrigger, which opens the door |
3 | ACTION_GotoAction? | ActionNumber | 0 | go to action 0 ("wait for player touch") |
That's all! Your door should now open when it gets hit or when a player approaches it and will only close again if the player is no longer nearby.
Related Topics
Comments
FreshMeat: Would you kindly put some notes in here for those of us who only want to proximity trigger double doors? Trying to sort out the damage stuff from the proximity stuff has me confused.
Wormbo: Only proximity triggering can be done with regular movers and a much simpler setup. I think a good example of proximity-triggered movers is the UT2004 map [DM-Reconstruct] by AngelMapper, which uses it in a very unique way.
Tarquin: Indeed. IIRC, you just stick a Trigger to detect player presence, and change a few settings of the mover.