How To Create a Non-Multi-Hit Sword With Custom Hitbox - Tutorial

Welcome to my tutorial on programming a sword that does damage when you swing it! You will also learn how to make a custom hitbox and how to prevent getting multiple hits on the same person.

Part A - Preparing the sword

The first step is to choose a sword model. I used the basic Roblox sword model. In the explorer window, your sword should look similar to this. It needs to be a tool with a handle; in this case, the handle is the part with the mesh. However, a mesh is not required

Screenshot 2024-09-16 at 4.26.56 PM

Now you can create the hitbox. Do this by inserting a part under the sword tool and naming it Hitbox, then putting it where you want the hitbox to be. Make sure it has can-touch on and can-collide off and use a weldConstraint to connect it to the rest of your sword. To create a weldConstraint, add it as an object to your Handle. Then in properties, set part1 to the handle, and part2 to the hitbox. The hitbox can be made transparent too.

The next step is to create animations. One will need to be made for both r6 and r15. First, create a rig using the rig builder in the avatar tab. Then place the sword tool in it like this:

Screenshot 2024-09-16 at 4.51.52 PM

Next, use the animation editor in the avatar tab to create and save a sword swing animation for r6 and r15. As this is a scripting tutorial, it won’t go into how to create animations, but many resources are available. Ensure their AnimationPriority is Action. Once you have your animations, create animation objects inside the sword tool and input the IDs in their properties.

Screenshot 2024-09-16 at 6.28.32 PM

Part B - Scripting the sword

To start, create a local script and a remote event called SwordSwingEvent as direct children of your sword. I’ll provide the code, which has comments in it if you are unsure what something does. This code is for the local script:

local sword = script.Parent -- Creates a variable that is the sword
local swingOnCooldown = false -- Creates a variable that is the cooldown for the sword
local SwordSwingEvent = sword.SwordSwingEvent -- Remote event to communicate with the server

local function swing() -- This function runs whenever the sword is activated
	if swingOnCooldown == false then -- If the sword isn't on cooldown
		swingOnCooldown = true -- Set it to be on cooldown
		local humanoid = sword.Parent:FindFirstChild("Humanoid") -- Find the player's humanoid
		local animator = humanoid:FindFirstChildOfClass("Animator") -- Find the player's animator

		local swingAnimation -- Create empty swingAnimation variable for later use
		
		if humanoid.RigType == Enum.HumanoidRigType.R6 then -- If the player is r6
			swingAnimation = sword:FindFirstChild("r6Animation") -- Use the r6 animation
		elseif humanoid.RigType == Enum.HumanoidRigType.R15 then -- If the player is r15
			swingAnimation = sword:FindFirstChild("r15Animation") -- Use the r15 animation
		end
		
		local SwingTrack = animator:LoadAnimation(swingAnimation) -- Load the swing animation into the player's animator
		SwingTrack:Play() -- Play the animation
		
		SwordSwingEvent:FireServer() -- Fires remote event to server script
	end

	wait(1) -- Wait 1 second
	swingOnCooldown = false -- Allow the player to swing again
end

sword.Activated:Connect(swing) -- Runs swing function when the player clicks while holding the tool

Next, add a server script under your sword tool. This is the server code, once again with comments.

local sword = script.Parent -- Creates a variable that is the sword
local hitbox = sword.Hitbox -- Creates a variable that is the hitbox
local SwordSwingEvent = sword.SwordSwingEvent -- Remote event to get a message from the client

local recentlyHit = {} -- Create array for recently hit targets

local function checkHumanoid(humanoid) -- Function to check if a specific humanoid is in the recently hit list
	if #recentlyHit == 0 then -- If nobody has been recently hit
		return false -- provides false value
	end
	for _, recentHumanoid in ipairs(recentlyHit) do -- Campare all values in the array to the humanoid
		if recentHumanoid == humanoid then -- If the humanoid appears on the array list
			return true -- provides true value
		end
	end
	return false -- In any other case provide false
end


local function doDamage(objectHit) -- Function to handle the damage triggered by hitbox getting touched
	local humanoid = objectHit.Parent:FindFirstChild("Humanoid") -- Look for humanoid
	if humanoid and checkHumanoid(humanoid) == false then -- If it has a humanoid and it's not been hit recently
		table.insert(recentlyHit, humanoid) -- Add it to recently hit array
		humanoid:TakeDamage(10) -- have humanoid take damage
	end
end

SwordSwingEvent.OnServerEvent:Connect(function() -- When server event is triggered
	local connection -- Create variable to hold the connection
	connection = hitbox.Touched:Connect(doDamage) -- turn on the connection to damage function
	wait(1) -- Wait 1 second
	connection:Disconnect() -- turn off the connection to damage
	recentlyHit = {} -- Empty the recently hit array
end)

Congratulations, you created a sword with a custom hitbox that hits each enemy only once per swing. Now all you have to do is place the sword in StarterPack to spawn with it. You can test it out by placing some rigs. Additionally, this link is to an uncopylocked version of the game: Sword Tutorial - Uncopylocked - Roblox. Hopefully, you found this tutorial helpful and if you need help, feel free to leave a comment!

8 Likes

W post, ive been trying to find out how to use hitboxes properly

I’m experiencing a problem with the cooldown. When I spam click, the first few clicks have a proper cooldown, but after that, the animation and damage trigger rapidly. Is there a way to fix this issue? or is it lag/studio issues? Edit: i moved the wait(1) and swingoncooldown = false into the if swingoncooldown == false then statement