Raycast Hitbox 4.01: For all your melee needs!

Great thank you! I tried setting up the Linked Attachments and in the end, the whole thing is starting to feel a little but Rube Goldberg for this use case.

The module is incredible as it is, but if I could add anything, it would be the ability to register a hit while the HitPoints are not in motion. Imagine a sword or lance stabbed through the target, doing damage continually as long as it is there.

Thanks for your support!

4 Likes

Guys I followed the tutorial on Github and made a part, put an attachment on it named “DmgPoint”, activated DebugMode, and put a script in it

-- << Initial Setup >> 
local RS = game:GetService("ReplicatedStorage")
local RAYCAST_HITBOX = require(RS.RaycastHitboxV3)

local newHitbox = RAYCAST_HITBOX:Initialize(script.Parent)

newHitbox:DebugMode(true)
newHitbox:HitStart()

now, the raycast message detected the attachment, but I couldn’t see the rays. Anyone know why?

2 Likes

I know it may seem obvious, but have you tried moving the part around? If rays are still not being drawn, make sure HitStop isn’t being written anywhere.

1 Like

Uh, for some odd reason, my hitbox.onhit function is unable to detect boolean values that are set outside and above that function.

function module.Swing(AnimTable, Tool, Damage, UserHumanoid, Hitbox, Special)
	local Handle = Tool:FindFirstChild("Handle")
	if Hitbox then
		local ChosenAnim = #AnimTable > 1 and AnimTable[math.random(1, #AnimTable)] or AnimTable[1]
		local TouchConnection
		ChosenAnim:Play()
		print(Special)
		ChosenAnim:GetMarkerReachedSignal("EnableHit"):Connect(function()
			Hitbox:HitStart()
			TouchConnection = Hitbox.OnHit:Connect(function(hit, Hum)
				print(Special)
				if Special == true then
					print("YEE")
					WeaponSpecials[Tool.Name](Hum)
				end
				Hum:TakeDamage(Damage)
			end)
		end)
		ChosenAnim.Stopped:Connect(function()
			Hitbox:HitStop()
		end)
	end
end

Hey!

I’m currently learning how to script, and I’ve been trying to learn how to make effective hitboxes. I mostly used raycasting, and the way my hitboxes worked is basically by casting 1 ray every time the melee weapon is activated/used. I usually cast the ray from the player’s humanoidrootpart, and although it works, it’s sometimes inconsistent, and it’s hard to calculate how far I want the ray to be.

Basically, the way your raycast hitbox works is by placing attachments in a melee weapon, and then raycasting all of those attachments at once whenever the tool is activated? I’m really curious about this

Assuming you are talking about the ‘Special’ boolean, seems to be working fine for me. I am using the following setup to test:

Details
A server script inside the tool
local RaycastModule = require(script.Parent:WaitForChild("RaycastHitboxV3"))
local Schwing = require(script.Parent.Schwing)
local newHitbox, userHumanoid

script.Parent.Equipped:Connect(function()
	userHumanoid = script.Parent.Parent:WaitForChild("Humanoid")
	newHitbox = RaycastModule:Initialize(script.Parent, {script.Parent.Parent})
end)

script.Parent.Activated:Connect(function()
	local Animation = Instance.new("Animation")
	Animation.AnimationId = "rbxassetid://6017466008" --- Example
	Schwing.Swing({userHumanoid:LoadAnimation(Animation)}, script.Parent, 5, userHumanoid, newHitbox, true)
end)
Same module as you are using
local module = {}

function module.Swing(AnimTable, Tool, Damage, UserHumanoid, Hitbox, Special)
	local Handle = Tool:FindFirstChild("Handle")
	if Hitbox then
		local ChosenAnim = #AnimTable > 1 and AnimTable[math.random(1, #AnimTable)] or AnimTable[1]
		local TouchConnection
		ChosenAnim:Play()
		print(Special)
		ChosenAnim:GetMarkerReachedSignal("EnableHit"):Connect(function()
			Hitbox:HitStart()
			TouchConnection = Hitbox.OnHit:Connect(function(hit, Hum)
				print(Special)
				if Special == true then
					print("YEE")
				end
				Hum:TakeDamage(Damage)
			end)
		end)
		ChosenAnim.Stopped:Connect(function()
			Hitbox:HitStop()
		end)
	end
end

return module

Ensure you are not fetching a hitbox that is created from a client or vice versa (ie. trying to fetch an initialized hitbox done by the client on the server).


@Crescendee

Yes, each heartbeat frame will run an entire iteration on all cached attachments. There is a bit of misconception being that people think that this module has a predetermined length the rays start off with when in reality all it does is check positions between the last frame and the current frame and draws a ray in between. Thus, the length is determined entirely by the speed of your hitbox movement. If your hitbox is unmoving, the length will be exactly zero.

2 Likes

I think it’s the get marker reached signal, then, that seems to run multiple times. If that’s the case, I don’t know why that happens.

1 Like

Yeah, if you aren’t destroying the animation or detaching the getmarkereached, I believe you are creating a new event for it every time.

1 Like

Hey, im curious as to how this works, and am seriously considering this for my game, however to my current understanding, when player swings, i have to call hitstart, and when the hit/animation ends, call hitstop. is this correct?

also what else should i be considering?

So should I create a connection and then disconnect it once getmarkerreached runs once? Because the first time it happens the hitbox.hit thing hasn’t run yet, but when it does the boolean is nil. (the first time it runs it’s true, times after that it’s nil). Keep in mind this happens in just one swing.

Correct, that is the basis for how to use it. Basically you would call HitStart/Stop whenever you want to turn on the hitbox (to detect enemies) and respectively, turn off the hitbox (to prevent hit detection). Now, people ask me what’s the best way to use it but honestly it depends on your own game. So at the end of the animation is ideal, also at the end of a swing as well, so its what you think feels right.

Sorry if I don’t understand, but could you clarify this point? It may be just late and I’m tired lol.


@0therw0rldly

You can possibly flip the positions of HitStart and OnHit event (which is what I do and encourage to do as it basically guarantees the event to be created by the time the hit detection starts).

--- This order instead

Hitbox.OnHit:Connect()
   -- blah
end)
Hitbox:HitStart()

Yeah, you can disconnect it once EnableHit is called, though might be best to disconnect it when the animation has stopped instead (the same place you put HitStop()) in case your animation has multiple EnableHits you want to utilize.

1 Like

Yeah, but even on the first swing this happens and my animation only has one enable hit.

Hmm, not sure. I just tested it with the revised ordering (as indicated from my last post) and the boolean is detected, regardless of its actually nil or an actual value.

If you are still stuck and don’t mind sharing a little bit of your system with me, you can send a simple repo place with the problem and I’ll be happy to diagnose it for you.

Alright, so here’s what it’s outputting. First, it runs once and prints it as true (keep in mind I just swung it in the air) then it’s on the same line but somehow runs three more times even though the swing function has only run once, and it prints nil those last three times.

function module.Swing(AnimTable, Tool, Damage, UserHumanoid, Hitbox, Special)
	local Handle = Tool:FindFirstChild("Handle")
	if Hitbox then
		local ChosenAnim = #AnimTable > 1 and AnimTable[math.random(1, #AnimTable)] or AnimTable[1]
		local TouchConnection
		ChosenAnim:Play()
		print(Special)
		ChosenAnim:GetMarkerReachedSignal("EnableHit"):Connect(function()
			print(Special)
			TouchConnection = Hitbox.OnHit:Connect(function(hit, Hum)
				print(Special)
				if Special == true then
					print("YEE")
					if WeaponSpecials[Tool.Name] then
						WeaponSpecials[Tool.Name](Hum)
					end
				end
				Hum:TakeDamage(Damage)
				HitSound(Hum.Parent)
			end)
			Hitbox:HitStart()
		end)
		ChosenAnim.Stopped:Connect(function()
			Hitbox:HitStop()
		end)
	end
end

Currently the output I’m speaking of is the line below ChosenAnim:GetMarkerReachedSignal

Not sure why it’s running multiple times, I only have one animation event in there.

@TeamSwordphin uuhh hello? Sorry to bother, just wondering if you know.

Good Module for now I can just double damage mobs which is kinda good but here is my question can I make the rays even longer than its current length?

Sorry, was busy. I would recommend trying to replicate this in a separate, empty place with just the swing module and a simple tool and see if the problem persists (the problem does not show itself on my end at least).

Without video footage it’s a bit harder to visualize but I assume the Special gets printed once, then an additional 3 times (making it x4 in total). Again, I recommend disconnecting the GetMarkerReachedSignal once the animation stops (though admittedly I have not used that function a lot so it is maybe different from KeyframeReached).

There a few solutions that you can try.

  1. Double check if there are multiple EnableHit markers on your animation by accident
  2. Does the problem persist when you remove the OnHit event and leave it to print merely by reaching the EnableHit marker?

This problem seems to be out of the scope of this module so if there is still some persistent problems, I’ll be happy to help over in DMs instead.


@FerbZides Refer to the end of this post on how this module works, there is not really a determined length that you can easily change.

Though I guess if you want to scale up the distance (ie. overshoot the rays a little), you can go into the MainHandler script and go to around line 56:

local raycastResult = workspace:Raycast(rayStart, rayDir, Object.raycastParams)

You can probably do something like rayDir * LENGTH or something.

2 Likes

I noticed the red ray that comes out of the weapon I use this on, does it show up in-game?

Those are the debug rays. They are meant to show you the position of them. Objects within those red lines are considered hit.

Yes, they will show up in the game. It’s on by default because most of the time scripters need to see where they start and end.

You can disable it in the main module, scroll down till you see the version and debugmode bool, and turn it false to disable it.

1 Like