Raycast Hitbox 4.01: For all your melee needs!

All the knife system is handled from the server, I only use the client to detect mouse input for the actual throw. I use .OnUpdate to constantly update a position variable, and I set the position to it after anchoring the knife as you suggested, but it is fairly choppy. That’s why I was wondering if there is a better solution, to make it look smooth.

If the server is lagging, that will be its longest delay. The module uses Heartbeat, so it will check for collision between frames. If there is lag, Heartbeat will stall and update less often, which will in turn also make OnHit not as accurate.

Handling anything server sided is choppy because replication runs at around 15 - 20hz (versus the buttery smooth 60hz on the client). If smoothness is the priority, I would opt to run the knife throw on the client side and have other clients replicate the same knife going out of the user, making the actual throwing portion smooth and hit detection a bit more accurate (though you will need to put a bit more server sanity checks to prevent people from just cheating the system).

Server sided knife throw
This choppiness you refer to is the server latency and there is not much you can do about it. You can try tweening to make it a bit smoother.

Client sided knife throw
Preferably, all hit detection should be done on the client with the server doing sanity checks to see if the client is cheating or not. This makes the experience a lot better and makes your game much more responsive in turn for less security.


Alternatively, instead of using OnUpdate to update the position, I would use RaycastResults, and it probably will be more performance efficient since it activates on the hit. This is what I basically used for the server example above.

hitbox.OnHit:Connect(function(hit, _, raycastResult)
     local normal = raycastResult.Normal
     local position = raycastResult.Position

     myKnife.Anchored = true
     myKnife.Position = position
end)
4 Likes

As you just showed, client sided throw is way more smooth. Are you basically recommending that I handle the hitbox including :HitStart() and :HitStop() as well as the tool activation events on the client and fire the server to deal damage/check if the hit was possible? I handled it on the server assuming giving the client control of a hitbox would open up a variety of vulnerabilities.

Alternatively, I set network ownership of the knife from the server, and fired the client to add body movers/ hooked up a touch event as well. When the touch event fired, I anchored the knife, which would replicate to the server. It is my first time using this API, and it’s pretty good, but I chose to do this on the server due to the example game + my own perception.

Yeah, preferably everything on the clients (includes hitstart/stop). Then you can tell other clients to draw the projectile on their screen too.

It won’t be as vulnerable if you do proper sanity checks, it’s just a bit more work but the payoff is worth it. One easy sanity check is to make sure if the client tells the server they hit the target, don’t trust them, instead do a raycast between the aggressor and the hit target to ensure that nothing is between them. You can also do a magnitude check as well, so if the aggressor is a 100 studs away from the target, and your throwing skills should only be around 20 studs, then that’s a red flag. You would also want to check for activation/cooldown speed as well, (don’t want people to be spamming the remote event). These are just a few examples but it all depends on what makes sense in your game.

4 Likes

Hello @TeamSwordphin,

I’m using this amazing module for a game I’m working for. It’s working great but there’re some problems I have.

I have some kind of rush attack where a character will punch multiple times in one go.
I modified the module so there’s a setting where you can disable/enable the feature that the hit box will always take a fresh humanoid. That part works fine. However, sometimes I do the attack, the damage points/red parts are not aligned with the attachments. You can see that in this GIF:

https://gyazo.com/b21d042678a109f63222100c240e36ad

As you can see the damage points are nowhere close to the hands/arms, where it should be. Sometimes it works fine, but most of the time it does this.

Here’s a GIF of whenever it does work, which happens rarely:

https://gyazo.com/6e84c9874d996bf6b9c166c86e480cb7

And here’s an image of the positions of the attachments, which are connected to the hands respectively:

image

Any idea what I did wrong or what’s causing this?
If you need to see the scripts, please DM me.

3 Likes

Is the hitboxing clientside or serverside? Double check the stand’s position on the server side. A previous user with a similar stand had the same issue as you where the raycasts were not following the animation of the stand when using this module server sided.

We found out that the animations and positions doesn’t really replicate properly (still unsure of the source of this problem, either because the stand was a descendant of the player or the player owns the physics of the stand).

The hit box is server sided. Is there a solution?

I’m fairly sure this is simply a replication bug on roblox end but if the stand is controlled by a player this can be easily fixed by moving the hitboxing to the clientside and utilizing proper server sanity checks. This would make your stand gameplay much more responsive in turn for a little bit more work serversided. Refer to a the knife posts examples above if you need some examples of sanity checking.

1 Like

Okay, I can try making combat client-sided. Any idea how to replicate this to other players? I’m a little new to client-sided and server-sided stuff…

Well, you can possibly keep the stand as it is on the server side, but keep the hit detection logic on the client side. Whenever you (the player) commands the stand to attack, the client will also take care of the hit detection (so this means using raycasthitbox on a client’s localscript). Once the stand gets a hit in the localscript, you can use a RemoteEvent to tell the server to damage the target that the stand hit.

Then, refer to this example post below on how the server can interpret the target. You don’t want to damage the target immediately without sanity checks. This ensures you have proper replication to all players while having adequate hitboxes.

Thank you so much, @TeamSwordphin ! You’re very generous and helpful! Here’s the final product, thanks to you:

https://gyazo.com/9416feadb6bc3f271580b132ba5e9291

dont mind that last buggy part lol…

1 Like

Looking good! Happy to help and good luck on your project :smiley:

2 Likes

Hey! I have a question about this module:
I have this sword with a few DmgPoints in it.
When I put it in StarterPack and swing it, the hitboxes work fine.
However, when I put it in ServerStorage and give it to the player via a script the tool works, but the hitboxes don’t (I turned on debug mode, lines weren’t showing up)

Any idea why this is happening?

Strange. Can i see the tool script where you initialized the Hitbox? Maybe i can catch something that you haven’t.

Hello, I upgraded the version of this module from 2.3 to 3.2 and now I am getting this error:

screenshot of code:

screenshot of error:
image

It seems the HitStart and HitStop methods don’t exist when I create a new hitbox. In the documentation the examples are still using the HitStart and HitStop methods so I’m guessing you didn’t remove them. Is there something obvious that I’m doing incorrectly?

Does any print messages come up in the output from RaycastHitbox? (Assuming warning messages are turned on). Are you deinitializing or destroying the hitbox anywhere?

Right when you responded I found the issue. The issue was that the CollectionService tag you have added to the object passed into the Initialize method got removed whenever I equipped it (I passed a tool into the object parameter) which caused the hitbox to deinitialize.

I don’t know why the tag got removed every time I equipped the tool but I managed to fix it by just passing in a different object that contained the DmgPoints.

Hmm that’s strange. Never had that problem when I was working with tools. As far as I know the tag is only removed when the instance you passed it in is destroyed.

Yeah, here you go:

local Players = game:GetService("Players")
local player 
repeat wait(); Players:FindFirstChild(script.Parent.Parent.Name) until script.Parent.Parent:IsA("Model")

local player = Players:FindFirstChild(script.Parent.Parent.Name)
local character = player.Character or player.CharacterAdded:Wait()
while character.Parent ~= workspace do
	if character.Parent ~= workspace then
		character = player.Character or player.CharacterAdded:Wait()
	end
	game:GetService("RunService").RenderStepped:Wait()
end
local humanoid = character:WaitForChild("Humanoid")

local trail = script.Parent.Blade.Trail

local currentlyAttacking = false
local canAttack = script.Parent.canAttack

--Hitboxes--
local RS = game:GetService("ReplicatedStorage")
local RAYCAST_HITBOX = require(RS.RaycastHitboxV3)

local ignoreList = {character}
local newHitbox = RAYCAST_HITBOX:Initialize(script.Parent.Blade, ignoreList)

newHitbox.OnHit:Connect(function(hit, humanoid)
	if character.RageActive.Value == true then
		humanoid:TakeDamage(10 * 1.5)
	else 
		humanoid:TakeDamage(10)
	end
	
	if hit.Name == "HeadHitbox" then
		if humanoid.Health <= humanoid.MaxHealth * 1/10 then
			for _, v in pairs(hit:GetChildren()) do
				v:Destroy()
			end

			humanoid.Head:Destroy()

			local blood = game.ReplicatedStorage.Blood:Clone()
			blood.Parent = humanoid.Parent.Torso
		end	
	end
end)

--Attack Functions--

function Slash()
	newHitbox:HitStart()
	canAttack.Value = false
	currentlyAttacking = true
	trail.Enabled = true
	wait(0.35)
	newHitbox:HitStop()
	trail.Enabled = false
	currentlyAttacking = false
end

function Attack()
	if canAttack.Value and not currentlyAttacking then
		Slash()
	end
end

script.Parent.cooldownFalse.OnServerEvent:Connect(function()
	canAttack.Value = true
end)

script.Parent.Activated:Connect(Attack)

The thing is that I have another tool which essentially has the same code in ServerStorage, and its given the exact same way; and only that one works.

Hmm how are you giving the tool to the player? I could not replicate the problem with the code in your post.


My tests has this one script in ServerScriptService:

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		wait(1)
		game.ServerStorage.Tool:Clone().Parent = player.Backpack
	end)
end)

And here’s a slightly modified version of your script for my testing (I doubt I fixed the main problem though for any edits I did)

local Players = game:GetService("Players")
local player 
repeat wait(); Players:FindFirstChild(script.Parent.Parent.Name) until script.Parent.Parent:IsA("Model")

local player = Players:FindFirstChild(script.Parent.Parent.Name)
local character = player.Character or player.CharacterAdded:Wait()
while character.Parent ~= workspace do
	if character.Parent ~= workspace then
		character = player.Character or player.CharacterAdded:Wait()
	end
	game:GetService("RunService").RenderStepped:Wait()
end
local humanoid = character:WaitForChild("Humanoid")

local currentlyAttacking = false
local canAttack = script.Parent.canAttack

--Hitboxes--
local RS = game:GetService("ReplicatedStorage")
local RAYCAST_HITBOX = require(RS.RaycastHitboxV3)

local ignoreList = {character}
local newHitbox = RAYCAST_HITBOX:Initialize(script.Parent.Handle, ignoreList)
newHitbox:DebugMode(true)

newHitbox.OnHit:Connect(function(hit, humanoid)
	print(hit)
end)

--Attack Functions--

function Slash()
	newHitbox:HitStart()
	canAttack.Value = false
	currentlyAttacking = true
	wait(0.35)
	newHitbox:HitStop()
	currentlyAttacking = false
	
	canAttack.Value = true
end

function Attack()
	if canAttack.Value and not currentlyAttacking then
		Slash()
	end
end

--- I moved canAttack.Value = true into the Slash() function
--script.Parent.cooldownFalse.OnServerEvent:Connect(function()
--	canAttack.Value = true
--end)

script.Parent.Activated:Connect(Attack)

Upon cloning and parenting the tool to the player’s backpack, the hitbox and tool work as expected. Is the RaycastModule printing anything in the output? Does it see any of the attachments in the sword?

1 Like