Raycast Hitbox 4.01: For all your melee needs!

There are two ways to do this. The foolproof way is to just instance a new attachment object, rename it, and adjust its worldposition in your characters fist. After, call initialize (or Deinitialize first if you already initialized prior to attachment instancing). The module should automatically fetch it.

The second, slightly more advanced but preferable way if you are scripting dynamic dmg points are using the :SetPoints function. I had an example code somewhere in this thread where you can search for. This is good because you can make new points of raycast without deinitizializing your created hitboxes as it caches immediately upon calling the function. It also is probably more performance friendly too since it’s all vector based without the hassle of performance costs from instancing.

5 Likes

Hey Swordphin, do you know why the initial first attachments on each hands aren’t spawning correctly?
I gotta punch each fist once before every punch after those work as intended. This only happens the first time each fist has to punch.

https://gyazo.com/1a36dd6be7a397aebe8c900c6c7a5788.gif

Code:

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


local function RightHandPoints(Hitbox)
	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)
		}
	)
	local RightPunch = Character.Humanoid:LoadAnimation(RightPunch)
	RightPunch:Play()
end


local function LeftHandPoints(Hitbox)
	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)
		}
	)
	local LeftPunch = Character.Humanoid:LoadAnimation(LeftPunch)
	LeftPunch:Play()
end

CombatRemote.OnServerEvent:Connect(function(Player, Info, Plr)
	if Player.Character.Properties.Move.Value and not MeleeDebounce and Player.Name == Plr.Name then
		Hitbox = RaycastHitbox:Initialize(Character, {Character})
		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(Punched)
					Punched:Play()
				else
					Humanoid.Health = 0.01
					print("Dead")
				end
				Move = false
				wait(0.3)
				Move = true
				Humanoid.AutoRotate = true
			end
		end)
		
		local Animation
		local Hand
		
		if Info == "Right" then
			RightHandPoints(Hitbox)
			Animation = RightPunch
			Hand = RightHand
		
		elseif Info == "Left" then
			LeftHandPoints(Hitbox)
			Animation = LeftPunch
			Hand = LeftHand
		end
		Hitbox:HitStart()
		local Time = Animation.Length
		--delay(.3, function() Hitbox:HitStop() end)
		wait(Time)
		MeleeDebounce = false
		RaycastHitbox:Deinitialize(Character)
	end
end)
2 Likes

If I were to take a gamble, it is that animations on the server side (for it’s first time use) will take a little bit before it loads completely. Animation.Length may not be immediately available and will probably return 0 seconds which deinitializes the hitbox sooner than you expect. I had this happen when I was making AIs back in the days but feel free to prove me wrong with this theory. Try having the wait(Time) to be something longer just to see if I am correct.

Edit: To rectify this, I would recommend using this module client sided so the animations are loaded completely.

1 Like

Version 1.5 Beta

V.1.5 introduces some new HitboxObject functions:
HitboxObject:RemovePoints(Instance part, table vectorPoints)
HitboxObject:LinkAttachments(Instance attachment0, Instance attachment1)
HitboxObject:UnlinkAttachments(Instance attachment0)

RemovePoints works exactly the same way as SetPoints, except reverse. It removes all SetPoints with the same part instance and vector values you feed it. It will not do anything if the supplied vectors doesn’t exist for the given vector points in a hitbox.

Example code:

local RaycastHitbox = require(RaycastHitboxModule)
local NewHitbox = RaycastHitbox:Initialize(workspace.Brick)

NewHitbox:SetPoints(NewHitbox.Object,
     {
          Vector3.new(1, 0, 0),
          Vector3.new(0, 10, 50),
          Vector3.new(-40, 10, 3)
     }
)
NewHitbox:RemovePoints(NewHitbox.Object,
     {
          Vector3.new(1, 0, 0),
          Vector3.new(0, 10, 50),
          Vector3.new(-40, 10, 3)
     }
)

LinkAttachments requires two attachment objects (support for vectors at a later date, probably) which will raycast between those two points instead of one point.

---Example

NewHitbox:LinkAttachments(Character.Sword.HiltAttachment, Character.Sword.TipAttachment)

For LinkAttachments, it is meant for niche uses or in severe cases, to improve performance. It is not recommended to use this system for fast paced combat. As you can see in the above footage, it is no where near as accurate as the original raycastings, often missing me even if I was directly in front of it. However, you can combine this with the original system to make even weirder hitbox types.

You can remove links by using the first attachment.

NewHitbox:UnlinkAttachments(Character.Sword.HiltAttachment)
9 Likes

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.

12 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