Hitbox Module by Salvatore

There aren’t many hitbox resources not made for projectiles that are easy to use right out of the box, even if they offload checks to the client they often do not include basic authentication to prevent exploiting. This is why I recommend you use my hitbox module, it authenticates to prevent exploiting and accommodates for latency (lagging players) and velocity dynamically. Its easy to use and requires no additional authentication checks right away.

This resource fixes character latency by offloading to clients without network latency interfering. Offload hitboxes to clients whenever the hitbox position changes relative to the character’s position i.e. welding a hitbox to the character.

Here is a showcase using it in my own game.

STEP 1: DOWNLOAD

Download the module here!

STEP 2: SETUP

Place the module in ReplicatedStorage and require it on both the server and client.

-- Example
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Hitbox = require(ReplicatedStorage:WaitForChild("Hitbox")

STEP 3: GET YOUR PART

Locate the part you want to create a hitbox with, OR create one using the .Part() method.

.Part(
	Size: Vector3 | number, -- If Vector3 then returns Block, if number then returns Sphere
	WeldTo: BasePart?, -- Optionally weld the part to another part
	Offset: CFrame? -- Optional offset for the weld
) -> Part -- Returns a part
-- Example
local Part = workspace.Part
-- OR
local Character = workspace.Player1 -- path to character
local Size = 5
local Part = Hitbox.Part(
Vector3.new(Size, Size, Size), -- Size: or use a number (the part will become a sphere)
Character.PrimaryPart, -- Weld To Part: Optional, welds the part to this part.
CFrame.new(0, -.5, ((Size / 2) * -1) -- Weld Offset: Optional, offsets the weld around another part.
)

STEP 4: CREATE A HITBOX

It is recommended that you do NOT create a new hitbox every time you use an ability. You should create one hitbox per ability when the ability script loads. The hitbox should be created long before it is used. It is also recommended that you offload hitboxes to the client if you intend for the hitbox to be moving with the character or when welding the hitbox to the character.

.New(
	Focus: BasePart, -- The part you want the hitbox to use
	Parameters: OverlapParams?, -- Overlap parameters for whitelist/blacklist
	Client: Player?, -- Optional, offload the hitbox onto a client
	Debug: boolean? -- Whether or not to show debug visuals in studio
) -> hitbox -- returns a hitbox

STEP 5: SETUP CONNECTIONS

Each hitbox comes with several events you can connect to.

To listen for characters being hit connect to the .Hit() event, this event will return a table of characters that get hit.

hitbox.Hit:Connect(function(characters: {Model})
	-- Make sure to use a task.spawn() here so effects happen to all hit characters simultaneously.
end)

You can listen for characters entering or leaving the hitbox with the .TouchBegan() & .TouchEnded() events.

hitbox.TouchBegan:Connect(function(character: Model) 
	-- logic here
end)

hitbox.TouchEnded:Connect(function(character: Model) 
	-- logic here
end)

You can listen for the hitbox being started or stopped with the .Started() & .Stopped() events.

hitbox.Started:Connect(function() 
	-- logic here
end)

hitbox.Stopped:Connect(function() 
	-- logic here
end)

You can listen for the hitbox being destroyed with the .Destroying() method. You only need to connect to this once.

hitbox.Destroying:Once(function() 
	-- logic here
end)

STEP 6: CONTROL THE HITBOX

For one time hit detection, use the :Once() method. A table of characters that were hit will be returned. If no characters were hit an empty table is returned.

:Once() -> {Characters}

To activate the hitbox use the :Start() method and to stop it use the :Stop() method. Once a character has been hit it can no longer be hit. If you want the hitbox to automatically stop after a given duration then pass a number as the Timer: number? parameter. If you would like the hitbox to have reoccurring hits to the same character pass a number to the Debounce: number? parameter.

hitbox:Start(Timer: number?, Debounce: number?)
hitbox:Stop()

Hitboxes store characters so they cant be hit again. If you would like them to be hit again, use the :Clear() method. You should do this every time you plan on reusing the hitbox.

hitbox:Clear()

You can weld the hitbox to a part with an offset, or change the hitboxes current weld and offset, with the :Weld() method. This will return a weld instance.

hitbox:Weld(Part: BasePart, Offset: CFrame?) -> Weld

To see if the hitbox has been started, use the :IsActive() method. This will return a boolean.

hitbox:IsActive() -> boolean

Once you no longer need the hitbox, use the :Destroy() method to remove it from server memory. If the part a hitbox was made from gets destroyed the hitbox object is automatically destroyed too.

hitbox:Destroy()

EXAMPLE CODE

-- Example

local damage = 10
local debounce = .5
local duration = 3
local hitboxSize = Vector3.new(5, 5, 7)

local Character = game.Players.Player1.Character

local params = OverlapParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = {Character}

hitbox = Hitbox.New(
	Hitbox.Part(hitboxSize, hrp, CFrame.new(0,-.5,((hitboxSize.Z / 2) * -1)) ), 
	params, 
	Players:GetPlayerFromCharacter(Character),
	true
)

Character.Destroying:Once(function()
	hitbox.Focus:Destroy()
	hitbox:Destroy()
end)

hitbox.Hit:Connect(function(characters: {Model})
	for _,character in pairs(characters) do
		task.spawn(function()
			local humanoid = character:FindFirstChildOfClass("Humanoid")
			humanoid:TakeDamage(damage)
		end)
	end
end)

hitbox.TouchBegan:Connect(function(character: Model)
	print(character.Name.." has entered the hitbox.")
end)

hitbox.TouchEnded:Connect(function(character: Model)
	print(character.Name.." has left the hitbox.")
end)

hitbox.Started:Connect(function()
	print("Hitbox started.")
end)

hitbox.Stopped:Connect(function()
	print("Hitbox stopped.")
end)

hitbox.Destroying:Once(function()
	print("Hitbox successfully destroyed.")
end)

-- Using the hitbox

hitbox:Clear()
hitbox:Start(duration, debounce)

-- OR

hitbox:Clear()
hitbox:Start(nil, debounce)
task.wait(duration)
hitbox:Stop()

If you have any question or feature requests, please let me know!

16 Likes

This looks interesting, but is there any reason I should use this over HitboxClass? For one, I know that HitboxClass has more features like hitting objects instead of humanoids. Someone else also made an edited version of hitbox class to include hit point information. Also, how does the client part of the hitbox work? That’s what I’m most interested about. If it’s better than how HitboxClass does it, I might switch

2 Likes

why does it need httpservice ccccccccc

1 Like

For UUID, I use it to ensure clients find hitbox focuses. People usually create parts for hitboxes right before they make the object and the client doesn’t have enough time to see it replicate- since the parameter would be nil a UUID is passed instead. I could just recursively invoke the client but I felt as if that was unnecessary.

1 Like

Not really,

In all honesty its not much different. I actually made this because I felt as if HitboxClass was a bit too verbose for me personally. HitboxClass doesn’t have any built-in validation, this resource does. Now- validation isn’t hard to do anyways, in most cases its just a magnitude check with forgiveness for latency. I just wanted to ensure there’s some safety if users who aren’t too familiar with exploit precaution were to use this.

I did do something differently for the client, just like HitboxClass it uses a heartbeat connection but instead of sending characters that are hit to the server it sends characters that enter or leave the hitbox. For hitboxes that have a short debounce I felt if it was unnecessary to have to send hit information so many times per second, this approach helps. There isn’t an approach that is ‘better’ per se, as long as hitbox modules offload runservice connections to clients and validate what is returned then its likely as performant as could be.

I wanted hit caching but also wanted to be able to use hitboxes for zones, some games use zones for no-pvp areas, npc spawning, and doors that open when you walk up to them- that sort of thing. .TouchBegan() and .TouchEnded() operate separately from the hit cache which I think is a nice feature.

2 Likes

I tried the client parameter in your module—it works perfectly on studio, but in-game, the latency delay from sending to client back to server is too huge and the hitbox is deleted before it can even detect hits! For example, if I did hitbox:Start(.1) with client parameter, the hitbox will never hit anything because of the latency delay. For now, I think I can only use it server side. And yes, I am creating hitboxes at the start of the script, long before using them, as you recommended

3 Likes

I easily found a fix by just using remote function lol

1 Like

Could you elaborate, please? I wanted to get around to helping you- but I couldn’t recreate the problem. For me, its been working fine without any problems.

How did using remote function solve it?

1 Like

its as simple as this

local VerifyOnClient = Packet("_HitboxClientVerify",Packet.Instance):Response(Packet.Boolean8)
if RunService:IsClient() then
	VerifyOnClient.OnClientInvoke = function(Focus)
		if Hitbox.Hitboxes[Focus] then
			print("hitbox verified on client")
			return true
		end
	end
end

--- inside :Start()...
	if Timer then
		task.spawn(function()
			if RunService:IsServer() and self.Client then
				VerifyOnClient:FireClient(self.Client,self.Focus)
			end
			task.wait(Timer)
			if self.__Timestamp == timestamp then
				self:Stop()
			end
		end)
	end
1 Like

hm im used to mucachohitbox one idk if i should use this

1 Like

I use it. it’s a top tier hitbox module, although muchacho hitbox is a good choice too

what top tier? hitboxclass? or this module?

I think this module outperforms hitbox class in every way that matters. Hitbox class is way too verbose, and it’s also old. It takes so many parameters to start a hitbox, and not just that, but the type checking isn’t really the best. There’s newer modules which are made by different owners based on hitbox class, but they’re even more verbose. It’s also really easy to edit this hitbox module. The only reason I can see someone sticking to it is because it doesn’t rely on instances (this module does indeed rely on parts, and not only that, uses parts in part spatial queries. Not that much of an issue because you can change it but still).
You might be asking, “what about projectiles” because of the hit object signal, but there’s also a projectile module around which I think should be used in place for that.
Muchacho hitbox is pretty clean and easy to use, and if you like it, you can stick with it.

1 Like

nvm, its really good i was surprised as hell, i was hoping i could change its size/cframe freely and it’ll follow the size/cframe kinda like runtime thing

1 Like

Just published a new version- I changed the remote to function, there’s no more need to keep https service enabled. Added a check to ensure if hitboxes are welded on the server then given network ownership to a client the client cannot break the weld locally. Also, @wima7114 I believe you can resize the part as much as you like on the server and it will work regardless of offloading to the client. Moving it and resizing it should work.

I also added a new method :AssignNetwork() to reassign network ownership correctly if you’ve welded the hitbox to a player character.

If anyone has any question please feel free to ask, thank you!

1 Like

let’s go updateddddddddddddddddddddddddd

edit : i think the only problem here is that the hitboxes share the same event if the same hitbox used for different attacks, they’ll broke or just do something weird i probably just reccomend using different key wwhenever hitbox is started so that every event is on their own and not shared,
cuz i ran into another problem where after i land some grab moves that makes the opponent has iframes, then set the iframes to false after the grab, when i try to hit M1 (different hitbox) it wont work not even printing the hit/hit character.

edit 2: nvm i randomly fixed it, but i highly reccomend using a different key whenever hitbox is started

also i found a random bug where i can stack up something with the hitbox if i miss it 3 times using the Connect/Once then hit the enemy after that it’ll return the same enemy thrice and so does the damage

May you please elaborate on how you got the hitbox to return the same character, I’m having trouble recreating the bug. If I can read your code it would help.

Usually, the setup for hitboxes is misinterpreted by users. The best practice for creating hitboxes is to create it once for every ability, and then each time the ability is used use the hitbox that you just created. There is no need to create a hitbox every time the ability is used.

Additionally, the signals you can connect to being .Hit(), .Started(), .Stopped() etc. should be connected once. If you were to connect to them every time the ability was used then the damage would stack. You only need to connect to these once.

If you send me your code I can rewrite it so that the hitbox and connections are only created once.

i kinda fixed it, the only fix is just to destroy touchbegan event, like this

hitbox.TouchBegan:Destroy()

also i made the hitbox when player joins and then use it for later,

edit : overall when using this hitbox its not great, it keep breaking for absolutely no reason even other script didnt touch an atom of this hitbox and yet it breaks i tried bunch of things even deleting detection for iframes, it still breaks for NO reason. it had better performance yes, its simple, but its really really frustating to work with, it was like that for days, it kinda dissapoints me but whatever let it be. ill just move to mucachohitbox/hitboxclass

I’m having some issue. I’m following the example provided. I’m creating a table that holds it all.

Server side:

local Damage = 10
local Debounce = 0.5
local Duration = 3
local HitboxSize = Vector3.new(8, 7, 9)
local Hitboxes = {}

CharacterAdded event below

player.CharacterAdded:Connect(function(character)
		
		Hitboxes[character] = {}
		
		local params = OverlapParams.new()
		params.FilterType = Enum.RaycastFilterType.Exclude
		params.FilterDescendantsInstances = {character}
		
		Hitboxes[character] = HitboxModule.New(
			HitboxModule.Part(HitboxSize, character.HumanoidRootPart, CFrame.new(0,-.5,((HitboxSize.Z / 2) * -1)) ), 
			params, 
			Players:GetPlayerFromCharacter(character),
			true
		)
		
		character.Destroying:Once(function()
			print('Destroying')
			Hitboxes[character].Focus:Destroy()
			Hitboxes[character]:Destroy()
			Hitboxes[character] = nil
		end)
		
		Hitboxes[character].Hit:Connect(function(characters: {Model})
			for _,character in pairs(characters) do
				task.spawn(function()
					local humanoid = character:FindFirstChildOfClass("Humanoid")
					humanoid:TakeDamage(Damage)
				end)
			end
		end)

		Hitboxes[character].TouchBegan:Connect(function(character: Model)
			print(character.Name.." has entered the hitbox.")
		end)

		Hitboxes[character].TouchEnded:Connect(function(character: Model)
			print(character.Name.." has left the hitbox.")
		end)

		Hitboxes[character].Started:Connect(function()
			print("Hitbox started.")
		end)

		Hitboxes[character].Stopped:Connect(function()
			print("Hitbox stopped.")
		end)

		Hitboxes[character].Destroying:Once(function()
			print("Hitbox successfully destroyed.")
		end)
	end)

Whenever I try and do a m1 and call the hitbox, it doesn’t exist…

	if Hitboxes[character] and Hitboxes[character]:IsActive() then
		Hitboxes[character]:Once()
	end

I’m not sure what I’m doing wrong, any help would be appreciated

An easy way to figure out what you’re doing wrong is to open the output window and look into the errors while you play test