Dash system fix

Hey guys, I need some help with ma code. The script does the dashing really well. I can press double W, D, A or S to dash front, back, right, and left. But what i need help in is the cooldown, rn i can dash infinetly and its really goofy. What i also need some help is the dash sound, how can i add a sound when dash?

local Character = script.Parent;
local Humanoid = Character:WaitForChild("Humanoid");
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart");

local SideGyro = Instance.new("BodyGyro")
SideGyro.MaxTorque = Vector3.new(3e5,0,3e5)
SideGyro.P = 5e5
SideGyro.Parent = HumanoidRootPart

local RunService = game:GetService('RunService')
local UserInputService = game:GetService('UserInputService')

local Dashing = false	
local DashSpeed = 45	
local DoubleTaps = {2}

local Animations = {
	["DashRight"] = 'rbxassetid://14168214362';
	["DashLeft"] = 'rbxassetid://14168217110';
	["DashFront"] = 'rbxassetid://14168318057';
	["DashBack"] = 'rbxassetid://14168210966';
}

for i,v in pairs(Animations) do
	local Anim = Instance.new("Animation")
	Anim.AnimationId = v
	Anim = Humanoid:LoadAnimation(Anim)
	Animations[i] = Anim
end

function Dash(Key)
	if Dashing then
		return
	end

	local DashAnim = "Back"
	local Front = -1
	local Side = 0

	if Key == Enum.KeyCode.A then
		Side=-1
		Front = 0
		DashAnim = "Left"
	end

	if Key == Enum.KeyCode.D then
		Side=1
		Front = 0
		DashAnim = "Right"
	end

	if Key == Enum.KeyCode.W then
		Side=0
		Front = 1
		DashAnim = "Front"
	end

	if Key == Enum.KeyCode.S then
		Side=0
		Front = -1
		DashAnim = "Back"
	end

	Dashing = true
	Animations["Dash"..DashAnim]:Play()

	local DashVelocity = Instance.new("BodyVelocity")
	DashVelocity.MaxForce = Vector3.new(math.huge,0,math.huge)
	DashVelocity.P = 9e9
	DashVelocity.Parent = HumanoidRootPart
	DashVelocity.Velocity = HumanoidRootPart.CFrame.LookVector * (Front * DashSpeed) + HumanoidRootPart.CFrame.RightVector * (Side * DashSpeed) 

	coroutine.wrap(function()
		task.wait(1.5)
		local DashEffect = game.ReplicatedStorage.DustTrail:Clone();
		DashEffect.Parent = workspace;

		if DashAnim == "Left" then
			DashEffect.DustRight:Destroy();
			DashEffect.CFrame = Character["Right Leg"].CFrame * CFrame.new(-1,0,-2);
		else if DashAnim == "Right" then
				DashEffect.DustLeft:Destroy();
				DashEffect.CFrame = Character["Left Leg"].CFrame * CFrame.new(3,2,5);
			else if DashAnim == "Front" then
					DashEffect.DustLeft:Destroy();
					DashEffect.CFrame = Character.HumanoidRootPart.CFrame * CFrame.new(0,-1,3);
				else
					DashEffect.DustLeft:Destroy();
					DashEffect.CFrame = Character.HumanoidRootPart.CFrame * CFrame.new(0,-1,-.5);
				end
			end
		end

		for i,v in pairs(DashEffect:GetDescendants()) do
			if not v:IsA("ParticleEmitter") then continue end
			v:Emit(5);
		end

		game.Debris:AddItem(DashEffect, .5);
	end)()


	repeat wait() 	DashVelocity.Velocity = HumanoidRootPart.CFrame.LookVector * (Front * DashSpeed) + HumanoidRootPart.CFrame.RightVector * (Side * DashSpeed) 
	until not Animations["Dash"..DashAnim].IsPlaying
	Dashing = false
	DashVelocity:Destroy()
end

function LoadDoubleTap(Key,Funct)
	DoubleTaps[Key] = tick()-10
	UserInputService.InputBegan:Connect(function(Pressed,Typing)

		if Pressed.KeyCode == Key then
			if (tick() - DoubleTaps[Key]) < 1.5 then
				Funct(Key)
			else
				DoubleTaps[Key] = tick()
			end
		end

	end)
end

LoadDoubleTap(Enum.KeyCode.W,Dash)
LoadDoubleTap(Enum.KeyCode.A,Dash)
LoadDoubleTap(Enum.KeyCode.S,Dash)
LoadDoubleTap(Enum.KeyCode.D,Dash)

3 Likes

Hello! I am not an expert or anything, however I will try and be helpful. I recommend moving a lot of stuff ( as much as you can as in the speed body velocity all that over onto server side ). I recommend this because I think ( to my knowledge ) exploiters can edit the cool-downs or velocity since it is on client side.

Anyways, once it is on the server side ( can be done via remote events ) you can try the following:

local playerCooldownList = {}

game.Players.PlayerRemoving:Connect(function(plr)
	playerCooldownList[plr] = nil
end)

remoteEvent.OnServerEvent:Connect(function(plr)
       local now = tick()
       local lastUsed = playerCooldownList[plr] or 0
       if now - lastUsed > 1.5 then -- replace the 1.5 with the time you want your cd to be
             playerCooldownList[plr] = now
             -- continue your code
       end
end)

Hopefully this helps, if not I can try to come up with a better solution!

The player’s movement is handled by the player, not by the server, for example exploiters can teleport around. Your suggestion wouldn’t give any benefit, unless you used a resource like this.

I am quite confused on what you mean. I know that they ( the players ) all have ownership over the network for them rather than the server ( if those are right terms, I am not very smart about the network service on Roblox ). I am still confused how this would mess up the movement. Wouldn’t it not matter if you have actually moved the player from the server, it seems much more secure as well.

Here, this should be a good solution. It works perfectly for me and I migrated most of it over to the server.

Client Side
local Character = script.Parent;
local Humanoid = Character:WaitForChild("Humanoid");
local event = game:GetService("ReplicatedStorage"):WaitForChild("DashHandler")
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart");
local RunService = game:GetService('RunService')
local UserInputService = game:GetService('UserInputService')
local DoubleTaps = {2}


function Dash(Key)
	local DashAnim
	
	if Key == Enum.KeyCode.A then
		DashAnim = "A"
	end

	if Key == Enum.KeyCode.D then
		DashAnim = "D"
	end

	if Key == Enum.KeyCode.W then
		DashAnim = "W"
	end

	if Key == Enum.KeyCode.S then
		DashAnim = "S"
	end
	
	event:FireServer(DashAnim) -- Fires the server with proper animation key.
end

function LoadDoubleTap(Key,Funct)
	DoubleTaps[Key] = tick()-10
	UserInputService.InputBegan:Connect(function(Pressed,Typing)

		if Pressed.KeyCode == Key then
			if (tick() - DoubleTaps[Key]) < 1.5 then
				Funct(Key)
			else
				DoubleTaps[Key] = tick()
			end
		end

	end)
end

LoadDoubleTap(Enum.KeyCode.W,Dash)
LoadDoubleTap(Enum.KeyCode.A,Dash)
LoadDoubleTap(Enum.KeyCode.S,Dash)
LoadDoubleTap(Enum.KeyCode.D,Dash)
Server Side
local playerCooldownList = {}
local anims = {
	["A"] = "A", -- Replace these with your animations!
	["S"] = "S",
	["W"] = "W",
	["D"] = "D"
}

game.Players.PlayerRemoving:Connect(function(plr)
	playerCooldownList[plr] = nil -- If the player leaves, we simply set their spot to nil.
end)

game:GetService("ReplicatedStorage"):WaitForChild("DashHandler").OnServerEvent:Connect(function(plr, dashAnim)
	
	if typeof(dashAnim) ~= type(string) then return end -- Checks if someone fired the remote event with an invalid type for dashAnim trying to find vulnerabilities
	
	local now = tick()
	local lastUsed = playerCooldownList[plr] or 0 -- Gets the time our player has last used dash, or if they haven't we set it to 0
	if now - lastUsed > 1.5 then
		playerCooldownList[plr] = now -- Add them to the cooldown list
		
		local neededAnim = anims[dashAnim] -- Get our dash animation
		local playerCharacter = plr.Character
		local hrp = playerCharacter:WaitForChild("HumanoidRootPart")
		local debris = game:GetService("Debris")

		local BV = Instance.new("BodyVelocity", hrp)
		BV.MaxForce = Vector3.new(50000, 0, 50000)
		BV.Name = "DashVelocity"

		BV.Velocity = playerCharacter.Humanoid.MoveDirection * 50

		game.Debris:AddItem(BV, 0.3)
		
		print("animation: " + neededAnim)
	end
		
end)

Hopefully this works for you, if any issues occur just let me know!

( I, for some reason, can not upload the video here, so here is a link: 2023-07-25 17-16-52 )

now why exactly would u handle dashing on the server?

I believe it is more secure then directly handling it on the client, especially the cool-downs. To my knowledge, anything on the client side is insecure and should be kept away as most as possible ( just in general not even Roblox ). So, for example, they could edit the Body Velocity if it was on the client ( high maybe ), however I know they could probably bypass the cool-down if it was on the client side. I don’t really have much knowledge when it comes to Roblox security, it just seems the right way to handle it in my mind. Please let me know otherwise though!

it’s not more secure, security doesn’t matter since the client can just do the same thing; your acting like exploiters can’t just make a new body velocity instead of spamming dash, it’s better to just handle it on the client instead of handling it on the server since the client’s movement is much smoother

1 Like

Don’t want to be that guy but this belongs in #help-and-feedback:scripting-support. This makes it easier for people like me to find your post.

Anyways, for the sound, you would just need to do something like this.

local sound = --put sound location here

local Character = script.Parent;
local Humanoid = Character:WaitForChild("Humanoid");
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart");

local SideGyro = Instance.new("BodyGyro")
SideGyro.MaxTorque = Vector3.new(3e5,0,3e5)
SideGyro.P = 5e5
SideGyro.Parent = HumanoidRootPart

local RunService = game:GetService('RunService')
local UserInputService = game:GetService('UserInputService')

local Dashing = false	
local DashSpeed = 45	
local DoubleTaps = {2}

local Animations = {
	["DashRight"] = 'rbxassetid://14168214362';
	["DashLeft"] = 'rbxassetid://14168217110';
	["DashFront"] = 'rbxassetid://14168318057';
	["DashBack"] = 'rbxassetid://14168210966';
}

for i,v in pairs(Animations) do
	local Anim = Instance.new("Animation")
	Anim.AnimationId = v
	Anim = Humanoid:LoadAnimation(Anim)
	Animations[i] = Anim
end

function Dash(Key)
	if Dashing then
		return
	end

	local DashAnim = "Back"
	local Front = -1
	local Side = 0

	if Key == Enum.KeyCode.A then
		Side=-1
		Front = 0
		DashAnim = "Left"
	end

	if Key == Enum.KeyCode.D then
		Side=1
		Front = 0
		DashAnim = "Right"
	end

	if Key == Enum.KeyCode.W then
		Side=0
		Front = 1
		DashAnim = "Front"
	end

	if Key == Enum.KeyCode.S then
		Side=0
		Front = -1
		DashAnim = "Back"
	end
        sound:Play
	Dashing = true
	Animations["Dash"..DashAnim]:Play()
--rest of code here

Put the sound in StarterPlayer so it plays from the player.

Hey! I see you’re having some trouble with your dash script. There are a few issues with the implementation you chose and I’d love to help you with them! I rewrote your script to include the features you requested as well as some general optimizations:

Code
-- Written by AverageLua

local ContextActionService = game:GetService("ContextActionService")
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Character = script.Parent
if not Character then
	warn("Could not find character, please place dash script in StarterCharacterScripts!")
	return
end

local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
local Animator = Character:WaitForChild("Humanoid"):WaitForChild("Animator")

local DashVelocityMagnitude = 45 -- dash speed
local DashCooldown = 3 -- cooldown for dashing
local DashSoundId = "rbxassetid://0" -- insert dash sound id

local CurrentlyDashing = false
local LastDashClock = 0

local ButtonPressTimings = {
	W = 0,
	A = 0,
	S = 0,
	D = 0
}

local ButtonDirectionMap = {
	W = Vector3.new(0, 0, -1),
	A = Vector3.new(-1, 0, 0),
	S = Vector3.new(0, 0, 1),
	D = Vector3.new(1, 0, 0)
}

local ButtonAnimationMap = {
	W = "rbxassetid://0",
	A = "rbxassetid://0",
	S = "rbxassetid://0",
	D = "rbxassetid://0",
}

local VelocityAttachment = Instance.new("Attachment", HumanoidRootPart)
local DashLinearVelocity = Instance.new("LinearVelocity")
DashLinearVelocity.Enabled = false
DashLinearVelocity.MaxForce = 1e5
DashLinearVelocity.Attachment0 = VelocityAttachment
DashLinearVelocity.RelativeTo = Enum.ActuatorRelativeTo.Attachment0
DashLinearVelocity.Parent = HumanoidRootPart

for Button, AnimationId in ButtonAnimationMap do
	local TemporaryAnimation = Instance.new("Animation")
	TemporaryAnimation.AnimationId = AnimationId
	ButtonAnimationMap[Button] = Animator:LoadAnimation(TemporaryAnimation)
	TemporaryAnimation:Destroy()
end

local function PlayDashSound()
	local NewDashSound = Instance.new("Sound")
	NewDashSound.SoundId = DashSoundId

	if not NewDashSound.IsLoaded then
		NewDashSound.Loaded:Wait()
	end
	NewDashSound.Parent = HumanoidRootPart

	NewDashSound.Ended:Once(function()
		NewDashSound:Destroy()
	end)
	NewDashSound:Play()
end

local function Dash(DirectionKey)
	if CurrentlyDashing or os.clock() - LastDashClock < DashCooldown then
		return
	end
	CurrentlyDashing = true

	if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
		-- Players who are not mouse locked can only dash forward.
		DirectionKey = "W"
	end

	local DashDirection = ButtonDirectionMap[DirectionKey]
	local DashAnimationTrack = ButtonAnimationMap[DirectionKey]
	DashAnimationTrack:Play()

	PlayDashSound()

	DashLinearVelocity.VectorVelocity = DashDirection * DashVelocityMagnitude
	DashLinearVelocity.Enabled = true

	task.delay(DashAnimationTrack.Length, function()
		DashLinearVelocity.Enabled = false
		DashLinearVelocity.VectorVelocity = Vector3.zero

		CurrentlyDashing = false
		LastDashClock = os.clock()
	end)
end

local function HandleAction(ActionName, InputState, InputObject)
	if InputState == Enum.UserInputState.Begin then
		local ButtonPressDelta = os.clock() - ButtonPressTimings[ActionName]
		ButtonPressTimings[ActionName] = os.clock()

		if ButtonPressDelta < .15 then
			Dash(ActionName)
		end
	end
	return Enum.ContextActionResult.Pass
end

ContextActionService:BindAction("W", HandleAction, false, Enum.KeyCode.W)
ContextActionService:BindAction("A", HandleAction, false, Enum.KeyCode.A)
ContextActionService:BindAction("S", HandleAction, false, Enum.KeyCode.S)
ContextActionService:BindAction("D", HandleAction, false, Enum.KeyCode.D)

(Place this code in a LocalScript within StarterCharacterScripts. Modify the variables at the top of the script to customize dash speed, dash cooldown, and the sound played when dashing. Also be sure to set AnimationIds within the ButtonAnimationMap table.)

There are a number of details I changed in the updated version. For starters, I opted to use ContextActionService instead of UserInputService for key press registration as it’s considered better practice. I also changed some logic for the Dash function code to make it simpler and easier to read. I added the dash sound and dash cooldown functionality you mentioned in your original post as well. Players who are not mouse locked can only dash forwards, you can remove this functionality by commenting out the if statement on line 80.

Note: I didn’t include the code that does the VFX when dashing to make the script usuable for more people. If you’d like to include this functionality, add this code on line 84:

Code
-- Written by AverageLua

task.delay(1.5, function()
	local DashEffect = ReplicatedStorage.DustTrail:Clone()
	DashEffect.Parent = workspace

	if DirectionKey == "A" then
		DashEffect.DustRight:Destroy()
		DashEffect.CFrame = Character["Right Leg"].CFrame * CFrame.new(-1, 0, -2)
	elseif DirectionKey == "D" then
		DashEffect.DustLeft:Destroy()
		DashEffect.CFrame = Character["Left Leg"].CFrame * CFrame.new(3, 2, 5)
	else
		DashEffect.DustLeft:Destroy()

		if DirectionKey == "W" then
			DashEffect.CFrame = Character:GetPivot() * CFrame.new(0, -1, 3)
		else
			DashEffect.CFrame = Character:GetPivot() * CFrame.new(0, -1, -.5)
		end
	end

	for _, Object in DashEffect:GetDescendants() do
		if Object:IsA("ParticleEmitter") then
			Object:Emit(5)
		end
	end

	task.delay(.5, DashEffect.Destroy, DashEffect)
end)