Kult's V2 Hitbox System [Combat System]

Greetings fellow developers,

I am excited to introduce the newest version of my Hitbox System - V2! Based on the feedback received from the previous version, I have worked to improve the stability, customization, and most importantly, make it exploiter-free.

To ensure the safety and integrity of the system, I have removed the client-sided detection. However, in doing so, I have provided more customization options and better organization for ease of use.

Let’s start with the settings. The first setting is the basepart, where you can insert the size of the hitbox and choose any shape that suits your needs. If you require a more reliable hitbox, you can select sphere over block. Orientation is used to rotate the hitbox, which can be set to false if you do not want to change it. Offset is used to adjust the hitbox position in relation to your character. Lastly, transparency can be used to change the opacity of the hitbox.

The second setting includes the functions of the hitbox. Damage is the amount of damage dealt to any humanoid interacting with the hitbox. DebrisTime is the duration for which the hitbox remains active. DebounceTime is the time required between two activations. DebouncePerHit is used for the DetectMulti setting, where you can insert the amount of time between hits.

Moving on, we have the hitbox settings. Burst mode allows for multiple hitboxes to be created, with the amount and time between each burst set by the user. Standard mode creates just one hitbox. Weld can be set to true if you want the hitbox to stick with your character. Even if you set it to false, a weld will be created and destroyed later for better stability.

Lastly, we have the detection settings. DetectOnce is the usual detection method, while DetectMulti can detect multiple hits. It has a global debounce for everyone.

local Settings = {}

Settings.M1 = {
	['BasePart'] = {
		Size = Vector3.new(5,5,5),
		Shape = Enum.PartType.Block,
		Orientation = false, -- leave false if not
		Offset = false, --leave false if not
		Transparency = 1,
	},

	['Functions'] = {
		Damage = 10,
		DebrisTime = 10,
		DebounceTime = 1.5,
		DebouncePerHit = 1,
	},
	
	["Types"] = {
		["Hitbox"] = {
			Burst = {
				ON = true,
				Amount = 4,
				TimePerBurst = .05,
			},
			Standard = false,
			Weld = false
		},
		["Detection"] = {
			DetectOnce = true,
			DetectMulti = false
		}
	}
}

return Settings

These are all the functions used for the main script. First, a new hitbox is created, and information is collected from the settings and the part. Then, the properties are set to the part using “SetProps.” It may seem unnecessary, but it’s used for other functions. You can turn on “DevMode” to receive prints of the hitbox creations and detections and make the hitbox visible. “SetCD” is primarily used for the damage part, where it checks if the character is in the table. If so, it returns to nothing; otherwise, it inserts the model into the table as a dictionary and continues damaging them. It then waits after the DebounceTime and removes them from the table. Lastly, “SetWeld” sets the weld of the hitbox to your HumanoidRootPart. It checks if you checked off “Weld” or not, based on the settings above.

local Hitbox = {}
Hitbox.__index = Hitbox

local CS = game:GetService("CollectionService")

function Hitbox.new(Settings, Part)
	local NewHitbox = setmetatable({}, Hitbox)
	
	NewHitbox.Part = Part
	NewHitbox.Size = Settings["BasePart"].Size
	NewHitbox.Shape = Settings["BasePart"].Shape
	NewHitbox.Orientation = Settings["BasePart"].Orientation
	NewHitbox.Offset = Settings['BasePart'].Offset
	NewHitbox.Transparency = Settings["BasePart"].Transparency
	
	NewHitbox.Damage = Settings["Functions"].Damage
	NewHitbox.DebrisTime = Settings["Functions"].DebrisTime
	NewHitbox.DebounceTime = Settings["Functions"].DebounceTime
	NewHitbox.DebouncePerHit = Settings['Functions'].DebouncePerHit
	NewHitbox.Weld = Settings["Types"]["Hitbox"].Weld
	return NewHitbox
end

function Hitbox:SetProps()
	self.Part.Size = self.Size
	self.Part.Shape = self.Shape 
	if self.Orientation ~= false then
		self.Part.Orientation = self.Orientation
	end
	if self.Offset ~= false then
		self.Part.CFrame = self.Part.CFrame * self.Offset
	end
	self.Part.Transparency = self.Transparency
	
	if CS:HasTag(self.Part, "DevMode") then
		self.Part.Transparency = 0.75
		self.Part.Color = Color3.fromRGB(255, 0, 0)
		self.Part.Material = Enum.Material.Neon
		print('Properties Succesfully Set.')
	end
end

function Hitbox:DevMode()
	CS:AddTag(self.Part, "DevMode")
	print('DevMode is Activated.')
end

function Hitbox:SetCD(CD, model, list, Type)
	if CD[model] then return end
	CD[model] = model
	local hum = model.Humanoid
	
	if hum:IsA('Humanoid') and hum.Health > 0 then
		if CS:HasTag(self.Part, "DevMode") then
			print('Damaged '.. model.Name)
		end
		hum:TakeDamage(self.Damage)
	end
	task.wait(self.DebouncePerHit)
	if Type.DetectMulti then
		CD[model] = nil
		elseif Type.DetectOnce then return
	end
end

function Hitbox:SetWeld(hrp, HitPart)
	task.spawn(function()
		if self.Weld == true then
			task.spawn(function()

			end)
			local WeldConstraint = Instance.new("WeldConstraint", hrp)
			WeldConstraint.Part0 = hrp
			WeldConstraint.Part1 = HitPart
		elseif self.Weld == false then
			local WeldConstraint = Instance.new("WeldConstraint", hrp)
			WeldConstraint.Part0 = hrp
			WeldConstraint.Part1 = HitPart
			task.wait(.05)
			WeldConstraint:Destroy()
			self.Part.Anchored = true
		end
	end)
end

return Hitbox

The inputs that will be used to detect if you can use the Hitbox System V2 and which input it will be activated are not mentioned in the given information.

local Input = {
	[Enum.KeyCode.E] = {
		["Name"] = "M1",
		["CanInput"] = true
	}
}
return Input

To use this input module, it requires the client to send the input keycode from the UserInputService to the server. The script checks for the presence of the input keycode in order to proceed with the hitbox creation process.

local RS = game:GetService("ReplicatedStorage")

game:GetService("UserInputService").InputBegan:Connect(function(input, process)
	if process then return end
	if input.KeyCode == Enum.KeyCode.E then
		RS.HitboxModule.HitboxRemote:FireServer(input.KeyCode)
	end
end)

The V2 Hitbox system has several safety checks to ensure that it is reliable and robust. Firstly, it checks if the input from the client is connected to any inputs in the module and if the “CanInput” is set to true. Then, it uses the CollectionService to check if the player is in cooldown, and if not, it creates a cooldown to the humanoidrootpart.

Once the safety checks are complete, it checks if burst mode or standard mode is on and creates a new hitbox part with the appropriate properties. It also includes a “DevMode” function for debugs to be sent back. The hitbox is then welded at the end for fewer issues.

After the hitbox is fully set up, it checks for collision using spatial queries. A separate table is used to insert models into to successfully debounce global events and avoid issues. It then creates a separate thread to avoid yielding and breaking the script, using the “SetCD” function to put the model into another table called “CDs” and removes it after the DebounceTime is done and the damage is fully done. Debris is also used to ensure that the hitbox is removed over time without errors.

At the end, the script controls the deletion of the hitbox, clearing the cooldown, runservice, and the cooldown tag that was created in the humanoidrootpart. This process is the same for both Burst and standard modes.

local RS = game:GetService("ReplicatedStorage")
local WS = game:GetService("Workspace")
local Debris = game:GetService("Debris")
local CS = game:GetService("CollectionService")

local HitboxModule = require(RS.HitboxModule)
local Settings = require(RS.HitboxModule.Settings)
local Input = require(RS.HitboxModule.Input)

local OP = OverlapParams.new()
OP.FilterType = Enum.RaycastFilterType.Blacklist

RS.HitboxModule.HitboxRemote.OnServerEvent:Connect(function(plr, input)
	if input == Input then return end
	local chr = plr.Character
	local hrp = chr:WaitForChild("HumanoidRootPart")
	
	if CS:HasTag(hrp, "CD") then return end
	local NewSettings = Settings[Input[input].Name]
	
	if Input[input].CanInput == true then
		local Cooldown = CS:AddTag(hrp, "CD")
		local CDs = {}
		OP.FilterDescendantsInstances = {chr:GetChildren(), WS.Baseplate}

		if NewSettings.Types.Hitbox.Standard == true then
			local HitPart = Instance.new("Part")
			HitPart.Massless = true
			HitPart.CanCollide = false
			HitPart.CFrame = hrp.CFrame * NewSettings.Offset
			HitPart.Parent = hrp

			local Hitbox = HitboxModule.new(NewSettings, HitPart)
			Hitbox:DevMode()
			Hitbox:SetProps()
			Hitbox:SetWeld(hrp, HitPart)
			Debris:AddItem(HitPart, Hitbox.DebrisTime)

			local Models = {}

			local HB = game:GetService("RunService").Heartbeat:Connect(function(DT)
				local Hit = WS:GetPartBoundsInBox(HitPart.CFrame, HitPart.Size, OP)

				if #Hit > 0 then
					for _, HPart in Hit do
						if Models[HPart.Parent] then continue end
						if HPart.Parent:FindFirstChildWhichIsA('Humanoid') and HPart.Parent:IsA('Model') then
							Models[HPart.Parent] = HPart.Parent
						end
					end
					for _, Model in Models do
						task.spawn(function()
							Hitbox:SetCD(CDs, Model, Models, NewSettings.Types.Detection)
							if NewSettings.Types.Detection.DetectMulti then
								Models[Model] = nil
							elseif NewSettings.Types.Detection.DetectOnce then return end
						end)
					end
				end
			end)

			task.spawn(function()
				task.wait(Hitbox.DebrisTime)
				HB:Disconnect()
				table.clear(CDs, Models)
			end)
			task.wait(Hitbox.DebrisTime + Hitbox.DebounceTime)
			CS:RemoveTag(hrp, "CD")
		elseif NewSettings.Types.Hitbox.Burst.ON == true then
			local Cooldown = CS:AddTag(hrp, "CD")
			local Amount = 0
			local HitParts = {}
			local Models = {}
			
			local HB = game:GetService("RunService").Heartbeat:Connect(function(DT)
				for _, hitpart in HitParts do
					local Hit = WS:GetPartBoundsInBox(hitpart[1].CFrame, hitpart[1].Size, OP)

					if #Hit > 0 then
						for _, HPart in Hit do
							if Models[HPart.Parent] then continue end
							if HPart.Parent:FindFirstChildWhichIsA('Humanoid') and HPart.Parent:IsA('Model') then
								Models[HPart.Parent] = HPart.Parent
							end
						end
						for _, Model in Models do
							task.spawn(function()
								hitpart[2]:SetCD(CDs, Model, Models, NewSettings.Types.Detection)
								if NewSettings.Types.Detection.DetectMulti then
									Models[Model] = nil
								elseif NewSettings.Types.Detection.DetectOnce then return end
							end)
						end
					end
				end
			end)
			
			repeat task.wait(NewSettings.Types.Hitbox.Burst.TimePerBurst)
				local HitPart = Instance.new("Part")
				HitPart.Massless = true
				HitPart.CanCollide = false
				HitPart.CFrame = hrp.CFrame
				HitPart.Parent = hrp

				local Hitbox = HitboxModule.new(NewSettings, HitPart)
				Hitbox:DevMode()
				Hitbox:SetProps()
				Hitbox:SetWeld(hrp, HitPart)
				Amount += 1
				HitParts[HitPart] = {HitPart, Hitbox}
				Debris:AddItem(HitPart, Hitbox.DebrisTime)
			until Amount == NewSettings.Types.Hitbox.Burst.Amount
			
			for _, hitpart in HitParts do
				task.spawn(function()
					task.spawn(function()
						task.wait(hitpart[2].DebrisTime)
						if HB then
							HB:Disconnect()
						end
						table.clear(CDs, Models)
					end)
					task.wait(hitpart[2].DebrisTime + hitpart[2].DebounceTime)
					CS:RemoveTag(hrp, "CD")
				end)
			end
		end
	end
end)

I wanted to thank all of you for your support, and we hope you’re as excited about the V2 system as I am! If you run into any problems or have any feedback, hit me up, and I’ll do my best to help you out. And, of course, feel free to ask me any questions you might have. Cheers!

Edit: I left a video of how it works at the end.

External Media
24 Likes

To note, this is for combat system. You can’t use this for as an usual hitbox. Though if you’re a scripter, then you can script it for it work like that. Unless you guys want me to change that. lmk.

3 Likes

I’m seeing that people are still buying the V1 System, but to let you know it’s very exploitable. But otherwise, here’s some tips to make it less exploitable. Move the settings to the server, and make sure the cooldown is either a number or 1, so it doesn’t return nil.

1 Like

If you want to test the hitboxes in-game, then try it here! To use the hitbox, press “E” and it should activate.

1 Like

Module is updated. I fixed an settings error, “offset” it should work now, and it can only accept CFrame.

If people care to respond, do you want me to add a way to add your own function to when it detects a hit? like if you wanted to damage them and as well add something to it? Cause I can add that option in the settings.

2 Likes

I’ve made something like this for my own hitbox system, yes you should. Can be useful if people want to heal the person who dealt the damage by x% of the damage dealt, or if they want to bring the hit character towards them to do another part of their attack.

Updated the module, I fixed some bugs with some settings and added a feature that will greatly help you guys.

In your settings, functions, you can now choose if you want to use a custom function or just the usual damage.

['Functions'] = {
		Damage = 10,
		DebrisTime = 5,
		DebounceTime = .5,
		DebouncePerHit = 1,
		CustomFunction = true,
		
		Function = function(Character)
			print(Character)
		end,
	},

Just need to turn on CustomFunction in order for the function to be ran, otherwise if it’s turned off, it’ll ignore the function and damage the Humanoid, simple as that.

Let me know if you got any errors or bugs!

Hey I keep getting attempt to index nil with value when using CustomFunctions and I have no clue why, I’ve looked at the hitbox module myself and can’t find anything wrong with it.

There seems to be a major issue with this hitbox module, the hitboxes seem to change ownership to whoever last uses a move

heres a video example:

1 Like

So here’s a huge announcement, I fixed a big bug that would break the combat system easily. I just fixed it, realizing it after months, here’s the new and updated working module. This is based on @TEHGR8_N00b problem, it’s all fixed now.

Just import the same model to get the new fixed module version, enjoy. :+1:

1 Like

Don’t worry now, just fixed it.

2 Likes

Thanks! I’ll try it out when I get home

1 Like

V3 coming tomorrow, it’s more organized, more performant, and easier to manage/adjust.

1 Like

V3 has been released!

Hello , Im just wondering if this Hitbox System can work to detect Fast moving objects ?

Because I’m making a soccer game and want to detect the soccerball (that sometimes can be at high velocity)

I would probably suggest using raycast instead, but my hitbox system is not compatible with something like this. Only combat related, sorry.