Raycast Hitbox 4.01: For all your melee needs!

I actually changed some things around last night and it started working with Animation.Length. Not sure why it started working but I’m still using Animation.Length so everything is good.

Also wanted to say that I really love your module so far, it’s a really cool way to do melee hit detection. I plan to make my melee combat system with your module. One thing I would love to see in the future is support for the ability to hit multiple humanoids in one attack, instead of stopping at just the first target hit? Not sure if this is possible to do with the way your module works though. Thank you for releasing this and being so quick at replying :smiley:

Another thing is I tried to use your new RemovePoints function, but points don’t seem to remove correctly? Each punch should do 10 damage on the humanoid, but as you can see below, they just add up instead:

https://gyazo.com/3542b50331cc26d9c32795ca74b5473b.gif

Here’s the way I coded it:

local Hitbox = RaycastHitbox:Initialize(Character, {Character})

local function RightHandPoints(Hitbox, Bool)
	if Bool then
		Hitbox:SetPoints(RightHand, {
			Vector3.new(0, 0, 0),
			Vector3.new(0, -0.1, 0),
			Vector3.new(0, 0.1, 0)
			}
		)
		Hitbox:SetPoints(RightLowerArm, {
			Vector3.new(0, 0, 0),
			Vector3.new(0, -0.1, 0),
			Vector3.new(0, 0.1, 0)
			}
		)
		RightPunch:Play()
	else
		Hitbox:RemovePoints(RightHand, {
			Vector3.new(0, 0, 0),
			Vector3.new(0, -0.1, 0),
			Vector3.new(0, 0.1, 0)
			}
		)
		Hitbox:RemovePoints(RightLowerArm, {
			Vector3.new(0, 0, 0),
			Vector3.new(0, -0.1, 0),
			Vector3.new(0, 0.1, 0)
			}
		)	
	end
end


local function LeftHandPoints(Hitbox, Bool)
	if Bool then
		Hitbox:SetPoints(LeftHand, {
			Vector3.new(0, 0, 0),
			Vector3.new(0, -0.1, 0),
			Vector3.new(0, 0.1, 0)
			}
		)
		Hitbox:SetPoints(LeftLowerArm, {
			Vector3.new(0, 0, 0),
			Vector3.new(0, -0.1, 0),
			Vector3.new(0, 0.1, 0)
			}
		)
		LeftPunch:Play()
	else
		Hitbox:RemovePoints(LeftHand, {
			Vector3.new(0, 0, 0),
			Vector3.new(0, -0.1, 0),
			Vector3.new(0, 0.1, 0)
			}
		)
		Hitbox:RemovePoints(LeftLowerArm, {
			Vector3.new(0, 0, 0),
			Vector3.new(0, -0.1, 0),
			Vector3.new(0, 0.1, 0)
			}
		)
	end
end

CombatRemote.OnServerEvent:Connect(function(Player, Info, Plr)
	if Player.Character.Properties.Move.Value and not MeleeDebounce and Player.Name == Plr.Name then
		MeleeDebounce = true
		
		Hitbox.OnHit:Connect(function(Hit, Humanoid)
			if Humanoid.Health > 1 then
				local Move = Humanoid.Parent.Properties.Move.Value
				local HumanoidRootPart = Humanoid.Parent.HumanoidRootPart
				Humanoid.AutoRotate = false
				if Humanoid.Health - Damage > 1 then
					Humanoid:TakeDamage(Damage)
					Humanoid:MoveTo(HumanoidRootPart.Position + -4 * HumanoidRootPart.CFrame.lookVector)
					local Punched = Humanoid:LoadAnimation(PunchedAni)
					Punched:Play()
				else
					Humanoid.Health = 0.01
					print("Dead")
				end
				Move = false
				wait(0.3)
				Move = true
				Humanoid.AutoRotate = true
			end
		end)
		
		local Hand
		local Animation
		
		if Info == "Right" then
			RightHandPoints(Hitbox, true)
			Hand = RightHand
			Animation = RightPunch
		elseif Info == "Left" then
			LeftHandPoints(Hitbox, true)
			Hand = LeftHand
			Animation = LeftPunch
		end
		local Time = Animation.Length
		Hitbox:HitStart()
		wait(Time)
		Hitbox:HitStop()
		if Info == "Right" then
			RightHandPoints(Hitbox, false)
		elseif Info == "Left" then
			LeftHandPoints(Hitbox, false)
		end
		MeleeDebounce = false
	end
end)

Edit:

Here’s another issue I discovered:

https://gyazo.com/a4a4aa2eea8904ef44792dc34f64aad0.gif

2 Likes

Okay, so I fixed the RemovePoints by making it check for MeshParts as well in the if condition. The R15 body parts are MeshParts, which is why it didn’t execute the rest of the function.

Here’s the fix:

function Hitboxing:RemovePoints(partInstance, vectorPoints)
	if partInstance then
		if partInstance:IsA("BasePart") or partInstance:IsA("MeshPart") then
			for _,Points in pairs(self.Points) do
				for _, vectors in ipairs(vectorPoints) do
					if typeof(Points.Attachment) == "Vector3" and Points.Attachment == vectors and Points.RelativePart == partInstance then
						self.Points[Points.Attachment] = nil
					end
				end
			end
		end
	end
end

However, I still get odd hitbox issues, where I’m able to hit enemies that are far away:

https://gyazo.com/24344db6f99ce25dbf02db4487b97b37.gif
https://gyazo.com/d2b1d94053b2c9cf49ffd25cfac310d8.gif
https://gyazo.com/08bf6a2b046061998fcd54dcb31781f3.gif
https://gyazo.com/8ad4c661f7cd01544635c82653d5ee79.gif

Code:

local function RightHandPoints(Hitbox)
	Hitbox:SetPoints(RightHand, {
		Vector3.new(0, 0, 0),
		}
	)
	RightPunch:Play()
	local PunchSFX = Punch1SFX:Clone()
	PunchSFX.Parent = Character.HumanoidRootPart
	PunchSFX:Play()
end

local function RemoveRightHandPoints(Hitbox)
	Hitbox:RemovePoints(RightHand, {
		Vector3.new(0, 0, 0),
		}
	)
end


local function LeftHandPoints(Hitbox)
	Hitbox:SetPoints(LeftHand, {
		Vector3.new(0, 0, 0),
		}
	)
	LeftPunch:Play()
	local PunchSFX = Punch2SFX:Clone()
	PunchSFX.Parent = Character.HumanoidRootPart
	PunchSFX:Play()
end

local function RemoveLeftHandPoints(Hitbox)
	Hitbox:RemovePoints(LeftHand, {
		Vector3.new(0, 0, 0),
		}
	)
end

CombatRemote.OnServerEvent:Connect(function(Player, Info, Plr)
	if Player.Character.Properties.Move.Value and not MeleeDebounce and Player.Name == Plr.Name then
		MeleeDebounce = true
		
		local Hitbox = RaycastHitbox:Initialize(Character, {Character})  -- Here I initialize
		
		Hitbox.OnHit:Connect(function(Hit, Humanoid)
			local Punched = Humanoid:LoadAnimation(PunchedAni)
			if Humanoid.Health > 1 then
				local Move = Humanoid.Parent.Properties.Move.Value
				local HumanoidRootPart = Humanoid.Parent.HumanoidRootPart
				Humanoid.AutoRotate = false
				if Humanoid.Health - Damage > 1 then
					spawn(function()
						HitEffect(Humanoid.Parent)
					end)
					Humanoid:TakeDamage(Damage)
					Humanoid:MoveTo(HumanoidRootPart.Position + -1 * HumanoidRootPart.CFrame.lookVector)
					Punched:Play()
				else
					Humanoid.Health = 0.01
					Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
					Humanoid:MoveTo(HumanoidRootPart.Position + -8 * HumanoidRootPart.CFrame.lookVector)
					print("Dead")
				end
				createSound(Humanoid)
				Move = false
				wait(Punched.Length)
				Move = true
				Humanoid.AutoRotate = true
			end
		end)
		
		local Hand
		local Animation
		
		if Info == "Right" then
			RightHandPoints(Hitbox) -- Here I set the points
			Hand = RightHand
			Animation = RightPunch
		elseif Info == "Left" then
			LeftHandPoints(Hitbox)  -- Here I set the points
			Hand = LeftHand
			Animation = LeftPunch
		end
		local Time = Animation.Length
		Hitbox:HitStart()
		wait(Time)
		Hitbox:HitStop()
		MeleeDebounce = false
		if Info == "Right" then
			RemoveRightHandPoints(Hitbox) -- Here I remove the points
		elseif Info == "Left" then
			RemoveLeftHandPoints(Hitbox)-- Here I remove the points
		end
		RaycastHitbox:Deinitialize(Character) -- Here I deinitialize
	end
end)

Notice I still have to Deinitialize the very bottom, or else the points just keep adding up. So in that case, I have to assume that RemovePoints did not work for me anyway.

This code has the issue with the hitbox being all over the place though.

If I spawn in attachments manually when a character is created and use those then it seems like hitboxes works perfectly. So it only appears that at least setting the points the way I did has a wonky hitbox. The issue with spawning in the attachments manually for me is I don’t want all attachments to be active for both hands at the same time, but rather that when punching with the left hand, only the DmgPoints in that hand should be active.

I realize I could just rename them when they’re in use and not in use though, but would be nice as an integrated feature in your module to somehow alternate between DmgPoints. Otherwise I hope you can help me figure out the issue with SetPoints, RemovePoints and the odd hitboxes that occur using them, at least the way I scripted it. I assume the alternating between different DmgPoints is what you meant to do with these functions :smiley:

5 Likes

Glad you got the Animation length worked out.

HitStart will already hit as many humanoids as it can if that’s what you’re implying. It will only hit each humanoid once but never the same humanoid twice. If you require multiple hits per part (similar to how the conventional Touched function works), PartsMode inside the module is a better option.

This is not a problem with the RemovePoints, instead, it is how you are coding the OnHit connection line. It is memory leaking because you are creating a new connection line every time OnServerEvent fires. It won’t garbage collect at the end because Hitbox is an actual object that still exists. Your code is the equivalent of:

while wait(1) do
     workspace.Brick.Touched:Connect(function(hit)
          hit.Parent.Humanoid:TakeDamage(10)
     end)
     --- This event will continue to be created, increasing damage taken by 10 each second
end

I would disconnect the event line at the end of HitStop so this problem won’t happen:

CombatRemote.OnServerEvent:Connect(function(Player, Info, Plr)
	if Player.Character.Properties.Move.Value and not MeleeDebounce and Player.Name == Plr.Name then
		MeleeDebounce = true
		
		local HitConnection;    --- New Variable
		HitConnection = Hitbox.OnHit:Connect(function(Hit, Humanoid)
			if Humanoid.Health > 1 then
				local Move = Humanoid.Parent.Properties.Move.Value
				local HumanoidRootPart = Humanoid.Parent.HumanoidRootPart
				Humanoid.AutoRotate = false
				if Humanoid.Health - Damage > 1 then
					Humanoid:TakeDamage(Damage)
					Humanoid:MoveTo(HumanoidRootPart.Position + -4 * HumanoidRootPart.CFrame.lookVector)
					local Punched = Humanoid:LoadAnimation(PunchedAni)
					Punched:Play()
				else
					Humanoid.Health = 0.01
					print("Dead")
				end
				Move = false
				wait(0.3)
				Move = true
				Humanoid.AutoRotate = true
			end
		end)
		
		local Hand
		local Animation
		
		if Info == "Right" then
			RightHandPoints(Hitbox, true)
			Hand = RightHand
			Animation = RightPunch
		elseif Info == "Left" then
			LeftHandPoints(Hitbox, true)
			Hand = LeftHand
			Animation = LeftPunch
		end
		local Time = Animation.Length
		Hitbox:HitStart()
		wait(Time)
		Hitbox:HitStop()

		HitConnection:Disconnect()   --- Disconnect

		if Info == "Right" then
			RightHandPoints(Hitbox, false)
		elseif Info == "Left" then
			LeftHandPoints(Hitbox, false)
		end
		MeleeDebounce = false
	end
end)

This is actually interesting, never had that happening and no one reported this yet. Even with a memory leaking hit connection, this shouldn’t do that. If it is still happening after fixing your connection line, I would be grateful if you could send a barebones version of the place you’re playtesting so I can diagnose it further.

4 Likes

Ah okay, I see what you mean with the HitConnection. I applied your suggestion to the code and it still persists as shown below.

Oh okay, I misunderstood what HitStart did. I thought it stopped damage at the first Humanoid hit, but glad to hear it will hit as many humanoids as you want until HitStop is called and it’s good the fact that it only hits the same humanoid once. Fits perfectly with what I’m trying to do with my game!

I took everything that has to do with the melee system and placed it in a new place, deleted all the unnecessary stuff and isolated the relevant code, which I assume is what you asked for :stuck_out_tongue:

There are no animations, but as you’ll see, it still happens even without animations. I made the place uncopylocked for you, so I hope you’ll be able to get to the bottom of this :smiley:

https://gyazo.com/96c73960aade941cfeb107e35da49065

https://gyazo.com/d5c6c30da52b62f14875e271c5a9c8ac.png

The envinroment script is where the player fires to the server event, nothing special here.
The main code is inside the MeleeSystem under StarterCharacterScripts.

https://www.roblox.com/games/4815646095/For-Swordphin123

3 Likes

Oh by the way:

https://gyazo.com/2ce28be4280b73717ca9d156cb751df6.png

If you want to use my place as a simple example model, feel free to modify it to your needs if you think it would suit what you’re looking for :stuck_out_tongue:

2 Likes

Thanks! Will definitely utilize this as a sample place if I get around to it :smiley:

Anyway, I really appreciate this place, it’s easy to recreate the bug and I’ve managed to fix it. Took an hour of head scratching to realize I forgot to add the direction on where the raycast should be going when using SetPoints. :man_facepalming:

RobloxStudioBeta_2020-03-24_17-32-23

The red is the “projected” debug rays (where it SHOULD be going) but the green is the real rays that are not drawn correctly when using SetPoints.

Try redownloading the Raycast module, I’ve updated it to V.1.51 with the new fixes.

3 Likes

Haha, glad I was able to help out and that you managed to find the issue after all :smiley:

I thought it was something with my code this morning. I was head scratching all morning trying to figure out why it happened as well, so I know how you feel haha :rofl:

Good looks, really appreciate you and this module @TeamSwordphin. Keep up the awesome work :muscle:

1 Like

Honestly this seems like a pretty efficient and useful module. Definitely will be using this for some projects in the future.

I also bought the donation so enjoy :wink:

4 Likes

V.1.52

Hi all, this version is just a minor fix for LinkAttachments, nothing too interesting.

But, wait, before you leave, I do have something interesting for folks who wants to try it out immediately without the need to code your own test tools or whatever.

Behold, the sample place!

(limited edition, link only available on the main OP, expires in the next 99 years)


It contains a modified Roblox LinkedSword, a damaging fan, and an impossible laser obstacle course. No @Shurikanz were harmed in the making of this film. I might have also stole the fan, so don’t use it for commercial work. Thanks.

11 Likes

Hahaha, this is great! I love it - thank you for this @TeamSwordphin :rofl:
Keep up the amazing work :smiley:

1 Like

Cool idea and well implemented. Maybe I’ll use that in my next game.

btw. who made you this cool character with the sword and the wings?
https://gfycat.com/enchantingdecentbeagle

Everything you see there is my own creation.

3 Likes

I created my own version of this module because I wanted to get rid of some complexity and use my own ray cast visualizer. However, I made something that might be of use for this module:

function CastDetector:GeneratePoints(resolution)
	local Increment = 1 / (resolution - 1)
	
	for alpha = Increment, 1 - (1 / resolution), Increment do
		local Attachment = Instance.new("Attachment")
		Attachment.Name = "CastPoint"
		Attachment.CFrame = self.Object.Bottom.CFrame:Lerp(self.Object.Top.CFrame, alpha)
		Attachment.Parent = self.Object
	end
end

It generates a line of attachments between a top and bottom attachment.

Before:
image

After:

The resolution argument specifies how many points in total including the top and bottom attachment.

Obviously this is only meant for normal swords and such.

11 Likes

definitely a cool module, I’ve used it a few times already!

1 Like

V.1.53

Due to the large amount of people DMing me lately about why their rays aren’t appearing, I’ve updated some of the outdated API and clarified the troubleshooting sections in the module. The module will now also scream warning messages when it detects an anomaly that prevents rays from being cast (for example, incorrect attachment names or transparent parts).

Functionality has not been altered. Hopefully this will help those that are still confused. If you are one of them, please consider DMing me or replying below so I can help.

4 Likes

Hello. I’m having trouble understanding why I would use Raycasting to detect hits rather than using the part.touched event in a tool. Can anyone explain the concept?

1 Like

I touched base on this topic a little on one of the threads I listed in the OP. But to summarize, I needed a way to have hitboxes that do two things: Be as accurate as possible while also maintaining adequate performance.

For simpler cases, like the classic Roblox sword, Touched is all you need. You don’t have to fiddle around making rays or region3s.

My games are based around extremely fast and fluid combat. Take this gif as an example:

Touched will not work with these kinds of animations because Touched relies on physics to register. If you played any games on Roblox where something flies really fast, sometimes you can see it phase or glitch right through solid objects when it should be bouncing back. Matured game engines like Unity and Unreal Engine still suffers from this. This is the same concept. Using raycasting will allow you to achieve hit detection velocity that is impossible with touched. It’s also, in my opinion, easier to work with then Touched.

In addition, if you are moving bricks with CFrame, Touched will not register them because they are not considered “physics”. Raycasting will fix this.

Region3s are another popular way for people to detect hits, even for Cframed parts, but they are static and not much you can do with them in terms of hitbox shapes.

Ultimately, there is no right or wrong answer to what you should use. Use whatever you feel is the most appropriate for your setup. Experiment, keep trying things out, and eventually, you will come to understand the limitations of each method. Raycasting for hitboxes suffers from resolution problems, meaning wider objects need fuller raycasting setups to achieve what Touched could already naturally do. Region3 is great but it requires modification for angles and lets in unnecessary parts.

All can be performance heavy if used wrongly, but in my testings, Raycasting is the easiest to get right.

19 Likes

I have a problem, the raycast won’t properly follow my model until after it gets a hit. After that, even deinitializing and reinitializing the hitboxes it still works.

EDIT 1: After further testing it seems that this only gets fixed attacking unanchored dummies, not anchored dummies or any player

2 Likes

How is the model getting created? Client or server sided? Might be a networking issue.

The model is added on character load, cloned from server storage.
Also, it seems that dying makes the raycast not follow the model again.