Raycast Hitbox 4.01: For all your melee needs!

I’m not really sure on why you opted out on a Destroy method within an actual HitboxObject and rather you created a Deinitialize method of the main module. Having a destroy method is more standard and common rather then having to get the model and input it as a parameter from a method in the main module.

True, though its mainly for backwards compatibility for games that used this module a year ago. I would have definitely switched to more standard terms if not for that. Frankly, I’m not too happy with the terms either so if there is ever a v4 of this module, I would be deprecating or changing the function names entirely to match more standard practices.

You could still add the Destroy function while leaving the Deinitialize function available. Also on the github, the latest version wasn’t released.

1 Like

Haven’t really had time to update it. Updated now though. I’ll add Destroy in the next version update so its more consistent with standard practice.

1 Like

:+1:
I was also looking through the source code and noticed that you use RenderStepped on the client to perform raycasts. I believe you are degrading performance when compared to using Heartbeat. There’s no point in running it every frame when physics updates on Heartbeat?

4 Likes

Amazing! This is so incredibly useful

3 Likes

I’m having an issue with it. I’m unable to actually deal damage, i’m getting this in output:

[ ReplicatedStorage.RaycastHitboxV3.Tools.Signal:15: invalid argument #1 to ‘create’ (function expected, got nil)]

Anyone know why this occurs?

2 Likes

Show your code that has the raycast module segments. You may have a simple typo or calling a hitbox that doesn’t exist (both are usually what causes that error)

1 Like

Is there anyway to get the position/normal that the hitbox it on?

1 Like

Are vertical rays going to be added anytime soon?

1 Like

@DiehvcTy The OnHit event returns the raycastresult.

https://developer.roblox.com/en-us/api-reference/datatype/RaycastResult

Hitbox.OnHit:Connect(function(hit, humanoid, raycastResult)
     local position = raycastResult.Position
     local normal = raycastResult.Normal
end)

@KelvenOne LinkAttachments are the main supported feature for vertical hit detection. There are currently no other plans to support any other version of vertical rays.

4 Likes

I am having a bit of trouble with the module. So, I created a simple melee combat. When I load in, the melee works fine however, when I reset then it breaks and no errors appear in the console. I would appreciate any help with this and thank you in advance!


Here is the local script:

--// SERVICES \\--
local players = game:GetService("Players")
local repStorage = game:GetService("ReplicatedStorage")


--// VARIABLES \\--
local combatTool = script.Parent
local hitboxModule =  require(repStorage.RaycastHitboxV3)
local combatEvent = repStorage:WaitForChild("combatEvent")

local player = players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local hum, hrp = char:WaitForChild("Humanoid"), char:WaitForChild("HumanoidRootPart")

local rArm = char:FindFirstChild("Right Arm") or char:WaitForChild("Right Arm")


local armPoints = {

	-- Center
	Vector3.new(0, 0.5, 0),
	Vector3.new(0, 0, 0),
	Vector3.new(0, -0.5, 0),
	-- Front Close
	Vector3.new(-0.5, 1, -0.5), 	-- Top
	Vector3.new(-0.5, 0, -0.5), 	-- Mid
	Vector3.new(-0.5, -1, -0.5),	-- Bottom
	-- Front Far 
	Vector3.new(0.5, 1, -0.5), 
	Vector3.new(0.5, 0, -0.5), 
	Vector3.new(0.5, -1, -0.5),
	-- Back Far
	Vector3.new(0.5, 1, 0.5), 
	Vector3.new(0.5, 0, 0.5), 
	Vector3.new(0.5, -1, 0.5),
	-- Back Close
	Vector3.new(-0.5, 1, 0.5), 
	Vector3.new(-0.5, 0, 0.5), 
	Vector3.new(-0.5, -1, 0.5),
}


--// EVENTS \\--

local rArmInit = hitboxModule:Initialize(rArm, {char})

rArmInit:SetPoints(rArm, armPoints)

rArmInit.OnHit:Connect(function(hit, hum)
	if hum and hum.Health > 0 then
		combatEvent:FireServer("punch", hit, hum)
	end
end)

combatTool.Activated:Connect(function()
	rArmInit:HitStart()
	wait(.3)
	rArmInit:HitStop()
end)

Server Script:

--// Services \\--
local repStorage = game:GetService("ReplicatedStorage")


--// Variables \\--
local combatEvent = repStorage:WaitForChild("combatEvent")


--// Events \\--
combatEvent.OnServerEvent:Connect(function(player, action, info, info2)
	local char = player.Character
	local hum, hrp = char:FindFirstChild("Humanoid"), char:FindFirstChild("HumanoidRootPart")
	
	if action == "punch" then
		local hit, ehum = info, info2
		if hum and hum.Health > 0 and ehum and ehum.Health > 0 then
			ehum:TakeDamage(10)
		end
	end
	
end)
3 Likes

Roblox respawning priority is a bit weird. With a bit of experimentation on my end with your script, tools seem to load faster than character themselves. When a character dies, they do not render player.Character = nil, instead it will still point towards your old character that is awaiting garage collection in memory and your tool will believe the character is still there. The ‘char’ variable will be pointing towards the old character, which will be forwarded to the RaycastModule, so technically it is working as expected according to the script.

The fix would be to define the variables after the player can use the tool and equips it. This will guarantee that the character is the one that the player spawns with.

Fixed ver:

--// SERVICES \\--
local players = game:GetService("Players")
local repStorage = game:GetService("ReplicatedStorage")


--// VARIABLES \\--
local combatTool = script.Parent
local hitboxModule =  require(repStorage.RaycastHitboxV3)
local combatEvent = repStorage:WaitForChild("combatEvent")

local player
local char
local hum, hrp

local rArm


local armPoints = {

	-- Center
	Vector3.new(0, 0.5, 0),
	Vector3.new(0, 0, 0),
	Vector3.new(0, -0.5, 0),
	-- Front Close
	Vector3.new(-0.5, 1, -0.5), 	-- Top
	Vector3.new(-0.5, 0, -0.5), 	-- Mid
	Vector3.new(-0.5, -1, -0.5),	-- Bottom
	-- Front Far 
	Vector3.new(0.5, 1, -0.5), 
	Vector3.new(0.5, 0, -0.5), 
	Vector3.new(0.5, -1, -0.5),
	-- Back Far
	Vector3.new(0.5, 1, 0.5), 
	Vector3.new(0.5, 0, 0.5), 
	Vector3.new(0.5, -1, 0.5),
	-- Back Close
	Vector3.new(-0.5, 1, 0.5), 
	Vector3.new(-0.5, 0, 0.5), 
	Vector3.new(-0.5, -1, 0.5),
}


--// EVENTS \\--

local rArmInit

combatTool.Equipped:Connect(function()
	player = players.LocalPlayer
	char = player.Character or player.CharacterAdded:Wait()
	hum, hrp = char:WaitForChild("Humanoid"), char:WaitForChild("HumanoidRootPart")

	rArm = char:FindFirstChild("Right Arm") or char:WaitForChild("Right Arm")
	
	rArmInit = hitboxModule:Initialize(rArm, {char})
	rArmInit:SetPoints(rArm, armPoints)
	rArmInit.OnHit:Connect(function(hit, hum)
		if hum and hum.Health > 0 then
			combatEvent:FireServer("punch", hit, hum)
			print("HIT")
		end
	end)
end)

combatTool.Activated:Connect(function()
	rArmInit:HitStart()
	wait(.3)
	rArmInit:HitStop()
end)
2 Likes

V.3.1 Stable

It has come to my attention from my tests that AncestryChanged does not fire reliably for characters or tools, leading to memory leaks and issues like

For people who do not Deinitialize or Destroy their hitboxes for character owned objects, you may have experienced some memory leakage to an extent.


The Solution

I have ditched AncestryChanged in favour of CollectionService, and using GetInstanceRemovedSignal. From my tests, this event fires reliably, even on character respawns. There should be no more memory issues after this one. Please update to 3.1 as soon as possible if you experience performance lost. AncestryChanged was used since RaycastModule 1.0, so all versions are currently affected. AncestryChanged was mainly used to automate if an object is destroyed and no longer in the world space.


New Update

Hitbox.OnUpdate:Connect(function(lastPosition)

end)

This event will fire for every point, every frame when a hitbox is active. Useful if you want the hitbox to do something while its traveling (and not necessarily when it needs to hit something). Highly suggested not to put anything performance heavy or if you have a lot of points on your hitbox.

An example I used with this feature is when I want to determine where the enemy projectiles are, so I use this event to see if the enemies are close enough to deflect them.


Bug fixes

Fixed some issues where signal will occasionally spit out an error message whenever OnHit isn’t defined.

9 Likes

Thank you very much! This module is awesome! Also, wouldn’t it be more efficient to disconnect and deinitialize upon unequipping?

3 Likes

Generally in this case you can leave it. The module will return the same hitbox if it finds it was initialized already, so it’s already quite efficient. It will only do the “real” initialization upon seeing it doesn’t exist. Deinitialization is used if you are sure you won’t be using the hitbox ever again.

2 Likes

Has anyone here thought of the idea of a time-based hit start? Basically, it would take one optional argument in :HitStart, which would be time in seconds. After the time passed (using an accurate custom wait() based on heartbeat and delta) the hitbox would automatically disable itself - instead of enabling it, doing something, then manually disabling it again.

Granted, it could be very easy to implement by oneself, but it would be a nice feature to see in the native module.

2 Likes

You could look into a Promise for that. With the limited knowledge I have on hand, here’s what you might be looking at:

local function timedHitbox(duration)
    return Promise.try(Hitbox.HitStart, Hitbox):andThen(function ()
        return Promise.delay(duration):andThen(function ()
            return Promise.try(Hitbox.HitStop, Hitbox)
        end)
    end):catch(function (exception)
        warn("Timed hitbox chain failure:", exception)
    end)
end

Having it natively supported in the API, given good use case, could be more helpful than this though. Promises are just one way you could solve for this missing feature through. Very crude example I’ve given and definitely recommend other ways though.

3 Likes

V.3.2 Stable

Small update to optimization and performance. Switched to table.clear for more efficient reuse of tables. Will benefit if you use HitStop a lot.

8 Likes

How can you remove the red trail?

1 Like