Ok. This is becoming annoying. I have made this script (below) which organises a sword (not a tool its attached to your arm with motor6d). Every once in a while it randomly one shots. I think its because of debounce not working, yet ive added many more things to make sure it runs ONCE PER SWING. Im really not sure what to do. Sorry for large amount of code but i wanted to make sure people understand my entire code. If anyone could point out a problem or anything it would be appreciated. This script is under each of the players sword
local function hitBox(character)
print(debounce, swing)
task.wait(0.25)
sound:Play()
print("so next line glitches it")
local physicalBox = Instance.new("Part", character)
print("Made")
physicalBox.Name = "Hitbox"
physicalBox.Transparency = 1
physicalBox.Anchored = false
physicalBox.CanCollide = false
physicalBox.Size = Vector3.new(5.5, 4, 5.5)
physicalBox.Massless = true
physicalBox.CFrame = character.HumanoidRootPart.CFrame * CFrame.new(0, 0, -3.5)
physicalBox.Parent = character.HumanoidRootPart
local weld = Instance.new("WeldConstraint")
weld.Part0 = character.HumanoidRootPart
weld.Part1 = physicalBox
weld.Parent = character.HumanoidRootPart
game.Debris:AddItem(weld, 0.5)
game.Debris:AddItem(physicalBox, 0.5)
local debounce = false
local affected = {} -- Track players already hit during a single swing
physicalBox.Touched:Connect(function(hit)
if debounce then
return
end
debounce = true (regular debounce)
damageCount += 1 -- incase it runs twice(which it still does)
wait()
if damageCount > 1 then return end
if hit.Parent and hit.Parent:IsA("Model") and hit.Parent:FindFirstChild("Humanoid") then
if hit.Parent == character or character:WaitForChild("IsRagdoll").Value == true then
debounce = false -- reset debounce
damageCount = 0
return
end
local player = game.Players:FindFirstChild(hit.Parent.Name)
if player and player:FindFirstChild("Bool") and player.Bool.Invincible.Value == true then
debounce = false -- Reset debounce before returning
damageCount = 0
return
end
-- makes sure player hasnt been hit in this swing (still doesnt have an effect)
if table.find(affected, player.Name) then
debounce = false -- Reset debounce before returning
damageCount = 0
return
end
table.insert(affected, player.Name)
-- Apply damage
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then
humanoid:TakeDamage(12)
end
-- Update stats and apply ragdoll effect
local attacker = game.Players:FindFirstChild(character.Name)
if attacker and attacker:FindFirstChild("leaderstats") then
attacker.leaderstats.Hits.Value += 1
end
swordRagdoll(hit.Parent, character)
else
debounce = false -- reset
damageCount = 0 -- reset
end
end)
end
script.Parent.SwordCombatRemote.OnServerEvent:Connect(function(player)
if swingCheck == false then
swingCheck = true -- stops swing being used again
swingCount += 1
if swingCount > 1 then return end
local char = player.Character
if char.IsRagdoll.Value == true then
swingCount = 0
swingCheck = false
return
end
local function stopSwing()
wait()
swingCheck = false
swingCount = 0
debounce = false
damageCount = 0
end
local Hum = char:WaitForChild("Humanoid")
if Combo == 1 then
playAnim = Hum:LoadAnimation(anim)
playAnim:Play()
Combo = 2
hitBox(char)
elseif Combo == 2 then
playAnim = Hum:LoadAnimation(anim2)
playAnim:Play()
Combo = 1
hitBox(char)
end
playAnim.Stopped:Connect(stopSwing)
end
end)
if table.find(affected, player.Name) then
debounce = false -- Reset debounce before returning
damageCount = 0
return
end
To this:
if table.find(affected, player.Name) ~= nil then
debounce = false -- Reset debounce before returning
damageCount = 0
return
end
Explanation:
Table.find returns nil if the subject is not found, and likewise if there is something it will return the number of the instance found. Using an if statement without operators is not advised in this case as it does not know what its looking for (or the condition its looking for to base on the premise of “true” or “false”), by adding that condition; ~= (means if the table.find does not return nil meaning the PLAYER has been AFFECTED), it will run the code and return.
Conclusion: I hope this helps, if not I will look into the code furthermore and see if I can see the real problem.
Also a question I would like to ask, Is this an AOE kind of damage, or it damages the first player it comes into contact with.
If it is the second then disconnect the function instead of returning.
Reference: RBXScriptConnection | Documentation - Roblox Creator Hub
Or just do what @RocketSlither said.
The issue seems to stem from the debounce logic, specifically where it interacts with other parts of the script, leading to inconsistent behavior and allowing multiple damage applications. Below, I’ll break down potential issues and suggest fixes:
Key Problems Identified:
Duplicate debounce Variable:
The local debounce inside the Touched event handler conflicts with the debounce declared at a higher scope. This creates separate states, which can result in unexpected behavior.
Race Conditions:
Multiple executions of the Touched event handler due to simultaneous or rapid collisions could bypass the intended safeguards.
damageCount Increment Logic:
While damageCount is used to limit damage, its checks and resets (if damageCount > 1) may not always prevent multiple damage applications due to timing issues.
Touched Events Are Triggered by Anything:
The Touched event might be triggered by unintended objects (e.g., parts other than players), which could lead to unexpected behavior.
Fixes and Optimizations:
1. Global Debounce
Use a single debounce for the entire hitBox function and reset it after the swing ends. This ensures that the state is consistent across all parts of the function.
2. Better Player Filtering
Avoid relying on table.find for every swing. Instead, use a dictionary (affected[player.Name] = true) for faster lookups.
3. Handle Touched Robustly
Filter out unwanted touches using stricter conditions, such as checking for a valid Humanoid.
4. Stop Damage Race Conditions
Include safeguards to ensure damage is applied only once per swing.
Here’s the improved code:
local function hitBox(character)
print(debounce, swing)
task.wait(0.25)
sound:Play()
local physicalBox = Instance.new("Part", character)
physicalBox.Name = "Hitbox"
physicalBox.Transparency = 1
physicalBox.Anchored = false
physicalBox.CanCollide = false
physicalBox.Size = Vector3.new(5.5, 4, 5.5)
physicalBox.Massless = true
physicalBox.CFrame = character.HumanoidRootPart.CFrame * CFrame.new(0, 0, -3.5)
physicalBox.Parent = character.HumanoidRootPart
local weld = Instance.new("WeldConstraint")
weld.Part0 = character.HumanoidRootPart
weld.Part1 = physicalBox
weld.Parent = character.HumanoidRootPart
game.Debris:AddItem(weld, 0.5)
game.Debris:AddItem(physicalBox, 0.5)
-- Use a global debounce for this hitbox
if debounce then return end
debounce = true
local affected = {}
physicalBox.Touched:Connect(function(hit)
-- Ensure debounce is active
if not debounce then return end
-- Filter valid hits
if hit.Parent and hit.Parent:IsA("Model") and hit.Parent:FindFirstChild("Humanoid") then
if hit.Parent == character or character:FindFirstChild("IsRagdoll").Value then
return
end
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
if not player or (player:FindFirstChild("Bool") and player.Bool.Invincible.Value) then
return
end
-- Prevent duplicate hits for this swing
if affected[player.Name] then
return
end
affected[player.Name] = true
-- Apply damage
local humanoid = hit.Parent:FindFirstChild("Humanoid")
if humanoid then
humanoid:TakeDamage(12)
end
-- Update stats and apply ragdoll
local attacker = game.Players:GetPlayerFromCharacter(character)
if attacker and attacker:FindFirstChild("leaderstats") then
attacker.leaderstats.Hits.Value += 1
end
swordRagdoll(hit.Parent, character)
end
end)
-- Reset debounce after 0.5 seconds
task.delay(0.5, function()
debounce = false
end)
end
script.Parent.SwordCombatRemote.OnServerEvent:Connect(function(player)
if swingCheck then return end
swingCheck = true
local char = player.Character
if char.IsRagdoll.Value then
swingCheck = false
return
end
local function stopSwing()
swingCheck = false
debounce = false
end
local Hum = char:WaitForChild("Humanoid")
if Combo == 1 then
local playAnim = Hum:LoadAnimation(anim)
playAnim:Play()
Combo = 2
hitBox(char)
elseif Combo == 2 then
local playAnim = Hum:LoadAnimation(anim2)
playAnim:Play()
Combo = 1
hitBox(char)
end
playAnim.Stopped:Connect(stopSwing)
end)
What Changed?
Global Debounce: debounce is now global to the hitBox function and resets only after the swing ends.
Dictionary for affected: Faster lookups for players hit during the swing.
Stricter Filtering: Ensures only valid player models are processed.
Reset Timing: Debounce resets consistently using task.delay, ensuring predictable behavior.
If a global debounce variable is called outside of the local function, it would affect all other debounces for other players doing the attack as they would all interfere with each other causing multiple functions to call upon a variable accessed by them.
Global Debounce: debounce is now global to the hitBox function and resets only after the swing ends.
Use a single debounce for the entire hitBox function and reset it after the swing ends. This ensures that the state is consistent across all parts of the function.
You’re using a server Script I am assuming since your calling a remote function, this script is handling all other player’s “swings”, making the debounce global, and by global he means to define the variable outside the functions, this allows that function and other functions to access it, however let’s say 2 players are swinging, and one of them hit a player first, then the function would change the debounce, tricking the other hitbox function thinking it too has also been hit since the debounce would be true/false.