Creating a Battlegrounds-like Hitbox

INTRODUCTION

You might have played top battlegrounds games, most notably The Strongest Battlegrounds. As a programmer, you may be wondering how they created their hitboxes, especially if you are an inspiring developer planning on creating something similar. In this tutorial, I will guide you on how to create a professional punch hitbox. Keep in mind that this will not carry you. To create a worthy punching system, you need to address the following:

  1. Sound Effects. You need to have good sound effects to really feel the kick in your punches.
  2. Animations. Very important. Without good animations, your punches will feel stale.
  3. Visual Effects. This contributes to your overall game design. Your punches must not feel bland.

Keep in mind that I will not be addressing these key details because this is a tutorial for your hitboxes.

MAIN IDEA

First of all, we need to figure out the way we will be creating the hitbox. This is the bread and butter.
If you are a beginner developer, you may suggest Touched Events. Please for the love of everyone in the Devforum, do not use Touched Events. They take up more resources than necessary, and is more complicated than the solution, which I will get into later.

If you are an intermediate developer, you might suggest Raycasting. This is a more viable option than Touched Events and you may use this, but I wouldn’t recommend, since you need several Raycasts to cover a larger area.

Spatial Queries

However, there exists Spatial Queries. There are several of them, but most notably, there are 3 main options. The following all are built-in roblox functions used on workspace.

  1. GetPartsInPart() is more accurate but takes up more resources because you need to have a part in workspace

  2. GetPartsBoundInRadius() is reliable, but is only used for spherical hitboxes

  3. GetPartsBoundInBox() is also reliable, but instead is used for cubical hitboxes, and doesn’t take up many resources.

These options may vary depending on what hitbox you are going for, but I highly recommend GetPartsBoundInBox(). For this tutorial, I will be using GetPartsBoundInBox() because of its utility in our scenario.

SCRIPTING

Now we get to the sauce of making your hitbox. Many people will recommend using a ServerScript but this is super unreliable. Using this will make everything happen Server-sided, so if you have high ping, it will feel very unfair because it looks like you hit your victim, when you didn’t.

However, using a LocalScript instead of a ServerScript is like switching out airpods for headphones. Everything now happens on your client which as a result, you actually hit the victim when you are supposed to. Beginner programmers will often look over Remote Events, which you can use to send a signal for the player who got hit to the server, which will then do the extra damage, stun, etc.

Here’s how you put it into action:

local plr = game.Players.LocalPlayer
local char = plr.Character

local HBsize = Vector3.new(4.6, 4, 5.75) -- change to your hitbox size
local overlapParams = OverlapParams.new()
overlapParams.FilterType = Enum.RaycastFilterType.Exclude
overlapParams.FilterDescendantsInstances = {char}

local parts = workspace:GetPartBoundsInBox(hrp.CFrame * CFrame.new(0, 0.25, -HBsize.Z/2), HBsize, overlapParams)
local plrsHit = {} -- table for all the players who were caught in the hitbox

for _, v in pairs(parts) do
	if v.Parent and v.Parent:FindFirstChild("Humanoid") and v.Parent:FindFirstChild("HumanoidRootPart") then

		local EChar = v.Parent

		if not table.find(plrsHit, EChar) then table.insert(plrsHit, EChar) end

	end
end

After, you then just send a signal through a RemoteEvent.
Now, here’s a step-by-step explanation on what the code does:

  1. First, we create our basic variables for the player who will create the hitbox.
  2. Then, we configure the settings our hitbox by listing the size of our hitbox, and by create parameters. FilterType = Enum.RaycastFilterType.Exclude will make everything in the FilterDescendantsInstances table be excluded for the hitbox, and FilterDescendantsInstances = {char} will make our character be excluded.
  3. Afterwards, we create another variable. It is what makes the hitbox. The first parameter is the CFrame of where our hitbox will be created, the second parameter is the size of our hitbox, then the third parameter is our Overlap Parameters, where we excluded us from being in the hitbox.

However, the variable will actually only return us a table full of parts which were caught in the hitbox. Instead, we would like to know the players caught in it. To do that, we create an empty table, then we execute a for loop for the parts variable, where we creating the hitbox. Remember, that variable is only full of parts that was in the hitbox.

Using the for loop, we check if the part has a parent, and if the parent has a Humanoid and a HumanoidRootPart. By doing that, we know if we detected a player, and from there we create a variable for the enemy character and insert it into the empty table that we had.

So, by doing that, we now have a table with the players caught in the hitbox!

EXTRA INFO

  • To get the amount of players caught, you would just to #plrsHit which will return a number of how many objects (or how many players) were in the table.
  • To actually damage the players or configure the players, you would create a for loop for the plrsHit table, where v is the character of the player. Then you would do something like
v.Humanoid:TakeDamage(number)

If you have any questions, do not hesitate to ask. Have a great day!

21 Likes

I actually am making a JoJo combat game right now and I have faced this exact problem…? How should i calculate my hitboxes, I like this Spatial Queries approach, It is indeed faster and better. But what do you think about MuchachosHitbox, or HitboxClass, or ZonePlus for hitboxes…?

For Example MuchachosHitbox has Velocity Predictions, that predict where the hitbox should be so it is way more accurate, sure you can do that yourself too but I feel like having the module do it for you is pretty neat.

Only downside as you mentioned, is that it’s using the Hitbox.Touched event and TouchEnded event.

You could make a hitbox module that uses Spatial Queries, and has velocity prediction. That feature is really good…

The Cyan Hitbox is using velocity prediction.
The Red Hitbox doesn’t use velocity prediction.

So which module should you use?

MuchachosHitbox because of its Velocity Predictions?

HitboxClass Because its fast and its OOP?

Or ZonePlus Because it uses Spatial Queries?

1 Like

In Strongest battleground mentioned above, the Hitbox system is done entirely through the server and does not use RemoteEvents to transfer information from client to server about who hit who.

I have a module that exactly replicates the Hitbox system in Strongest battleground, using 2 modules.

HitboxCaster - which via Bindable event passes data to the script when someone was hit and tracks :GetTouchingParts().

HitboxModule - which creates a hitbox and binds it to a player via Weld.

I think your idea to transmit hits through the client is very bad, as there are many nuances besides accuracy.
Exploiters can take advantage of this, and even if there is a defense there is always a workaround.
If your opponent is pinging too, for you he can hit the air and hit you, even though on your screen you will be far away from him.
Also RemoteEvents has a delay in transferring information from client to server, so the hits will be out of sync, and you need to replicate through the client if you do it your way

So I think that server Hitbox Modules are the most reliable and the best.
Also lags are prevented by manually clearing various RBXScriptConnection and other data that accumulates in the server memory.

Modules like Trove, Maid and others.

Also in Strongest Battleground is used module (which I will attach). This module makes synchronization between players to have a smooth gameplay.

local Thread = {}

local RunService = game:GetService("RunService")

local threads = {}

RunService.Stepped:Connect(function ()
	local now = tick()
	local resumePool

	for thread, resumeTime in pairs(threads) do
		-- Resume if we're reasonably close enough.
		local diff = (resumeTime - now)

		if diff < 0.005 then
			if not resumePool then
				resumePool = {}
			end

			table.insert(resumePool, thread)
		end
	end

	if resumePool then
		for _,thread in pairs(resumePool) do
			threads[thread] = nil
			coroutine.resume(thread, now)
		end
	end
end)

function Thread:Wait(t)
	if t ~= nil then
		local t = tonumber(t) or 1 / 30
		local start = tick()

		local thread = coroutine.running()
		threads[thread] = start + t

		-- Wait for the thread to resume.
		local now = coroutine.yield()
		return now - start, os.clock()
	else
		RunService.Heartbeat:Wait()
	end
end

function Thread:Spawn(callback)
	task.spawn(callback)
end

function Thread:Delay(t, callback)
	task.delay(t, callback)
end

return Thread
1 Like

great question! MuchachosHitbox, HitboxClass, and ZonePlus are alternative options. it really depends on the scenario. MuchachosHitbox and HitboxClass are the same from what I’ve seen, so I’ll just refer to both as HitboxClass. also, I haven’t checked the scripts of the modules, so I may be wrong on some parts.

HitboxClass

firstly we have HitboxClass, which uses velocity predictions in order to execute the hitbox. as mentioned, it does use Touched Events which are a no-no when it comes to hitboxes. However, in some cases you could have a valid reason of using it.

For example, if you are making an ability which involves a tackle that moves you forward, in some cases you can actually use velocity prediction because it crosses a wide area.

ZonePlus

the next one and last is ZonePlus. this involves spatial queries, but uses it in a different way. by only looking at the videos, I see that it constantly checks if there are players inside the zone.

while it does use spatial queries, unless you want to make some sort of tackle in which you have to constantly create a new hitbox. ZonePlus seems to only be useful if you have an ability which has a zone. taking your JoJo game into consideration, you could use ZonePlus for something like a timestop.

CONCLUSION/TL;DR

again, I haven’t read any of the modules, but overall, ZonePlus is a good option when it comes to making zones. not especially useful when it comes to stuff like punches, where creating one singular hitbox is enough.

HitboxClass is another alternative, however it’s a bit complicated. it uses velocity predictions for parts and uses touched events, but I don’t understand why you would really need to use velocity predictions because you can just weld the part. maybe if you’re making an ability that depends on your velocity?

however, if you don’t want to use any of these, you could instead make your own module to fit your needs, or search for other modules. I like the idea of creating a module for something you’ll use very often.

also, I’m not very active in the devforum, so I apologize for the late response.

1 Like

you have a good point, however if you’re worried about exploiters taking advantage of how you put all the properties client-sided, then you could simply develop an anti-cheat. according to you:

however, to every workaround, there’s a fix. I have thought of most ways exploiters can take advantage of the creation of hitboxes, so here are the ways you can prevent cheating:

  1. if you are worried about exploiters exploiting the cooldown between the creation of each hitbox, then you can either check to see if hitboxes are being created too fast, or more viably, you could just execute the cooldown both in the server and the client.
  2. next, if you are worried that they will create a gigantic hitbox, you could check to see if the distance between the 2 players are within reach. if not, you could simply kick the executor of the hitbox out of the game.

you do have another point, where you state that lags can be prevented. while this is mostly true, it doesn’t prevent all lag. it’s much more viable to execute it on the client to avoid the hassle of reducing the player lag, because somebody (me) will host their internet on a potato, in which all the attempts to reduce the lag won’t have any effect.

you do have a point that extremely laggy players will be far away when the punch executes and will be a problem, however it isn’t a problem.

like, taking the strongest battlegrounds as an example, you would want to check if the player is stunned on the server which is also delayed, so unless you’re fighting somebody who is AFK, chances are you are going to be stunned when the punch actually registers, which in turn, you won’t damage the player.

NOTE:

as I’ve said previously: I like the idea of using a module. that’s totally fine, especially when you’re going to be creating hitboxes left and right. popular modules like the ones you listed are a valid option, but I want you to take into account of it being (usually) easier to create the hitbox client-sided to avoid all the unnecessary ping problems.

I am a player of 100+ ping, sometimes spiking to 200 ping. I am extremely confident that there will always be some delay even when you remove all the ways the player could be lagging.

You are creating a tutorial on implementing client-sided hitboxes, where information about who the player hit is transmitted via RemoteEvent. However, using client-sided hitboxes in Roblox has numerous drawbacks, which is why they are often avoided in favor of server-sided hitboxes. Here are the main reasons why this approach is problematic:

  1. Vulnerability to Cheating
    Client-sided hitboxes are processed on the player’s side, making them susceptible to manipulation. A cheater can:
  • Alter the size, position, or behavior of the hitbox to deal damage from a greater distance or avoid being hit.
  • Remove or rewrite the client script, bypassing your checks.
    Since the server does not control these actions, detecting and preventing exploits is nearly impossible. Server-sided hitboxes, on the other hand, perform all calculations on the server, significantly reducing the risk of cheating.
  1. Issues with Ping and Latency
    Client-sided hitboxes depend on the player’s internet connection. With high ping:
  • Data transmission via RemoteEvent is slower compared to low ping, causing delays in hit registration.
  • This leads to desynchronization between the client and server, resulting in “laggy” hits or missed strikes.
    In fast-paced games where precision and reaction speed are critical, such delays are particularly noticeable and degrade the gaming experience.
  1. Inconsistent Gameplay Experience
    Each client processes hitboxes independently, leading to different outcomes for the same action among players with varying ping or device performance. For example:
  • One player may think they hit an opponent, while another believes they dodged.
  • Opponents’ movements on a player’s screen may not match their actual position on the server, especially with high ping.
    Server-sided hitboxes provide a single source of truth, minimizing discrepancies and ensuring a consistent experience for all.
  1. Synchronization Challenges
    In multiplayer games, client-sided hitboxes require constant data synchronization between all clients through the server. This:
  • Increases network load.
  • Can cause lag, especially in games with many players.
    Server-sided hitboxes eliminate the need for such complex synchronization, as all calculations are centralized.
  1. Reduced Fairness
    Client-sided hitboxes create unfair situations where players with better internet connections or more powerful devices gain an advantage. For example:
  • Players with low ping can transmit hit data faster.
  • High-performance devices handle local calculations more efficiently.
    Server-sided hitboxes level the playing field, as all calculations occur on the server, independent of client-side factors.
  1. Why Don’t Major Games Use Client-Sided Hitboxes?
    If client-sided hitboxes were effective, they would be used in popular games like The Strongest Battlegrounds. However, the developers of this game rely on server-sided hitboxes to ensure security, fairness, and stability. This proves that the client-sided approach does not hold up in real-world conditions.

  2. False Sense of Brilliance
    I used to think that transmitting hits via the client was a revolutionary idea. However, in practice, it proved impractical due to issues with synchronization, security, and performance. Therefore, I strongly advise against using client-sided hitboxes in your tutorial.

Recommendation
For robust hitboxes, use server-sided calculations with Roblox tools like Raycasting or GetPartsInPart. This will ensure:

  • Protection against cheating.
  • Smooth and fair gameplay for all players.
  • Minimal desynchronization, regardless of ping or device.
    To improve your tutorial, switch to server-sided hitboxes and include server-side checks for all actions. You can study modules like HitboxClass or Roblox’s official documentation for examples.

You won’t be able to create an effective anti-cheat system because it would need to run on the client side to detect how players manipulate hitboxes. However, client-side scripts can be easily removed or altered, especially if they’re embedded in critical game scripts.

You need to recognize that your game is multiplayer, not single-player. Based on your approach, it seems you lack the experience to understand why this idea is fundamentally flawed.

Here is a script that tracks hits on the server exactly like Strongest Battlegrounds. You can try it out and you’ll see that it is.
As I said it passes hit information through BindableEvent. And it doesn’t have latency anymore because it passes the information - Server > Server.
And it no longer has latency as in your example with RemoteEvent which passes information from Client > Server.

local module = {}
module.__index = module;

local hitboxConnections = {}
local debug = true
local trove = require(game.ReplicatedStorage.Resources.Modules.Trove)

function module.newCaster(hitboxPart : BasePart, casterSettings, caster : Model) : ()
	local metatable = setmetatable({}, module)
	metatable.newHitbox = hitboxPart
	metatable.castSettings = casterSettings
	metatable.caster = caster
	return metatable
end

function module:stop()
	self.maid:Clean()

	for i, v in hitboxConnections do
		if typeof(v) == "RBXScriptConnection" then
			v:Disconnect()
		elseif v:IsA("Instance") then
			v:Destroy()
			v = nil
		elseif typeof(v) == "function" then
			v()
			v = nil
		end
	end
end

function module:castHitbox(castEvent)
	local newCastEvent = castEvent
	self.castSettings = self.castSettings or {
		canHitSelf = false,
		doubleHit = false,
	}

	self.hitTable = {}

	local maid = trove.new()
	self.maid = maid

	if self.newHitbox and self.newHitbox:IsA("BasePart") then
		local castTime = time()
		local minCastTime = 0.015

		local function hitTarget(v)
			if v.Parent then
				if v.Parent:FindFirstChildOfClass("Humanoid") then
					if not v.Parent:HasTag("Ignore") and not v.Parent:FindFirstChild("DefaultIFrame") and not v.Parent:FindFirstChild("IFrame") and not v.Parent:FindFirstChild("DashCancel") and not v.Parent:FindFirstChild("AbsoluteImmortal") then
						if not table.find(self.hitTable, v.Parent) then
							table.insert(self.hitTable, v.Parent)
							if v.Parent.Name ~= self.caster.Name then
								newCastEvent:Fire("Hit", {
									hit = v.Parent
								})
							end
						end
					end
				end
			end
		end

		local function createCast()
			if not self.newHitbox.Parent then
				return self:stop()
			end

			if self.caster:FindFirstChild("Stunned") or self.caster:GetAttribute("Ragdolled") then
				return self:stop()
			end

			for i, v in self.newHitbox:GetTouchingParts() do
				if v:IsA("BasePart") then
					local target = v.Parent

					if not target then
						continue
					end

					local targetHumanoid = target:FindFirstChildOfClass("Humanoid")

					if not targetHumanoid then
						continue
					end

					hitTarget(v)
				end
			end
		end

		maid:Connect(self.newHitbox.Touched, function(v)
			if not self.newHitbox.Parent then
				return self:stop()
			end

			if self.caster:FindFirstChild("Stunned") or self.caster:GetAttribute("Ragdolled") then
				return self:stop()
			end

			hitTarget(v)
		end)

		maid:Connect(self.newHitbox.AncestryChanged, function()
			if self.newHitbox:IsDescendantOf(game) then
				return
			end

			maid:Clean()
		end)

		task.spawn(function()
			while task.wait() do
				if not self.newHitbox:IsDescendantOf(game) then
					break
				end

				createCast()
			end
		end)

		createCast()
	end
end

function module:start()
	local newCastEvent = Instance.new("BindableEvent")
	newCastEvent.Name = "HitboxBindable"
	table.insert(hitboxConnections, newCastEvent)

	task.delay(0.0135, function()
		self:castHitbox(newCastEvent)
	end)

	return newCastEvent
end

return module

to be frank, you are wrong. client-side hitboxes do not depend on the player’s ping.

LATENCY

it’s the server that does. when you have high ping (say 1000 ping), the server would think that you’re at a spot when instead that’s where you were 1 second ago. client-side views literally the exact same POV as you.

I hate to burst your bubble, but I have tried casting the hitbox server-sided and while it worked well in studio, it worked horribly while play-testing with friends because of how bad the latency is server-side. when I tried casting the hitbox client-side, all of my problems suddenly vanished. if you don’t believe me and you still think the latency is bad client-side, what makes you think server-side would be any better?

by all means, I don’t want to be rude, but I hate to see that you haven’t tried play-testing this with other people. the ping you get while in studio is much better than what you would get in-game, so I would love to talk to you when you actually test casting hitboxes client-side. I have several instances of where I get much support with client-side hitboxes, and people complaining about the latency through server-side hitboxes.

DISREGARDING

also, it appears you disregarded my recommendations. you state the following:

while this is true, you don’t see much complaining about this from the larger games. this is not because they use server-side hitboxes, but rather it’s because the majority of players play on low ping. if somebody joins with 12000 ping, it’s their blame for joining a ping-reliant game. most players of this category will ANYWAYS know that it’s their fault for joining this type of game.

also, either way client-sided hitboxes literally make it more forgiving for players with relatively high ping because of the fact that CLIENT-SIDE is NOT dependent on ping.

Chat GPT ahh replies

You try to make it seem like your replies are absolute because you mention popular battlegrounds titles and say they use what you write, you also include trove which is completely irrelevant to the topic and only adds complexity to the code you share that’s meant to be an example.

The only way server-sided hitboxes could “maybe” work is by having a complex lag compensation system which seems overkill to me since you can simply fetch the information from the client and send it to the server.

Sanity checks can be done on the server and if done properly can eliminate most issues exploiters can take advantage of.

The security issues caused by hitbox detection done on the client side are:

  • Spamming the remote event. This can be dealt with by handling cooldowns on both the client and the server, which prevents damage from happening more often than it needs to.
  • Manipulating hitbox size, manipulating damage, etc… This can be dealt with by only sending the information about which target can affected and nothing else. The server must be able to deduce what the stats it must use from the little information the client is sending.
  • Trying to attack someone from far away. This can be dealt with by adding distance checks between the player attacking and the targets. This is not a 100% foolproof way to deal with it since the distance between players and targets is not the same than what clients see. A distance check too low prevents correct cases from correctly happening and a distance check too high can be exploited.

I’m open for debate, but arguing with someone who uses an AI to try to spread some kind of “absolute truth” is a waste of time.

3 Likes

Both HitboxClass and MuchachoHitbox do not use touched events, they use spatial queries. They have signals you can connect to that act like .Touched just for simplicity sake, but they’re much more accurate and cleaner under the hood because they use spatial queries.

The reason why velocity prediction is a thing is to actually change the offset of the hitbox depending on the player’s movement, which helps combat the feeling of latency. Welding does not do that by default unless you manually change the hitbox’s C0 and C1 weld. If you use a weld, you would always need to have a part for the hitbox, which increases network usage if you’re having it replicated to everyone as well including the constant updates to the weld. You shouldn’t ever need to use a weld if you’re not using PartsInPart.

2 Likes

My English is bad and I told the neural network to correct my text and make it readable that I sent her.
Everything I wrote is my thoughts and nothing was made up for me.

I had a battleground in which I used client hitboxes, but first of all everything happens too fast and it’s almost unrealistic to react. Because of the unsynchronization.

It works well but it’s just not practical, if you make a good game with client hitboxes I will definitely try it out.
Also it’s still not clear to me how you can fully control the manipulation of hitboxes in the client, if you can change the code and remove your checks.

You will only be able to do checks on the server, and will not be able to fully control everything. I don’t know how you will be able to track hitbox size if your anti-cheat on the client removes or changes or removes the check for hitbox size.
Even if you send the hitbox size information to the server, you can still do a spoof.

All the main problems of this topic as I think, I described to you in the post above. I don’t think it’s a good idea, but if you think otherwise I understand. But you should try your method when your game becomes popular, and there will be many different players who will definitely find a way to manipulate them.

Also I said that passing a hit via RemoteEvent is possible, but not practical. It’s better to use other ways to make the hit instantaneous. Because it has a delay and already at a ping of 200 will be perceptible

ok time to address all of these even though this was 20 days ago

vulnerability to cheating

yes, client sided hitboxes ARE vulnerable to cheating. that’s because it’s the server’s job to verify if hits are valid based on the info given. for example, is the target super far away from the attacker? if so, don’t let the hit go through. simple

ping issues and latency

depending on what kind of game you make, desynchronization is negligible (most notable in fps games, though i’d say that it could work in battleground games if handled well.) besides, ping will be a problem regardless of you using server-sided hitboxes or not.

inconsistent gameplay experience

like the above, will still happen even if you choose to implement server-sided hitboxes because this is related to ping

synchronization challenges

clients still need to send information to the server when they are going to attack and send information to other clients when they are attacked regardless. you’re not going to get hugely increased network load, and said synchronization challenges still exist with server-sided hitboxes.

reduced fairness

can be solved by making your own lag compensation systems, like by sending timestamps when clients do hits (this CAN be exploited by hackers, but the impact can be mitigated by limiting how far clients can “rollback”)
plus, since clients still need to send information about when they are going to attack to the server, this will exist with server-sided hitboxes too.

why major games don’t use client-sided hitboxes

ok two problems
first: where’s your source
second: just because something isn’t used doesn’t mean it’s not viable. server-sided/custom character controllers aren’t used a lot, but they’re very useful since you can fine-tune them to your liking.

tl;dr - most of these are also problems with server-sided hitboxes, except for the cheating problem, and that can be easily mitigated.