Trying to optimize my combat system

my hitbox class


local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Signal = require(script.Parent.Signal)

local Hitbox = {}
Hitbox.__index = Hitbox

function Hitbox.new(Size: Vector3, CF: CFrame, Lifetime: number, Visualizer: boolean, Params: OverlapParams, OnHit: (Model) -> ())
	local self = setmetatable({}, Hitbox)
	self.Size = Size
	self.CFrame = CF
	self.Lifetime = Lifetime
	self.Visualizer = Visualizer
	
	self.Active = false
	
	self.Stopped = Signal.new()
	self.Hit = Signal.new()
	
	self.Params = Params
	
	self.HitConn = self.Hit:Connect(OnHit)
	return self
end

function Hitbox:_start()
	local result = workspace:GetPartBoundsInBox(self.CFrame, self.Size, self.Params)
	for _, instance in ipairs(result) do
		if instance then
			local Character = instance:FindFirstAncestorOfClass("Model")
			if Character then
				self.Hit:Fire(Character)
			end
		end
	end
	
	if self.Lifetime and self.Lifetime > 0 then
		task.delay(self.Lifetime, function()
			if self.Active then
				self:deactivate()
			end
		end)
	end
end

function Hitbox:activate()
	if self.Active then return end
	self.Active = true
	self:_start()
end

function Hitbox:deactivate()
	if self.Active == false then return end
	self.Active = false
		
	self:_stop()
end

function Hitbox:_stop()
	self.Stopped:Fire()
end

return Hitbox

server validation

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Debris = game:GetService("Debris")

local Tagger = require(ReplicatedStorage.Modules.Tagger)
local Hitbox = require(ReplicatedStorage.Modules.Utilities.Hitbox)

local Conn

ReplicatedStorage.Events.ValidateHitbox.OnServerEvent:Connect(function(Player, CF, timestamp)
	local Character = Player.Character
	if not Character or not Character.PrimaryPart then return end

	local CurrentTime = tick()
	if CurrentTime - timestamp > 0.5 then
		return
	end

	if (Character.PrimaryPart.Position - CF.Position).Magnitude > 10 then
		return
	end

	if not Player then
		warn("what the cheese is lil bro tryna do")
		return
	end

	local Params = OverlapParams.new()
	Params.FilterType = Enum.RaycastFilterType.Exclude
	Params.FilterDescendantsInstances = {Character}
	print("Validation completed")

	ReplicatedStorage.Events.HandleClientHitbox:FireAllClients(CF, Player)
end)

client input manager

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local Debris = game:GetService("Debris")
local RunService = game:GetService("RunService")

local Tagger = require(ReplicatedStorage.Modules.Tagger)
local FC = require(ReplicatedStorage.Modules.Utilities.FunctionUtils)
local Hitbox = require(ReplicatedStorage.Modules.Utilities.Hitbox)

local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()

repeat task.wait() until Character:FindFirstChildOfClass("Tool")

local Tool = Character:FindFirstChildOfClass("Tool")

local SwingAnim = Character.Humanoid.Animator:LoadAnimation(ReplicatedStorage.Assets.Swing)

UserInputService.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		if Tagger:HasTag("Attacking", Character) then return end
		local Timestamp = tick()
		local HitboxCF = Character.PrimaryPart.CFrame * CFrame.new(0, 0, -3)
		
		Tagger:TagObject("Attacking", Character)
		
		ReplicatedStorage.Events.ValidateHitbox:FireServer(HitboxCF, Timestamp)
		
		task.delay(0.5, function()
			Tagger:RemoveTag("Attacking", Character)
		end)
	end
end)

ReplicatedStorage.Events.HandleClientHitbox.OnClientEvent:Connect(function(CF, attackingPlayer)
	print("Passed handleclienthitbox")

	if attackingPlayer == Player then
		SwingAnim:Play()
	end

	local Params = OverlapParams.new()
	Params.FilterType = Enum.RaycastFilterType.Exclude

	local attackerChar = attackingPlayer.Character
	if attackerChar then
		Params.FilterDescendantsInstances = {attackerChar}
	end
	
	local VisualHB = FC.HitboxVisualizer(Vector3.new(5,6,5), CF, 0.06, Player)
	Debris:AddItem(VisualHB, 0.06)

	local HB = Hitbox.new(Vector3.new(5,6,5), CF, .06, true, Params, function(Instance)
		if Instance and Instance:FindFirstChild("Humanoid") then
			if Instance ~= attackerChar then
				Instance.Humanoid:TakeDamage(5)
			end
		end
	end)

	HB:activate()
end)

i think the modules like FunctionUtils and Tagger are self explanatory, the HitboxVisualizer function just creates a visual hitbox and the Tagger module I have no idea why I made it but it just tags stuff so i can keep track of a players state

i heard about clientsided prediction and velocity prediction and whatever there is but its a bit complicated for me but give literally any feedback even if its on the prediction topics its all appreciated since im trying to make the most accurate hitbox i can

2 Likes

i don’t think you need to make your own tagger class when attributes already exist, they’re very easy to implement and I use them for my combat system

id just use collectionservice tags unless im trying to assign actual values to players which isnt the case, i used to use attributes but i heard somewhere that theyre not the most efficient and they should only be used when you really desperately have a specific use case for them

#help-and-feedback:code-review

where did you hear that from?
i don’t see anything wrong with attributes

2 Likes

cant rmemeber from where but does it really matter? i just need help to make this code i sent better

I think attributes will help you more since you don’t have to manually remove them when the player leaves