Kult's V3 Hitbox

Good day, fellow developers,

After receiving some helpful feedback and giving it some thought, I’ve made some great improvements to the V3 Hitbox System! I’d love to hear your feedback and bug reports, so please feel free to share them.

Let me start by explaining the changes I’ve made to the way you input data. I’ve simplified it quite a bit. Now, all you need to provide is the Offset (which uses the HumanoidRootPart as the reference point), the Size, and the function that handles the Target and the Attacker. What’s notable is that this version no longer relies on parts as hitboxes. If you’re curious about what the new hitboxes look like, just keep reading for more info.

local Input = {}
Input['M1'] = {
	Offset = CFrame.new(0,0,-2.5),
	Size = Vector3.new(5,5,5),
	Function = function(Target, Attacker)
		Target:FindFirstChild('Humanoid'):TakeDamage(2.5)
		print(Attacker.Name..' damaged '..Target.Name)
		task.wait(1)
	end,
}
return Input

Now, let’s talk about Object-Oriented Programming (OOP). I’ve made some adjustments to give you more control over when the hitbox is active or not. If you want the HitBox to stay visible, just use “true” when calling the StartDetection method .

local HitboxModule = {}
HitboxModule.__index = HitboxModule

local ActiveHitboxes = require(script.ActiveHitboxes)
local Input = require(script.Input)

function HitboxModule.new(Player, Type)
	if ActiveHitboxes[Player] then return warn('Player is already running a hitbox!') end
	local self = setmetatable({}, HitboxModule)
	self.Player = Player
	self.Type = Type
	self.Hitbox = nil
	self.InDetection = false
	return self
end 

function HitboxModule:StartDetection(Bool)
	if self.InDetection then return warn('Already has been ranned!') end
	self.InDetection = true
	local Character = self.Player.Character or self.Player.CharacterAdded:Wait()
	local HRP = Character:WaitForChild('HumanoidRootPart')
	local Settings = Input[self.Type]
	local OP = OverlapParams.new()
	OP.FilterDescendantsInstances = {Character}
	OP.FilterType = Enum.RaycastFilterType.Exclude
	ActiveHitboxes[self.Player] = {Settings['Offset'], Settings['Size'], OP, Settings['Function'], self}
	if Bool == true then
		local Character = self.Player.Character or self.Player.CharacterAdded:Wait()
		local HRP = Character:WaitForChild('HumanoidRootPart')
		local Settings = Input[self.Type]

		local Weld = Instance.new('WeldConstraint', HRP)
		local HitPart = Instance.new('Part', HRP)
		HitPart.Size = Settings['Size']
		HitPart.CFrame = HRP.CFrame * Settings['Offset']
		HitPart.Anchored = false
		HitPart.CanCollide = false
		HitPart.Massless = true
		HitPart.Transparency = .75
		HitPart.Color = Color3.fromRGB(255,0,0)
		Weld.Part0 = HitPart
		Weld.Part1 = HRP
		self.Hitbox = {HitPart, Weld}
	end
end

function HitboxModule:StopDetection()
	if not self.InDetection then return warn('Already has been stopped!') end
	if not ActiveHitboxes[self.Player] then return end
	ActiveHitboxes[self.Player] = nil
	self.InDetection = false
	if self.Hitbox == nil then return end
	for _, obj in self.Hitbox do
		obj:Destroy()
	end
end

function HitboxModule:Disconnect(Bool)
	if Bool then
		self:StopDetection()
	end
	if self.InDetection then return warn('Stop Detection First!') end
	setmetatable(self, nil)
	self = nil
end

return HitboxModule

Moving on to the main script that handles hitbox detection. I’ve added a separate dictionary for each player when they join the game. This helps manage their individual detection models. These player-specific dictionaries are removed either when the detection is complete or when a player leaves, which helps keep your game running smoothly. Plus, I’ve simplified things by using a single RunService Loop to handle all hitbox detections, which makes things faster .

local RS = game:GetService('ReplicatedStorage')
local ActiveHitboxes = require(RS.HitboxV3.ActiveHitboxes)

local YieldModels = {}
local Models = {}

game.Players.PlayerAdded:Connect(function(Player)
	YieldModels[Player] = {}
	Models[Player] = {}
end)

game.Players.PlayerRemoving:Connect(function(Player)
	YieldModels[Player] = nil
	Models[Player] = nil
	if ActiveHitboxes[Player] then
		local Hitbox = ActiveHitboxes[Player][5]
		Hitbox:Disconnect(true)
		ActiveHitboxes[Player] = nil
		warn(Player.Name..' left during detection. Cleaned up!')
	end
end)

game:GetService('RunService').Heartbeat:Connect(function()
	for Player, Settings in ActiveHitboxes do
		local Character = Player.Character or Player.CharacterAdded:Wait()
		local Humanoid = Character:WaitForChild('Humanoid')
		local HRP = Character:WaitForChild('HumanoidRootPart')
		
		if Humanoid:GetState() == Enum.HumanoidStateType.Dead then return end
		local Detection = game.Workspace:GetPartBoundsInBox(HRP.CFrame * Settings[1], Settings[2], Settings[3])
		if #Detection == 0 then return end
		
		for _, obj in Detection do
			local Model = obj.Parent
			if Models[Player][Model] then continue end
			local Humanoid = Model:FindFirstChild('Humanoid')
			if not Humanoid or not Model:IsA('Model') then continue end
			if Humanoid:GetState() == Enum.HumanoidStateType.Dead then return end
			Models[Player][obj.Parent] = obj.Parent
		end
		
		for _, Model in Models[Player] do
			task.spawn(function()
				if YieldModels[Player][Model] then return end
				YieldModels[Player][Model] = Model
				Settings[4](Model, Character)
				if YieldModels[Player] then
					YieldModels[Player][Model] = nil
				end
				if Models[Player] then
					Models[Player][Model] = nil
				end
			end)
		end
	end	
end)

To use this system, start by checking if the required input settings exist . It’s important to ensure that hitbox exists because this version only supports one at a time. You can easily turn the hitbox on and off using the StartDetection() and StopDetection() methods. Don’t forget to use the Disconnect() method when you’re done to free up memory .

local RS = game:GetService('ReplicatedStorage')
local HitboxV3 = require(RS.HitboxV3)
local Settings = require(RS.HitboxV3.Input)
local InputRemote = RS.InputRemote

InputRemote.OnServerEvent:Connect(function(Player, Type)
	if not Settings[Type] then return end
	if Type == 'M1' then
		local Hitbox = HitboxV3.new(Player, Type)
		if not Hitbox then return end
		Hitbox:StartDetection(true)
		task.wait(5)
		Hitbox:StopDetection()
		Hitbox:Disconnect()
	end
end)

Your support and feedback are greatly appreciated!

4 Likes

the model is offsale, put it on sale or something

1 Like

What do you mean? It is onsale.

1 Like

Fixed a bug with the detection for humanoid.

I have made some changes to it. It’s actually protected now, before you were able to spam it kinda, but now you shouldn’t be able to anymore. I added a QOL feature where you can disconnect and stop detection at the same time if you do :StopDetection(true).

Added some fixes, changes, and some type checks.

Also, for disclaimer, to properly add cooldowns, you need to add task.wait() after :StopDetection() and then :Disconnect() after done it’s waiting.

local Hitbox = HitboxV3.new(Player, 'M1')
if not Hitbox then return end
Hitbox:StartDetection(true)
task.wait(.25)
Hitbox:StopDetection()
task.wait(1)  -- wait 1 second until you can m1 again.
Hitbox:Disconnect()