Hitbox Detection

I’ve had issues for the past days trying to implement hitbox detection for my projectiles. I’ve considered these methods: Touched, Raycasting, Region3, GetTouchingParts.

None of these methods seem to be good enough though. Touched is not reliable, doesn’t fire sometimes (especially with fast-moving projectiles) and can be exploited. Raycasting was almost good, but not good enough for when it comes to large projectiles. Region3 is just bad for performance. GetTouchingParts worked the best for me, but people I know say it’s also bad for performance.

So, what am I supposed to use? What hitbox detection method do you use for large projectiles?

5 Likes

Your best bet is to make an invisible, Non-CanCollide hitbox surrounding the bullet, then do GetTouchingParts() every frame. In order for it to work with it being non cancollide, you will have to use this function:

function getTouchingPartsWithNoCanCollide(hitbox)
     local connection = hitbox.Touched:Connect(function() end) -- creates a TouchInterest, making GetTouchingParts() work
     local results = hitbox:GetTouchingParts()
     connection:Disconnect()
     return results
end

-- then in your loop
for _, v in pairs(getTouchingPartsWithNoCanCollide(TheHitBox)) do
     if not v:IsDescendantOf(theBulletModel) then -- so it doesn't register the bullet itself
          print(v:GetFullName())
     end
end

That is easily exploitable, and not recommended at all.

I would use Raycasting, and simply do multiple casts depending on the shape and size. Any other method is going to fail in reliability or be slow.

Definitely don’t use Touched, GetTouchingParts or anything that relies on the projectile being in a certain position at a certain frame - lag could cause the projectile to skip right past an object in a single step.

If your projectile is cylindrical, consider how many points are needed to represent it reasonably well. If it’s relatively small, a single Raycast will generally suffice. If it’s very large, 8, 12, 24 perhaps. You could create an algorithm to decide how many casts to do based on the size of the projectile, and then using some trigonometry place their origin around the circumference.

If your projectile is cuboid, Raycast at the four corners and then again based on size decide if it’s necessary to do interim casts along each straight edge.

In many cases, this will still be quicker than Region3 and with appropriate CollisionGroup or whitelist filtering, you can help to reduce the overhead further.

10 Likes

Building on top of what BanTech said:
Raycasting is (arguably) the most lightweight solution, and while it probably will turn into a hellhole with huge projectiles, you can just grab all players within a zone, and do some simple math from there.
If it’s a relatively small object like a sword, I usualy just create a “net” out of rays, going through the whole hitbox of the sword.
For even smaller objects like bullets etc, I just use a custom tween, and right before moving the bullet to it’s next position, I check if there’s an object between the bullet’s current and next position.

2 Likes

I don’t rely on roblox’s physics for bullet vs enemy or bullet vs player detection.

Have you tried doing a simple distance check?

Assuming your projectiles are spheres, you can also use a point-in-box check where you expand the size of your box on all axis’s by radius, and that’ll work too.

As mentioned in my reply - anything that relies on checking the projectile’s own space each step is flawed as the accuracy is dependant upon the frames per second of the machine checking.

You should check not only where the projectile is, but also where it’s “been” or should be going in the next step, like avozzo mentioned in his reply.

Apologies for the horrendous diagram, but this is why visually:

If the left (1) was its position in a given frame, and right (2) was its position in the next frame, simply checking the radius of the projectile itself is not good enough for a check of whether or not other parts are within its sphere / box / whatever shape.

The part in between the two frame would be completely skipped despite being right in the projectile’s path. You need to check all of the area between the two steps too.

1 Like

Yah, but tunneling even for fairly fast projectiles isn’t an issue if you are checking on the server at 60hz… pretty easy to check :slight_smile:

Hey, this is probably off-topic, but I’ve something to ask if you don’t mind;
I’ve been trying to implement a hitbox for my melee weapons, and obviously as you’ve said checking an object’s own space would be flawed.
My melee weapons use animations, and so each .Stepped I check if there was something in-between a weapons current and last position. Is there some way of implementing the method I mentioned with projectiles onto melee weapons?
I’ve wanted to try to somehow split up an animation and tween between each keypoint individually, but that’d probably be really expensive.

that is not true. If this is used on the client, of course it can be exploited, but if its used on the server, then it cannot be.

Yes it can. .Touched relies on the client to tell the server “Hey, I hit this!”, because the client has network ownership over itself.
The exploiter can easily just loop delete any TouchTransmitter the moment it’s created, as proven by many, many exploits that already abuse this flaw.

Worst part is, the server wouldn’t even be able to detect this, as deleting that TouchTransmitter wouldn’t change anything on the server, other than the fact that the local player wouldn’t ever fire that TouchTransmitter instance again.

1 Like

I would leave it as a tween, and in your Stepped handler or you could use a GetPropertyChangedSignal( ‘CFrame’ ) handler whilst the tween is ongoing, and :Disconnect() it when Tween.Completed fires.

If the shape is complex, like a sword, I would probably use Attachments wherever I’d want a ray to originate from, and each time your handler is called, draw a ray between the previous WorldPosition of each attachment and its current WorldPosition for the same attachment. That way the ray is essentially tracing (assuming a linear path) where that point on the sword has moved through.

If any objects get caught by that ray, they got slashed by the sword between the previous frame and the current frame.

I would never assume that your code is being run at any particular frequency. The server may have slowed down due to an issue either within or outside of your control, creating larger steps.

And in the case of very fast projectiles, such as a bullet, if it were real life speeds and you were checking at 60Hz, that bullet travels ~40 feet between each check. If you only check where the bullet is on each frame, you will completely miss 40 feet worth of objects. It’s super important to use rays and not just a distance check of the projectile hitting something at its exact location in the frame.

i just came across this problem myself, and you are actually right about it lol. Thanks for teaching me

1 Like