Making A Dash/Roll System Tutorial [Youtube]

My latest tutorial on character dashing!

19 Likes

I’ve seen the tutorial and it’s very good but is it possible to make a version without Promise module and maybe give the model so we can read the script easier ? Btw it’s a very good dash

3 Likes

You can. Promises act as wrappers around coroutines, offering more functionality. Particularly relevant to this tutorial is the ability to chain threads to execute in succession. The alternative is to return an event for completion, which would be fired when we reach target velo (pretty much just substitute promise resolves for event firings). This would be similar to tweens where you can subscribe to a .Completed event.

But I would recommend using promises here. Returning events in this context is quite repetitive, inflexible and awkward. Imagine each time you want to chain you have to create a new event. Here it’s as simple as just returning a promise. Having this uniform API makes it easier to cancel the promise chain and also to handle errors (I’m sure you can imagine an error scenario, perhaps we couldn’t complete the dash because the character wasn’t on the ground, etc.). See Promises and why you should use them.

As for providing a model, this is purposefully a community tutorial and not a resource. So the expectation is to go through the tutorial and take what you need! :slight_smile:

4 Likes

The dash is laggy , like if i want to dash but in a wall my character will just fling…

1 Like

mhmm, laggy is concerning, but if you only meant it flings the character when it hits a wall, then that is expected when using LinearVelocity in general. Imagine it’s trying to maintain some velocity while knocking against a wall every frame. For a solution, you can do a cast to see if it comes up against a wall and handle it appropriately from there.

See:

2 Likes

I know how to make dashing but still, you’re like the only dude who makes high quality video tutorials like the one on sliding. W

1 Like

There’s a few ways to fix this problem

First, the linearvelocity’s maxforce, lower it to around 30000 for a player.
Second, a bodygyro, the reason for using the deprecated one is because it works differently and fits what you want

This is an example, of the bodygyro and the maxforce, the actual dash itself isn’t as clean as the tutorial one.

function Dash(Direction, Force, Duration)
	if Character.HumanoidRootPart:FindFirstChild("VectorForce") then
		Character.HumanoidRootPart:FindFirstChild("VectorForce"):Destroy()
	end
	local vectorForce = Instance.new("LinearVelocity")
	local Torque = Instance.new("BodyGyro")
	Torque.Parent = Character.HumanoidRootPart
	Torque.MaxTorque = Vector3.new(math.huge,math.huge,math.huge)

	vectorForce.Parent = Character.HumanoidRootPart
	vectorForce.Attachment0 = Character.HumanoidRootPart.RootAttachment
	vectorForce.VelocityConstraintMode = Enum.VelocityConstraintMode.Line
	vectorForce.RelativeTo = Enum.ActuatorRelativeTo.World
	vectorForce.MaxForce = 30000

	vectorForce.LineDirection =  Character.HumanoidRootPart.CFrame[Direction]
	vectorForce.LineVelocity = (Force)

	game.Debris:AddItem(Torque,Duration+0.15)
	game.Debris:AddItem(vectorForce,Duration)
end
Dash("LookVector",45,0.3)

also one of the most important parts

Character.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false)
Character.Humanoid:SetStateEnabled(Enum.HumanoidStateType.FallingDown, false)
2 Likes

Hey, i tried using your physics logic to make a dash system but my character only goes around 5-10 studs, can i get some help, thanks!

module


	u = intial velocity
	v = final velocity
	dt/t = time
	s = distance
	a = acceleration

]]
-- // Services

local runservice = game:GetService("RunService")

-- // Modules

local module = {}
local Promise = require(script.Parent.Promise)

-- // Functions

function AccelerateLinear(lv:LinearVelocity,targetvelocity:Vector2,acceleration)
	
	local currentvelocity : Vector2 = lv.PlaneVelocity
	local deltaV : Vector2 = targetvelocity - currentvelocity -- Change in velocity
	
	local _,dt = runservice.Stepped:Wait() -- gotta change vecloity before frame renders
	
	local newaccel = deltaV/dt -- accelration required to reach target velocity in time
	
	local finalaccel = (newaccel.Magnitude < acceleration) and newaccel or newaccel.Unit * acceleration -- acceleration we will use

	return Promise.new(function(resolve,reject)
		
		local connection : RBXScriptConnection
		local frame_left = 1 -- how many frame left before we stop connection
		
		connection = runservice.Stepped:Connect(function(time,dt)
			
			-- Updating the velocity
			
			lv.PlaneVelocity += finalaccel * dt -- We add the velocity we supposed to add in 1 frame, v - u = a*t(u is 0 here)
			
			if (targetvelocity - lv.PlaneVelocity).Magnitude <= finalaccel.Magnitude * dt * frame_left then
				
				lv.PlaneVelocity = targetvelocity
				connection:Disconnect()
				resolve()
			end
		end)
		
	end)
end

function module:AccelerateDistanceLinear(targetvelocity:Vector2,acceleration_time,s,hrp)
	
	local atc = Instance.new("Attachment")
	atc.Parent = hrp
	
	local lv = Instance.new("LinearVelocity") 
	lv.MaxForce = math.huge
	lv.VelocityConstraintMode = Enum.VelocityConstraintMode.Plane
	lv.PlaneVelocity = Vector2.zero
	lv.Attachment0 = atc
	lv.RelativeTo = Enum.ActuatorRelativeTo.World
	lv.PrimaryTangentAxis = Vector3.xAxis
	lv.SecondaryTangentAxis = Vector3.zAxis
	lv.Parent = hrp
	
	local s_travelled = 0.5 * (targetvelocity.Magnitude) * acceleration_time -- s = 1/2 * at^2, a = v-u/t, u = 0
	local s_left = s - s_travelled
	local negative_acceleration = math.abs(-(targetvelocity.Magnitude^2)/2*s_left) -- v^2 - u^2 = 2as
	
	return AccelerateLinear(lv,targetvelocity,targetvelocity.Magnitude/acceleration_time):andThen(function()
		return AccelerateLinear(lv,Vector2.zero,negative_acceleration):andThen(function()
			atc:Destroy()
			lv:Destroy()
		end)
	end)
end

return module

local script module

local targetvelocity = 60
local dashdistance = 18


local direction = {
	ForwardDash = Vector3.new(0,0,1),
	BackDash = Vector3.new(0,0,-1),
	LeftDas =Vector3.new(-1,0,0),
	RightDash = Vector3.new(1,0,0),
}

-- // Functions

function module:Dash(plr:Player,humanoid:Humanoid,hrp)
	if candash then
		
		candash = false

		local movedirection = (humanoid.MoveDirection * Vector3.new(1,0,1)).Unit
		local animation = "ForwardDash"
		
		if humanoid.MoveDirection == Vector3.zero then
			
			movedirection= (hrp.CFrame.LookVector * Vector3.new(1,0,1)).Unit
			animation = "ForwardDash"
		
		elseif uis.MouseBehavior == Enum.MouseBehavior.LockCenter then
			
			local closest = math.huge
			
			for name,velocity in pairs(direction) do
				local difference = 1 - (hrp.CFrame * velocity):Dot(humanoid.MoveDirection)
				if difference < closest then
					closest = difference
					animation = name
				end
			end
		end
		
		print(direction)
		return Promise.new(function(resolve)
			animationhandler:Playanimation(plr.Character,animation)
			physicsstimulator:AccelerateDistanceLinear(targetvelocity * Vector2.new(movedirection.X,movedirection.Z).Unit,0.01,dashdistance,hrp)
			resolve()
		end):andThen(function()
			candash = true
		end)
	end
end```

That is strange. The only thing I see wrong perhaps is the debounce logic for canDash, since this promise will resolve immediately (assuming Playanimation doesn’t yield and just plays). You’d need to either wait on animation to end, AccelerateDistanceLinear to finish, or whichever one takes longer using Promise.all, then resolve.

Otherwise, if you don’t press the dash key while it’s already active (debounce logic wouldn’t matter in that case) then it should travel the correct distance. I could be overlooking a small detail, but as of right now my eyes don’t see anything wrong.

Thanks for this answer actually, this is the best and simplest way of going about it. If one wants to be methodical about how they set MaxForce exactly, the following code should work, it’s based on the maximum acceleration the hrp (or whatever you’re accelerating) will possibly experience.

LV.MaxForce = ((targetVelocity.Magnitude/accTime) * hrp.AssemblyMass) + 250

250 is arbitrary legroom, this way the force output to reach target velocity is capped and the physics engine doesn’t go beyond it in trying to maintain velocity going against an immovable object.

1 Like

Hey the dash works well but do you know how I could implement like the direction of the dash update every frames ?

Carried out discussion offline. Turns out OP had to rewrite (targetvelocity.Magnitude^2)/2*s_left as
(targetvelocity.Magnitude^2)/(2*s_left). Division and multiplication are of the same precedence (Programming in Lua : 3.5) so associativity takes over evaluating from left to right, unless we surround multiplication with parentheses.

I watched the video and finished the script ,but i really didnt know how to make the animation .So ,my character couldnt walk ,instead of playing the animation in a fixed place