(Not for beginners) Making advanced sword combat

NEW TUTORIAL DROPS ON THE 14TH (v day)!!

View Post for the old tutorial.

THERE ARE SOME ERRORS IN THE CODE WHERE I USE Humanoid:LoadAnimation() INSTEAD OF Humanoid.Animator:LoadAnimation(). HOWEVER THESE METHODS STILL WORK BUT CAN RESULT IN CODE MAYBE NOT WORKING PROPERLY.

Hello!
We will be teaching you how to make a advanced sword combat system.

But what is a combat system?

A combat system is that it includes punching, swords, or and can deal damage. These systems are usally found in games eg: ZO
Assets Required

:dagger: | Sword Model
:dagger: | Sword Trail
:dagger: | Sword Animations

Lets get started!

WARNING: Very long tutorial. Please make sure you did everything correctly.

Step 1 -- Making the sword model and the basics of Welding

So you've all seen your tools fall down like a cracker, right?
Say no more! Ungroup the model, drag it in the tool, DONT MOVE anything or else it will break!

And rename Meshes/Katana_Cube to Handle or you got no tool lol
Make a WeldConstraint in the tool then set Part0 to the Instance Handle and Part1 to the Instance Meshes/Katana_Cube.001

Play around with the name of Meshes/Katana_Cube.001, it wont break.
However, you might end up getting this, dont do anything else
image

Step 2: Animations

Paste a LocalScript in your tool, then make a folder named Sword then make another folder in it called "Animations". Put "Sword" in repliactedstorage! If it takes up too much time, write the following code in the command bar:
local memefolder = Instance.new("Folder", funnifolder) memefolder.Name = "Animations"

Now, you should end up with this
image

Paste the code in your tool:

local Tool = script.Parent
local Player = game.Players.LocalPlayer
local Char = game.Workspace:WaitForChild(Player.Name)
local RS = game.ReplicatedStorage


local Humanoid = Char:WaitForChild("Humanoid")
local Check = 0
local Enable = true
print (Player.Name)

Tool.Activated:Connect(function()
	local humanoid = script.Parent.Parent:WaitForChild("Humanoid")
	if Enable == true then
		Enable = false
		if Check == 0 then
			humanoid:LoadAnimation(RS.Sword.Animations["1"]):Play()
			wait(0.5)
			Check = 1
			print (Check) --However, we make sure that is it working normally by printing the Check value inside of the script.
			Enable = true
		else if Check == 1 then
				humanoid:LoadAnimation(RS.Sword.Animations["2"]):Play()
				wait(0.5)
				Check = 2
				print (Check) --However, we make sure that is it working normally by printing the Check value inside of the script.
				Enable = true
			else if Check == 2 then
					humanoid:LoadAnimation(RS.Sword.Animations["3"]):Play()
					wait(0.5)
					Check = 0
					print (Check) --However, we make sure that is it working normally by printing the Check value inside of the script.
					Enable = true
				end
			end
		end
	end
end)

while wait(5) do
	if Check == 1 or Check == 2 then
		Check = 0
	end
end

(decided to go crazy with coding)

" but it keeps saying “1 is not a vaild member of RepliactedStorage”
real mature
Make 4 animations in repliacted storage saying as the first one, 1
Then the second one, 2
Then the third one, 3
Then the fourth one, 4

Upload only animations Slash1, Slash2, Slash3, and Slash4, but DO NOT publish any more.


Now, paste Slash1’s animation asset ID in the Animations Folder 1 Animation Id → RepliactedStorage.Sword.Animations.1.AnimationId

Then, paste Slash2’s animation asset ID in the Animations Folder 2 Animation Id → RepliactedStorage.Sword.Animations.2.AnimationId

Then, paste Slash3’s animation asset ID in the Animations Folder 3 Animation Id → RepliactedStorage.Sword.Animations.3.AnimationId

Finally, paste Slash4’s animation asset ID in the Animations Folder 4 Animation Id–> RepliactedStorage.Sword.Animations.4.AnimationId

But you’ll end up with this:


Thats because we didnt add RemoteEvents!
Run the following in your cmd bar:

local memefolder = Instance.new("Folder", game.ReplicatedStorage.Sword) memefolder.Name = "Remotes"
local Slash = Instance.new("RemoteEvent", memefolder)
Slash.Name = "Slashing"

You should get this:
image

Now, update the Animation code:

local Tool = script.Parent
local Player = game.Players.LocalPlayer
local Char = game.Workspace:WaitForChild(Player.Name)
local RS = game.ReplicatedStorage


local Humanoid = Char:WaitForChild("Humanoid")
local Check = 0
local Enable = true
print (Player.Name)

Tool.Activated:Connect(function()
	local humanoid = script.Parent.Parent:WaitForChild("Humanoid")
	if Enable == true then
		Enable = false
		if Check == 0 then
			humanoid:LoadAnimation(RS.Sword.Animations["1"]):Play()
			RS.Sword.Remotes.Slashing:FireServer(script.Parent.Handle)

			wait(0.5)
			Check = 1
			print (Check) --However, we make sure that is it working normally by printing the Check value inside of the script.
			Enable = true
		else if Check == 1 then
				humanoid:LoadAnimation(RS.Sword.Animations["2"]):Play()
				RS.Sword.Remotes.Slashing:FireServer(script.Parent.Handle)

				wait(0.5)
				Check = 2
				print (Check) --However, we make sure that is it working normally by printing the Check value inside of the script.
				Enable = true
			else if Check == 2 then
					humanoid:LoadAnimation(RS.Sword.Animations["3"]):Play()
					RS.Sword.Remotes.Slashing:FireServer(script.Parent.Handle)

					wait(0.5)
					Check = 0
					print (Check) --However, we make sure that is it working normally by printing the Check value inside of the script.
					Enable = true
				end
			end
		end
	end
end)

while wait(5) do
	if Check == 1 or Check == 2 then
		Check = 0
	end
end

Woohoo! You just got your slash animations:

And now, let’s add a idle animation.
Make a Animation in ReplicatedStorage → Sword → Animations and call it “Idle”

And then, make a localscript in the tool with the code:

script.Parent.Equipped:Connect(function()
	animation = game.Players.LocalPlayer.Character.Humanoid.Animator:LoadAnimation(game.ReplicatedStorage.Sword.Animations.Idle)
	animation:Play()

	print("playing idle")
end)
script.Parent.Unequipped:Connect(function()
	animation:Stop()
	print("unequip detected, stopped animation")

end)

script.Parent.Activated:Connect(function()
	animation:Stop()
	wait (9)
	animation:Play()
	print ("animation is playing again")  
end)

Step 3: Creating the Equip Animations

Open your animations dummy with Animation Editor, then open "SwordEquip" After that, publish it to roblox and make a animation in ReplicatedStorage,Sword, then Animations. Name it "Equip_And_UnEquip", and paste the Equip Animation there.

Now put a LocalScript in your tool and name it “EquipAnimations”
The localscript’s code should be:

--This code is mine! So i didnt steal this entire code.
local Tool = script.Parent

local function onEquipped(_mouse)
	local animation = game.Players.LocalPlayer.Character.Humanoid.Animator:LoadAnimation(game.ReplicatedStorage.Sword.Animations.Equip_And_UnEquip)
	animation:Play()

end

Tool.Equipped:Connect(onEquipped)

Tool.Unequipped:Connect(function()
	local animation = game.Players.LocalPlayer.Character.Humanoid.Animator:LoadAnimation(game.ReplicatedStorage.Sword.Animations.Equip_And_UnEquip)
	animation:Play(0.100000001, 1, -1) -- Reverses Animation
end)

Now, we need to make our sword damage others. I’ll cover this in Step 4.

Step 4: Damaging the player

We need to search for a humanoid if the otherpart finds one, then uses the TakeDamage function. (Script below is not mine)
local tool = script.Parent
local canDamage = false
    
local function onTouch(otherPart)
  
	local humanoid = otherPart.Parent:FindFirstChild("Humanoid")
	local hrp = otherPart.Parent:FindFirstChild("HumanoidRootPart")

 
    if not humanoid then 
        return 
    end
 
    if humanoid.Parent ~= tool.Parent and canDamage then 
		humanoid:TakeDamage(10)


    else
        return
    end
 
    canDamage = false
end

local function slash()
    local str = Instance.new("StringValue")
    str.Name = "toolanim"
    str.Value = "Slash" 
    str.Parent = tool
    canDamage = true
end

tool.Activated:Connect(slash)
tool.Handle.Touched:Connect(onTouch)


Woohoo! You just made your first advanced sword combat!

But theres something missing.

Wanna control the damage value with a IntValue? Say no more!

Insert a “Configuration” in the, ( you know what i’m gonna say), ReplicatedStorage, then Sword.
Then, add a IntValue saying Damage and update the code you just wrote:

local tool = script.Parent
local canDamage = false
    
local function onTouch(otherPart)
  
	local humanoid = otherPart.Parent:FindFirstChild("Humanoid")
	local hrp = otherPart.Parent:FindFirstChild("HumanoidRootPart")

 
    if not humanoid then 
        return 
    end
 
    if humanoid.Parent ~= tool.Parent and canDamage then 
		humanoid:TakeDamage(game.ReplicatedStorage.Sword.Configuration.Damage.Value)


    else
        return
    end
 
    canDamage = false
end

local function slash()
    local str = Instance.new("StringValue")
    str.Name = "toolanim"
    str.Value = "Slash" 
    str.Parent = tool
    canDamage = true
end

tool.Activated:Connect(slash)
tool.Handle.Touched:Connect(onTouch)

Step 5: The sounds --Lets make some music!

It seems quiet in your lonely area-- why not some SOUNDS?!?!

https://www.roblox.com/library/10914398099/things
From the model up top, get it and remove the particles.
Keep the following:
image
Drag them all into the Sounds Folder in the Sword Folder in replicated storage, then update the animation code with this:

local Tool = script.Parent
local Player = game.Players.LocalPlayer
local Char = game.Workspace:WaitForChild(Player.Name)
local RS = game.ReplicatedStorage


local Humanoid = Char:WaitForChild("Humanoid")
local Check = 0
local Enable = true
print (Player.Name)

Tool.Activated:Connect(function()
	local humanoid = script.Parent.Parent:WaitForChild("Humanoid")
	if Enable == true then
		Enable = false
		if Check == 0 then
			humanoid:LoadAnimation(RS.Sword.Animations["1"]):Play()
			local swing = RS.Sword.Sounds.SwingSharp1:Clone()
			swing.Parent = game.Workspace
			swing:Play()
			game.Debris:AddItem(swing,5)
			wait(0.5)
			Check = 1
			print (Check) --However, we make sure that is it working normally by printing the Check value inside of the script.
			Enable = true
		else if Check == 1 then
				humanoid:LoadAnimation(RS.Sword.Animations["2"]):Play()
				local swing = RS.Sword.Sounds.SwingSharp1:Clone()
				swing.Parent = game.Workspace
				swing:Play()
				game.Debris:AddItem(swing,5)
				wait(0.5)
				Check = 2
				print (Check) --However, we make sure that is it working normally by printing the Check value inside of the script.
				Enable = true
			else if Check == 2 then
					humanoid:LoadAnimation(RS.Sword.Animations["3"]):Play()
					local swing = RS.Sword.Sounds.SwingSharp1:Clone()
					swing.Parent = game.Workspace
					swing:Play()
					game.Debris:AddItem(swing,5)
					wait(0.5)
					Check = 0
					print (Check) --However, we make sure that is it working normally by printing the Check value inside of the script.
					Enable = true
				end
			end
		end
	end
end)

while wait(5) do
	if Check == 1 or Check == 2 then
		Check = 0
	end
end

Step 6 --Your FX

It just dosen't give out the feel, why not change that with some FX?

Make a part (should look like this)
image
Add a attachment, then a particle emitter inside of it
Change the texture to rbxassetid://7185003058 and set the speed to 0.
Change the size to this by clicking on the three dots and try to get the size accurate to the one below.
image
Set the lifetime to 0.25, the Rate to 1, and your done


Now, make a folder in the sword saying “FX” and drag the attachment in there. You will see image
Dont worry! Take the Particle out of the attachment, then drag it in. Name it “HitFX”
Now, set the rate to 0 as we are gonna be using :Emit() and we dont want to get it stuck on the player.

Update the damage script code with the following:

local tool = script.Parent
local canDamage = false
    
local function onTouch(otherPart)
  
	local humanoid = otherPart.Parent:FindFirstChild("Humanoid")
	local hrp = otherPart.Parent:FindFirstChild("HumanoidRootPart")

 
    if not humanoid then 
        return 
    end
 
    if humanoid.Parent ~= tool.Parent and canDamage then 
		humanoid:TakeDamage(1)
		local slash = game.ReplicatedStorage.Sword.Sounds.Slash3:Clone()
		slash.Parent = hrp
		slash:Play()
		local hrpattachment = Instance.new("Attachment", hrp)
		local fx = game.ReplicatedStorage.Sword.FX.HitFX:Clone()
		fx.Parent = hrpattachment
		fx:Emit(1)
		game.Debris:AddItem(hrpattachment, 3)
		game.Debris:AddItem(slash, 6)


    else
        return
    end
 
    canDamage = false
end

local function slash()
    local str = Instance.new("StringValue")
    str.Name = "toolanim"
    str.Value = "Slash" 
    str.Parent = tool
    canDamage = true
end

tool.Activated:Connect(slash)
tool.Handle.Touched:Connect(onTouch)

Then, make two attachments positioned like this on the blade:
image
Add an trail onto it and name it “SwordTrail”
Set the Attachment0 to the first attachment you just made, and the Attachment1 to the second one you made on the blade.

The texture dosen’t look very nice, so grab out the sword trail texture (7509141236) and paste it in the “Texture” box.


As you can see-- It’s lasting forever! Change the lifetime in the lifetime trail box to 0.2
And finally, set the Enabled property of the trail to false. It will turn back on when we slash

Make another localscript and paste the code in the localscript:

script.Parent.Equipped:Connect(function(Mouse)
	Mouse.Button1Down:Connect(function()
		script.Parent.Handle.SwordTrail.Enabled = true
		wait (0.2)
		script.Parent.Handle.SwordTrail.Enabled = false
	end)
end)

Voila! Your first ever advanced sword combat is finished! Progress:


(this took me 3 hours to write)

24 Likes

Didn’t know how to make this until now, what makes the sword advanced though :thinking:

3 Likes

Lets get you started!
FX, the sword trails, equip animations

If not im gonna keep it i mean fix it

2 Likes

Yo this is really cool! I will most likely be using this! Thanks!

Yeah, no problem! Although i might be making this a resource…

1 Like

Well if you do I’ll definitely use it! :grin:

Could you provide some comments/description of what your code does? Not really a tutorial if you’re just telling people how to copy paste scripts.

3 Likes

Some of the scripts are mine some are not. They can write it themselfs if they want but if its long paste it

I don’t want to be mean but you are using a lot of either deprecated or outdated stuff in your code.

Such as wait instead of task.wait you are loading animations directly through humanoid instead of the Animator.

Another thing I would point out is else if instead of elseif (we are in Lua not in JavaScript), make your capitalizing consistent don’t shorten local variable names too much.

And most importantly as others said PLEASE COMMENT (YOUR) CODE so people can learn from it rather than just mindlessly copy it.

Anyways, thanks for the contribution if you fix those things it can bring many people some knowledge about how combat systems work.

2 Likes
new tutorial drops you might want to ignore this

I’m not really into using wait though

And i forgot to change the Humanoid:LoadAnimation() bit, i’ll get working on that, sorry about that, i will make a guide on top of the post

task.wait() superseded wait() earlier in the 2021 year, iirc.

Although wait() hasn’t be directly marked as deprecated, Roblox personally recommends to switch over to task.wait() because it works directly with the task scheduler, which in turn makes it more reliable than wait(). You’ll find that wait times are more accurate to the number you inputted over wait, so making the switch will not change anything with the script, it will only make it more accurate.

1 Like

I don’t recall myself forcing you to switch. This is a developer forum, you need to expect that people are going to tell you better ways of doing something. No need to overreact, just chill out lol.

Once you get into the habit of using task.wait(), you’ll be able to type it in seconds. It’s okay if you used a bunch of wait() statements. Nobody is telling you to go back and switch every single one.

When you make something new, just give it a go. It will only benefit you more than not.

If you’re one for the facts over anything, take a look at the following benchmark below. This is obviously me not testing in a live game, but even when testing by simply running the command in the command bar whilst in Edit mode, the performance on task.wait() is better.

image

If you don’t trust me, and think I rigged the results, test yourself:

local wait_time = os.clock()

wait()

local diff_1 = os.clock() - wait_time

local task_wait_time = os.clock()

task.wait()

local diff_2 = os.clock() - task_wait_time

print("wait(): "..diff_1.."\n\ntask.wait(): "..diff_2) 
7 Likes

i came here expecting combat mechanics when i read “advanced sword combat”
i found how to make a sword with effects.
i have been mislead

4 Likes

There are some minor issues in this tutorial. You are putting debug print statements in a tutorial, which can confuse some people when they see random print statements in their output. In one part of the tutorial, you dont explain what type of script to paste the code. Beginners wont understand that it is meant to be in a localscript. (I wont mention the fact that you tell people to paste code, I think you already know that)

Theres no visible debounce here. Meaning that if a player gets the touch event to fire as fast as possible (by moving a lot with shift lock or something), they can damage more than a normal player moving. I would be pretty mad if I lost a fight in a game due to inconsistent damaging.

“%” exists.

local count = 0 
if count % 3 == 1 then -- First animation
if count % 3 == 2 then -- Second animation
if count % 3 == 0 then -- Third animation
-- Count would increase every time you click. 
-- if count was 4 then 4 % 3 would be 1
-- if count was 5 then 5 % 3 would be 2 
-- if count was 6 then 6 % 3 would be 0
-- much better, you could probably make the animation code 3x shorter

But other than that, nice tutorial.

2 Likes

This feels like more of a recourse that a tutorial, considering that were not learning much.

I don’t understand most of the code, and I have doubts that you don’t ever, or this may not even be your code. There’s not many comments, barely to have reference off of, and nothing is really explained, this feels like a youtube Roblox Developer “tutorial” where they just tell you to copy and paste the code.

Best to put this in #resources:community-resources

got backlashed xd

This would not fit it, i will have some issues commenting it
Half of the code is mine, do you not see?

script.Parent.Equipped:Connect(function()
	animation = game.Players.LocalPlayer.Character.Humanoid.Animator:LoadAnimation(game.ReplicatedStorage.Sword.Animations.Idle)
	animation:Play()

	print("playing idle")
end)
script.Parent.Unequipped:Connect(function()
	animation:Stop()
	print("unequip detected, stopped animation")

end)

script.Parent.Activated:Connect(function()
	animation:Stop()
	wait (9)
	animation:Play()
	print ("animation is playing again")  
end)

Thats one of my code,

--This code is mine! So i didnt steal this entire code.
local Tool = script.Parent

local function onEquipped(_mouse)
	local animation = game.Players.LocalPlayer.Character.Humanoid.Animator:LoadAnimation(game.ReplicatedStorage.Sword.Animations.Equip_And_UnEquip)
	animation:Play()

end

Tool.Equipped:Connect(onEquipped)

Tool.Unequipped:Connect(function()
	local animation = game.Players.LocalPlayer.Character.Humanoid.Animator:LoadAnimation(game.ReplicatedStorage.Sword.Animations.Equip_And_UnEquip)
	animation:Play(0.100000001, 1, -1) -- Reverses Animation
end)

If you want to go ahead and complain about this, go find something else to look at.

Not sure what code is even yours’s whatsoever, considering that you said this.

Also, the Development Forum isn’t a battleground of arguments, not sure why you’re getting so defensive because I commented a recommendation, due to the fact that your tutorial isn’t easy to follow. Most people who are commenting on here are just pointing out things that they think should be changed, as some stuff may be outdated or just may have issues in the long run.

Never said you stole any code btw, you said yourself that you have used scripts that weren’t made by you, and the lack of comments is making things very hard to understand for new programmers on the platform.

Another thing to point out is, you defiantly don’t need to make a folder, just create a new animation instance, its way easier, and more organized.

1 Like

I’m not even sure why I have to write this out, almost every new person on the Developer Forum always gets extremely defensive.

Remember, unless someone is throwing insults at you or straight up trying to put you down, that’s the difference from starting an argument and constructive criticism.

And when I’m referring to you as “Getting Defensive” or “Trying to put down a reliable argument”, I’m referring to this.

“Scary Code” will most likely rub people the wrong way who pointed out that the codes a bit outdated

That’s just some examples of a bad way to respond to constructive criticism. A lot of what your using is either outdated, or there’s an easier way to do this. It’s best that you just consider using it, or telling others, as it may become a repetitive practice for new programmers to start using functions such as wait(), Humanoid:LoadAnimation() and a few other functions.

wait() has more runtime, while task.wait() is more accurate.

3 Likes

Sorry for the rant, but I’d thought it should be pointed out, way to many people on the forums like that.

Acting all defensive and being a bit childish is not going to end on a good note, all its going to do is start an argument, that’s why many people dislike new users going into #development-discussion and saying something pointless, and then proceeding to get defensive when other’s trying to explain to them a different opinion on the [Matter/Topic] discussed,

2 Likes

Is a loop really needed for Check why not just use a numbervalue with GetPropertyChangedSignal?

2 Likes