< Back 

Sword Masters: Dash

2024-09-15

Dash, the free ability every player starts with, throws you up to 50 studs forward. Under it there's an AlignPosition pulling the root towards a point ahead, an AlignOrientation locking the facing, and two ways to stop: touching an invisible marker at the destination or 0.4 seconds going by.

LuauRobloxGameplayPhysics
Go to quick view

A dash is two constraints and a timer.

Warning

Sword Masters was abandoned in October 2024, and most of its animations, sounds and images belong to the group that owned it, so the game can’t run as it did and this system can’t be shown on video.

Abstract. Dash is the ability every Sword Masters player starts with: you press E and the character shoots up to 50 studs in the direction it’s facing, with an animation, a sound and a burst of particles. It’s an instant ability in the sense of the ability system, so all of its work happens in the server’s Use. The movement comes from two physics constraints on the character’s root: an AlignPosition that pulls it towards a point 50 studs ahead and an AlignOrientation that keeps it facing the same way during the dash. The dash ends whichever comes first, the root touching an invisible marker placed at the destination or 0.4 seconds passing. This article goes through each piece, a detail about Euler angles that only works because characters stand upright, and God’s Dash, the admin variant that reuses the same code.

1. The problem

A dash has to feel instant, has to go where you were looking when you pressed and not where you turn halfway, and has to stop on its own. It also had to run on the server, like every ability in the game, but the character’s physics is simulated by the player’s client, which has its network ownership. Writing the position frame by frame from the server would fight against that ownership and look jittery for the person dashing.

Constraints avoid that problem. If the server puts an AlignPosition inside the character, the constraint replicates and is simulated by whoever owns the assembly, which is the player’s own client, so the person dashing sees a smooth movement, and everyone else sees the result through normal replication.

2. The destination

When Use runs, the server computes the destination once:

pend=p0+Dfˆ

where p0 is the root’s position, fˆ its look vector and D the ability’s MoveStuds, 50 for Dash. The look vector is captured at the instant you press, so turning the camera mid-dash doesn’t change the destination.

3. Pulling

DashTo is an AlignPosition stored as a template inside the module. The server clones it, sets the destination as its target position and attaches it to the root’s RootAttachment. Its Responsiveness comes from the ability’s MoveSpeed, 30. The data file calls it “studs per second”, but it isn’t a speed: it’s how stiffly the constraint tries to close the gap, and the real speed comes out of that combined with the force and velocity limits of the template.

The 0.4 s cut-off puts a ceiling on what the dash can cover. Getting all the way to 50 studs in that time takes an average of

v‾=500.4=125 studs/s

about eight times the default walk speed of 16. If the constraint doesn’t get there in time, the dash simply stops wherever it is at 0.4 s. A dash into a wall pushes against the wall until the time runs out, because the character’s collisions keep working while the constraint pulls.

4. Keeping the facing

The character already carries an AlignOrientation on its root. Dash sets its target to the root’s current orientation and enables it for the length of the dash, so the character can’t turn mid-air even if the player moves the camera or the stick:

local ao:AlignOrientation = hrp.AlignOrientation
ao.CFrame = CFrame.Angles(hrp.CFrame:ToOrientation());

That line works for a reason that isn’t obvious. ToOrientation returns angles (α,β,γ) meant for CFrame.fromOrientation, which applies the rotations in Y, X, Z order, while CFrame.Angles applies them in X, Y, Z order:

RYXZ=Ry(β)Rx(α)Rz(γ),RXYZ=Rx(α)Ry(β)Rz(γ)

They’re different rotations in general. The root of a standing character, though, only has yaw: α=γ=0, and with those two at zero both products are Ry(β). If the character were tilted when it dashed, the target orientation would come out slightly wrong. The correct inverse is CFrame.fromOrientation(hrp.CFrame:ToOrientation()), or simply the root’s own rotation.

5. Stopping

There are two exits. When it starts, the dash places an anchored, invisible, non-colliding part of 0.1 studs at the destination and listens to its Touched. If a part of the player’s own character touches it, the dash ends right there. And a task.delay(0.4, ...) ends it in any case. Both call the same function, which destroys the AlignPosition, disconnects the marker, destroys it and disables the AlignOrientation. It runs twice when the marker fires first, and destroying or disconnecting what’s already gone does nothing, so it doesn’t need a guard.

The marker only listens to the player who dashed. Another player walking through it doesn’t end someone else’s dash.

6. Animation and effects

Dash has two animations that alternate. The server keeps an AnimationState counter in the player’s data for this ability, plays the track at that position and advances it, wrapping back to the first one at the end of the list. Two dashes in a row play different animations, which reads as a lot less mechanical than repeating the same one.

The sound starts 0.4 s into the clip, skipping the attack, and every particle emitter under the root’s DashFX emits two particles that then fade, raising their transparency from 0.8 to 1.0 over 21 frames, about a third of a second at 60 fps. When the dash ends, the transparency goes back to its initial value for the next one.

7. God’s Dash

God’s Dash is the admin version and reuses the code with four changes. It goes 65 studs instead of 50, its destination is one stud higher, it has no cooldown, and it heals 10 health points while emitting the healing particles on top of its own. That it fits in four differences is the argument for having written Dash with its numbers in data and not in the code.

8. Limitations

There’s a dead piece. Dash also clones DashDirection, a LinearVelocity, and sets its LineDirection to (fz,0,−fx), the horizontal perpendicular to the look vector, but never parents it, so it does nothing before being destroyed.

The shared signature from the ability system shows up here too. Dash’s Use declares a third parameter, vfxName, to pick the effect, but the manager calls every Use with the same arguments, and the third one is the Loaded remote. The type(vfxName) == "string" check is what saves the call and falls back to "DashFX".

And, like every ability in the game, the 5-second cooldown is only checked on the client.

9. What I took away

Moving characters with constraints instead of writing positions from the server was the most important decision, and the network ownership argument from section 1 is what convinced me. The double exit, marker or timer, is also a pattern I’ve used again: an event ends the action when everything goes well and a timer guarantees it ends when it doesn’t. What I’d change is the naming. MoveSpeed isn’t a speed and MoveStuds is a distance the dash doesn’t always cover. With names like Responsiveness and MaxDistance, section 3 would have been unnecessary.

This text by Mastedore. All rights reserved.