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