Movement features
A character has one movement component. When every plugin brought its own movement class, traversal, swimming and climbing could not be used together. The module PulseMovement gives them one component to share: UPulseCharacterMovementComponent (Pulse Character Movement), a Character Movement Component that carries movement features (UPulseMovementFeature). Pulse plugins add their kind of movement as a feature (PulseTraversal: Traversal); you add the features a character needs to its movement.
Use it
- In C++:
SetDefaultSubobjectClass<UPulseCharacterMovementComponent>(ACharacter::CharacterMovementComponentName)in the constructor of your character, then add the features toFeatures(in the constructor withObjectInitializer.CreateDefaultSubobject<UMyFeature>(Movement, TEXT("MyFeature")), so the movement is their outer). - In a Blueprint: set Character Movement Component Class of the character to Pulse Character Movement (class settings of a Blueprint character, or a C++ parent that does it), then add features in Features of the movement.
- Your own movement class derives from
UPulseCharacterMovementComponentinstead ofUCharacterMovementComponent.
Server and clients must have the same features in the same order: the order gives the custom modes and the input bits.
What the component does for its features
| Part | How it works |
|---|---|
| Custom modes | Each feature asks for GetNumCustomModes modes. The component hands them out from First Feature Mode (default 10, the modes below stay free for the game), in the order of the features. PhysCustom of a feature runs while the character is in one of its modes (EnterCustomMode, IsInCustomMode). |
| Input | Each feature gets GetNumInputBits bits per move (at most 32 for all features). SetInput on the owning client, GetInput during the move on every machine. The bits travel with the move and come back for replays. |
| Move data | SerializeMoveStart is written before a move on the client and read before the same move on the server and before a replay on the client (for example the water time of a move). SerializeMoveResult is written after a move on the client and read before the move on the server (a hint such as the edge the client found). Only features that have data write it (HasMoveStart, HasMoveResult). |
| Corrections | SerializeCorrection carries the state of a feature from the server to a corrected client. The client reads it after it took the corrected position and before it replays its moves; OnCorrected follows. |
| Combining | Moves with feature input or data are never combined, and features block combining while their state changes with time (BlocksMoveCombining): combining runs the time of the pending move again. Such moves are also sent at once instead of being held back. |
| Other clients | Features are replicated subobjects of the component. Give them replicated properties (push model) for what other players should see. |
| Hooks | BeforeMovement, AfterMovement, HandleJump (use the jump input instead of jumping), AllowsJump, GetMaxSpeed, OnMovementModeChanged, InitializeFeature, BeginPlay, EndPlay. |
| Debugging | The snapshot category Movement lists the mode and the features; each feature adds its own sections (GatherDebugSnapshot). |
Writing a feature
Keep the rules of the feature in pure functions and the feature itself thin: it collects what the rules need (traces, the state of the character), runs them and applies the result. Then the rules have specs without a world, and another movement system could host the same rules later.
Things that break prediction silently:
- Everything a move depends on must be in the move: the input bits, the start data, the state the feature had when the move started. A value read from the world clock or from a frame counter differs between client and server.
- Entering a custom mode resets the jump input of the character (
ACharacter::OnMovementModeChanged). A feature that starts something fromHandleJumpalso sets an input bit, so the server and replays start it too. - State that counts down with time must block combining while it counts.