Mover
A StaticMesh actor that can move between predefined key positions and rotations.
See Mover (UT) for the UT version of this class, which uses a brush instead of a StaticMesh.
Properties
Main
- name AntiPortalTag
- Tag of attached AntiPortalActor?.
- bool bDamageTriggered
- Triggered by taking damage
- bool bDynamicLightMover
- Apply dynamic lighting to mover.
- bool bOscillatingLoop
- Goes from 0 up to X then back down to 0
- byte BrushRaytraceKey
- Raytrace the brush here.
- bool bSlave
- This brush is a slave.
- bool bToggleDirection
- If this Mover is in the RotatingMover state, toggle directions between "Triggerings".
- bool bTriggerOnceOnly
- Go dormant after first trigger.
- name BumpEvent
- Optional event to cause when any valid bumper bumps the mover.
- EBumpType BumpType
- Determines what actors can bump-trigger the mover (see declaration for EBumpType below).
- bool bUseShortestRotation
- Rot by -90 instead of +270, etc. If end key was 360 and start key is 0, this means it won't have to rotate at all.
- bool bUseTriggered
- This Mover is Triggered by any Touching player who presses the "Use" key.
- float DamageThreshold
- Minimum damage to trigger
- float DelayTime
- Delay before starting to open
- int EncroachDamage
- How much to damage encroached actors. Note: This damage is taken by encroached actors before any MoverEncroachType reaction is applied.
- byte KeyNum
- Current or destination keyframe.
- EMoverEncroachType MoverEncroachType
- Determines how the mover reacts if encroached by another actor (see declaration for EMoverEncroachType below).
- EMoverGlideType MoverGlideType
- GlideByTime will interpolate between keys by starting out slow, building speed to the halfway point, then slowing down until stopped at the next key. MoveByTime will interpolate linearly, at a constant speed between keys.
- float MoveTime
- Time to spend moving between keyframes.
- byte NumKeys
- Number of keyframes in total (0-3).
- float OtherTime
- TriggerPound's StayOpenTime.
- name PlayerBumpEvent
- Optional event to cause when the player bumps the mover.
- float StayOpenTime
- How long to remain open before closing. (unless TriggerPound is used, see OtherTime)
- byte WorldRaytraceKey
- Raytrace the world with the brush here.
AI
- bool bAutoDoor
- Automatically setup Door NavigationPoint for this mover
- bool bNoAIRelevance
- Don't warn about this mover during path review
MoverEvents
- name ClosedEvent
- Event to cause when closed
- name ClosingEvent
- Event to cause when closing
- name LoopEvent
- Event to cause when the mover loops
- name OpenedEvent
- Event to cause when opened
- name OpeningEvent
- Event to cause when opening
MoverSounds
- sound ClosedSound
- When finish closing.
- sound ClosingSound
- When start closing.
- sound LoopSound
- Played on Loop
- sound MoveAmbientSound
- Optional ambient sound when moving.
- sound OpenedSound
- When finished opening.
- sound OpeningSound
- When start opening.
ReturnGroup
- bool bIsLeader
- This Mover is the leader of a return group of movers.
- name ReturnGroup
- The return group this mover belongs to. All movers in a return group form a linked list of "followers" with each mover's Leader pointing to the main mover that should be triggered, bumped, or otherwise activated. The leading mover will tell its Follower to open/close whenever it opens or closes itself. The follower in turn will do the same with its own follower, and so on. Make sure only one mover in a return group is flagged as the leader, otherwise you might experience weird stuff including "Infinite Script Recursion" crashes.
Hidden
- KeyRot[24]
- KeyPos[24]
- array<AntiPortalActor?> AntiPortals
- vector BasePos
- rotator BaseRot
- bool bClientPause
- bool bDelaying
- bool bOpening
- bool bPlayerOnly
- int ClientUpdate
- Mover Follower
- Next mover in the linked list of movers belonging to a "return group".
- Mover Leader
- The primary mover of a "return group".
- NavigationPoint myMarker
- int numTriggerEvents
- Number of times triggered ( count down to untrigger )
- vector OldPos
- vector OldPrePivot
- rotator OldRot
- float PhysRate
- Interpolation rate per second.
- byte PrevKeyNum
- Previous keyframe.
- vector RealPosition
- rotator RealRotation
- vector SavedPos
- rotator SavedRot
- Actor SavedTrigger
- The Actor that triggered this Mover.
- vector SimInterpolate
- vector SimOldPos
- int SimOldRotPitch
- int SimOldRotRoll
- int SimOldRotYaw
States
- OpenTimedMover
- Master State for OpenTimed mover states (for movers that open and close between two keys)
- StandOpenTimed (extends OpenTimedMover)
- Open when stood on, wait, then close.
- BumpOpenTimed (extends OpenTimedMover)
- Open when bumped, wait, then close.
- TriggerOpenTimed (extends OpenTimedMover)
- When triggered, open, wait, then close.
- LoopMove
- When triggered, the mover keeps looping through each of the keys until it gets untriggered. It will go to the first key after the last key.
- TriggerToggle
- Toggle between two keys when triggered.
- TriggerControl
- Start opening immediately when Triggered, begin closing immediately if UnTriggered. Keep a constant speed, which is based on the MoveTime and the distance between the two keys.
- TriggerPound
- Can be thought of as acting like the ["Ring The Bell"] carnival attraction. You "pound" (trigger) it to the top (open key) and it will fall back down. (closed key) When triggered, begin to Open immediately, and if left alone, Open completely and stay open for OtherTime, not StayOpenTime. If Untriggered, begin to Close immediately, similar to TriggerControl, and as if the "pound" didn't have enough strength to make to the top and ring the bell. Unlike TriggerControl which keeps a constant speed, TriggerPound will always use the MoveTime no matter where the Mover is between keys. So, if Triggered while closing or if UnTriggered in the midst of opening, it will reverse direction and move more slowly to the next key. (Note: Although the code appears to indicate that a TriggerPound Mover can loop from a fully Closed key after StayOpenTime if more Trigger events have been sent to it than UnTrigger events, it doesn't appear to loop in any situation tested.)
- TriggerAdvance
- (UT200x only?) When Triggered, move toward the Open key. If UnTriggered, stop moving.
- BumpButton
- Open when bumped, close when reset.
- ConstantLoop
- Loop this mover from the moment we begin. It will go to the first key after the last key.
- LeadInOutLooper
- A Looping move that goes from 0 to 1 when trigger, loops 1-x then returns to 0 when triggered again.
(For Scripters: Goes to state LeadInOutLooping when triggered and back to LeadInOutLooper when triggered again.)
- RotatingMover
- Uses Movement → RotationRate. It rotates when triggered and stops rotating when untriggered. If bToggleDirection is true, it toggles rotation directions when triggered.
Enums
EMoverEncroachType
- ME_StopWhenEncroach
- Stop when the mover hit an actor.
- ME_ReturnWhenEncroach
- Return to previous position when the mover hit an actor.
- ME_CrushWhenEncroach
- Crush the poor helpless actor.
- ME_IgnoreWhenEncroach
- Ignore encroached actors.
EMoverGlideType
- MV_MoveByTime
- Move linearly; a constant speed determined by the distance between the keys and the MoveTime.
- MV_GlideByTime
- Move with smooth acceleration; starting out slow, building speed towards the half way point, then slowing down to a stop at the next key, all within the MoveTime.
EBumpType
- BT_PlayerBump
- Can only be bumped by player.
- BT_PawnBump
- Can be bumped by any Pawn (UT).
- BT_AnyBump
- Can be bumped by any solid actor.
Known Subclasses
- ClientMover
- DamageTriggerMover (custom)
- VariableTimedMover (custom)
Related Topics
- The Mover Topics page
- Types of Trigger and Event
Discussion
EricBlade: A question. In Land of the Dead (UE 2226, roughly UT2003 base code, it seems), movers do not replicate properly. I see there is a bunch of code in Mover, ActionableMover, and DestructableMover to accomodate replication, but it all doesn't seem to work. Would anyone be interested in taking a look at the code, to see if there's something that can be done to fix it without subclassing and reworking every single mover in the entire codebase?
SuperApe: From what I know, it seems that the bNoDelete setting is the primary reason for proper Mover replication. This sets the Mover object itself to replicate to all Clients. So besides the replication block in Mover, you may need to confirm that your engine version is set up to act the same way in that regard.
EricBlade: It turns out, that apparently the actual location/rotation of these movers is not replicated properly.. what the effect is, say you have a door that opens and closes. If the door's normal state is closed, but you open that door .. then another player logs in, they get the door in the original position on their client, even though it is in the open position on the server. This gives you a door that appears open, but is actually closed. Except that it's actually appearing in a third position, the exact opposite of it's normal rotation. It's very weird.
Xian: Well bNoDelete Actors will not be Spawned or Destroyed by the script. It is not responsible for replication DIRECTLY, as is bNetOptional for example, or Role/RemoteRole properties, but apparently the server will delete Server-Side only actors from the Client on game start (not sure if in the PostBeginPlay() session of Engine.GameInfo or earlier. bNoDelete will make the Engine assign the necessary Role/RemoteRole value on Client and Server. Yeah I guess I see what you mean now, you can say it is responsible for replication, but wouldn't say it's the primary reason. You can just as well use bStatic I guess, although apparently Movers don't seem to want it (even though a Brush is bStatic...)
What EricBlade said makes sense, but I am curious how long it takes. I am not that interested in UE2, but in UE1 Timer() is responsible for updating the Client to the Server position, so the first few seconds you join will have a wrong position, although it should be updated later on. Perhaps doing a ClientSetLocation() where you set the position and rotation would be helpful in this case, but I do admit I am too lazy to test, and since I don't have an UE2 dev folder it will be kinda difficult, but I suggest you try it
SuperApe: Thank you for elaborating the long answer for me.
EricBlade: Not to bring up an oldish subject, but I didn't notice there were some answers here. Although the code for Mover says "Movers default to bNoDelete==true", that was not the case in my actual code. I set bNoDelete=true to the Door movers in the game, and now they appear in the right place when you load the level, but then a few moments later, they either completely disappear, or jump to FSM-only-knows where in the world, apparently on the timer when the position is replicated. Weeeeird.
Sweavo: "Pound Open when triggered. Pound Closed when untriggered." what on earth does it mean "to pound"? There is no other reference to pounding in this context on the wiki
Tarquin: yeah, the TriggerPound stuff is a mystery
Xian: Rough skimming tells me that one difference is that there is no Trigger notification (i.e. calling EndEvent() on the triggering actor) and that the movement loops. Also, you can see that there is no Stop command after Open, but there is, however, a Sleep, and there is a GoTo command after Close which calls the Open label (making a looping session). Although I am really lazy to make a test map (or edit one) to check it, to me it seems like once a Mover with this state is triggered it will cause an Open-Close, Open-Close, Open-Close loop "animation". Considering it belong to UnrealI, I never played it, but I assume one use could be, for example, in a factory, where you have those crushing thingies, f.e. a conveyor belt, and at a point stuff on the belt gets smashed by this, then incinerated or whatever, like a trap-quest, and you'd have to find the right timing to get past it (which explains the name Pound). Just a thought, but if any of you kind people would test, I might get a confirmation I'm not sure but these are all assumptions after checking the code.
SuperApe: Making a test map to test TriggerPound took all of two minutes. That and after looking through the code more carefully, I think the best way to consider this Mover state is like that of a carinval attraction: "Ring the Bell" or ["High Striker"]. This is where you use a large mallet to strike a see-saw to propel a weight up a pole, trying to get it to the top to hit a bell. (thus, "Pound") When a Mover set to TriggerPound is triggered, it will Open and Close and stop (like you hit the weight hard enough to go to the top, ring the bell and fall back down). If it is Untriggered during this sequence is will begin to Close immediately, like TriggerControl (also like if you didn't hit the weight hard enough to go to the top of the pole). Unlike TriggerControl, TriggerPound will use the same MoveTime and alter it's Speed to travel whatever distance it needs to Open when triggered, or to Close when Untriggered. IOW, TriggerControl will always move at a constant Speed when Opening or Closing, no matter where it is in that movement between the keys, but TriggerPound will move more slowly to the Open key if it is triggered inbetween the keys, as if you added extra force ("pound") during it's Close movement and you interrupted it's momentum to send it back Open. TriggerPound uses a method of recording how many Trigger and Untrigger events it recieves.
Xian: Except that it loops (or should according to the code). What is IOW btw ? Well I am sorry if my description was not 100% accurate (except maybe 15%-20%), but it's better that you tested.
SuperApe: A Mover set to TriggerPound does not loop. It works as I describe above. It was tested as well as confirmed in the code. IOW = In Other Words.
Xian: According to this...
Open: Disable ('Trigger'); DoOpen(); FinishInterpolation(); FinishedOpening(); // If the next key frame is not the last, then // keep playing back the frames. // if (Keynum < NumKeys-1) { GotoState ('GradualTriggerOpenTimed', 'Open'); } Close: Disable ('Trigger'); DoClose(); FinishInterpolation(); FinishedClosing(); // throw the current Event, if exists if (KeyNum > 0) // Still more key-frames to go through GotoState ('GradualTriggerOpenTimed', 'Close'); Sleep(StayOpenTime); if( bTriggerOnceOnly ) { AmbientSound = None; GotoState(''); } if( SavedTrigger != None ) goto 'Open'; }
... it does. At least in theory. As you can see, if the pointer to the SavedTrigger is still there, it loops to Open. Perhaps because it was not triggered using both params (Other and Instigator) it didn't loop in your case, but rest assured, it does loop. There are situations of calling Trigger(None,X) (where X is the Instigator) so I am not surprised if it doesn't happen in all cases.
SuperApe: We're obviously looking at two different versions of code. In UT200x, a Mover has no "GradualTriggerOpenTimed" state and the only time Trigger() is disabled is after a bTriggerOnceOnly check has been made. (Incidentally, I see minor differences between the code in UT2003 and UT2004 to accomodate a Reset() function, but it behaves the same way in both.) I found nothing in the UT200x Mover code that matches the code block you posted. But regardless, the SavedTrigger is simply allowing the Mover to keep track of multiple Trigger and UnTrigger events sent to it. When Triggered, SavedTrigger = something, when UnTriggered an equal number of times, SavedTrigger = None. But that does not seem to allow a fully Closed Mover set to TriggerPound to loop back Open in any case, whether Triggered by a NormalTrigger or via causeevent console command (which gives the Mover more Trigger events than Untrigger events). The description I state above is based on both my interpretation of the UT200x code and the results of an empirical test of the Mover state in Ued. It wasn't a guess or theorem. Didn't you ask for a confirmation of your intial assesment?
Xian: I did It was pasted from UT. If you'll check the code there you'll see that, as I pasted, the movement gets looped If the SavedTrigger is still referenced (as we both said via triggering). Unfortunately code changed a lot it seems over versions, though I appologize for the confusion.
SuperApe: During a long drive, I had time to think about this and I was bothered by the code that seemed to loop, but didn't in my tests. Then I remembered the weird thing TriggerPound does with the timing properties: OtherTime is used in place of StayOpenTime with TriggerPound, and StayOpenTime is used as shown above, to sleep after the Mover is Closed all the way. I'm convinced you're right, it must loop. Although I am not at a machine I can test this, I'll bet I just didn't wait the default 4 seconds after it Closed to see it loop. However, it should only loop if the Mover was given more Trigger events than Untrigger events, either by causeevent console command, ACTION_TriggerEvent from an AIScript subclass, UseTrigger or the like. A single NormalTrigger will always give the Mover an equal number of Trigger and Untrigger events, so in that case it would never loop. I apologize for the confusion. I think we should be able to refactor this Mover state now.
Xian:
However, it should only loop if the Mover was given more Trigger events than Untrigger events
My point exactly Yeah np, I must have confused you a bit as well with the code above (forgetting to mention it is UT). Well trial and error is the key Hopefully we'll both get to the bottom of this.
SuperApe: Well, whattaya know. I did more testing, waited extra long times to be sure, but it actually never loops from the fully Closed key. Whether you give it more Trigger events than UnTrigger events, or not. Go figure. I am a little confused by the code now; it seems like it should loop in some instance, but it never appears to do so. So, I guess my analogy of the "ring the bell" carnival attraction describes the TriggerPound Mover state the best.
Xian: OK double checked both UE1 and UE2 and they are the same in the Close session. That is weird indeed. According to the code as long as one SavedTrigger pointer exists (as you said one extra trigger event, which most likely seems to be caused the easiest by CauseEvent) would cause it to loop. I do trust that you did it properly so I am confused as well now... the code makes my analogy correct but the way it works makes yours correct. I think I'll do some testing in UT once I get some free time... "this is too weird for school" as they say
SuperApe: I agree, the way the code makes it look, it is confusing. On the other hand, to me TriggerPound sounds like the way it is behaving, it sounds like a "ring the bell" attraction. BTW, it appears that a NormalTrigger is the only way to always get an even number of Trigger and UnTrigger events, but there are several ways a Mover can get just Trigger events along with the causeevent console command: ACTION_TriggerEvent, UseTrigger, other Mover Events, etc.
Sweavo: wow, great work guys! All I can offer is "is bTriggerOnceOnly set?" hope I'm not insulting you but it's all too common to miss something like that when you're too close to the task! I'm not able to get to the source at the moment to look for myself...
SuperApe: Not sure what you're asking, Sweavo. Anyway, this page has been refactored in terms of TriggerPound/TriggerControl (and TriggerAdvance), see above descriptions. We can probably remove this part of the discussion at least, if everyone is satisfied.
Xian: He's right. He means that if bTriggerOnceOnly was set then they won't react to other triggering effects. According to the code, if that is set it leaves that state completely. Would you mind testing with and without that var just to see why the code says one thing and the way it behaves is different?
Sweavo: You are saying "according to the code it should loop, but I don't see it looping"... so I added "the code doesn't loop if bTriggerOnceOnly is set," as Xian also said just there. Trouble is, I don't see bTriggerOnceOnly in the default properties for Mover or an of its super- or sub-classes. Weird.
SuperApe: My tests were with the default setting bTriggerOnceOnly=false. bTriggerOnceOnly=true does what you'd expect it to: that is, Trigger (and Untrigger, if Untriggered) once and immediately go to a dormant state no matter what Mover state you're using. Tests confirmed this.
Sweavo: OK so that's a blind alley then. As another aside I wonder why that default doesn't show up in my uscript refernece source tree.
SuperApe: Booleans are false by default. There's no need to declare that in defaultproperties.
Mychaeel: ...unless, of course, you're overriding the default inherited from some superclass (or making sure that you don't accidentally inherit some default other than false
).
Xian: Unfortunately this is not the case since this is the parent/base class.