Raycast Hitbox 4.01: For all your melee needs!

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

That is the debug trail to see where the raycasts are going. You can turn off DebugMode in the module settings itself.

1 Like

Thanks, but do you know how to make it work ONLY on people NOT in your current team?

1 Like

Since this module does not support team filtering, you will need to incorporate it yourself. There are a few ways to go about this. The first way can use the ignore list with Initialize to ignore the ally team:

local allyTeam = game:GetService("Teams")["My Team Name"]
local ignoreList = {} 

for _, ally in ipairs(allyTeam:GetPlayers()) do
     if ally.Character then
        table.insert(ignoreList, ally.Character)
     end  
end

local hitbox = RaycastModule:Initialize(weapon, ignoreList)

Another way is to use collision groups (each team has it’s own collision group which ignores itself) and using the hitbox’s raycastParams to ignore it.

local hitbox = RaycastModule:Initialize(weapon)
hitbox.raycastParams.CollisionGroup = myCollisionGroup

The last way can be checking merely on Hit (which is the classic way to go about things).

local allyTeam = game:GetService("Teams")["My Team Name"]

hitbox.OnHit:Connect(function(hit, humanoid)
     local targetPlayer = game.Players:GetPlayerFromCharacter(hit.Parent)
     if targetPlayer and targetPlayer.TeamColor ~= allyTeam.TeamColor then
          humanoid:TakeDamage(10)
     end
end)
3 Likes

I am using the module to simulate a barrage of punches in front of the character (yeah, JoJo game). I am being a bit lazy and not raycasting on each flying punch, but instead I just want a simple hitbox in front of the character that represents the area where the punches are landing.

I am using :HitStart() and :HitStop() fairly quickly, something like every .25 seconds but I notice that it will not register a new hit unless the ray attachments actually move. If I wiggle around and move the hitbox across the target, it will register hits just fine.

I have only thought of two ways so far to fix this, one is to actually CFrame the hitbox a tiny bit every time I cycle the :HitStop

Another method is maybe the Link Attachments? But I am not sure if they will register hits unless there is motion as well.

So in short, it seems that hits are only registered if there is motion, a non-moving HitPoint will not register a hit.

Am I missing something in my setup or thinking? Thanks!

1 Like

Apologies, this post contains some misleading or inaccurate information. I did forget about some of the API members and actually a way that this could work. Please view discussion below for a proper resolution. Idle hitboxes also work with this.

Archived post

This resource probably won’t be good for your use case. RaycastHitbox is primarily intended for moving melee hitboxes, moved with animations or because of actual physical movement.

You might want to think about opting for lightweight solutions, such as a single cast or some from your own attachment setup within the hitbox or from your NPC’s root part. You would also be able to cast as in when the arm thrusts out during the punch rather than calling HitStart and HitStop frequently across a small time frame (instantaneous and once > quickly toggled). It does mean losing a few of the goodies this module offers and having to make your own sort of module to help better work with your hitboxes.

It provides an interesting use case though. Toggle features, depending on what they are, come well equipped with a singls-use method as well. For example, ParticleEmitters have the Enabled property but then also Emit which will send out a number of particles immediately. Having a method native to RCHB which allows developers to cast a single time out from their attachments might be pretty useful for use cases like this.

I was sure someone might answer in this way, but this module is very useful to me in other ways throughout the game, I am more likely to try and make this work by wiggling the hitbox part in some way just to keep things “simple” than to use a separate solution for this one part of the game.

I guess I’m still curious if the Linked Attachments work differently because they have a ray stretched across two point instead of single points that require some motion to cast the ray.

Does anyone know if this is the case?

1 Like