I am looking for help with a strange bug that is occuring within my hitbox system.
The Premise
I’ve been working on a side project over the last few days, set in a cave system where gunpowder is used in everyday life. I recently made another weapon, a large, slow battleaxe with a cannon on the other end. Due to the nature of the weapon, I made the hitboxes quite large.
The Problem
Large hitboxes resulted in an odd problem where the weld I use to keep the hitbox attached to the player seemed to freeze both the player and the hitbox in place. This only happens with large hitboxes, although I’m not sure of the exact dimensions required to trigger the bug.
Here is how hitboxes should act, and how they do act when they are small enough:
And here is how hitboxes act when they are large, notice the player getting stuck for a split second:
I’ve skimmed over the forums to see if anyone was having this same issue, but didn’t find a solution. I’ve also tried messing around with the dimensions of the hitboxes, and the hitbox function itself, to no avail. The hitbox is unanchored and CanCollide is set to Off.
Current Code
Here’s my current function for a hitbox. A lot of the arguments are passed through to the damage function but the main ones are rig which defines the character and the three that dictate size, width, height, length.
function module.Hitbox(rig, damage, duration, stunduration, params, width, height, length, blockable, posturedamage, attacker)
local offset = Vector3.new(0, 0, -((length / 2) + 0.5))
local hit = {}
local hitbox = hitboxtemplate:Clone()
hitbox.Name = rig.Name .. "Hitbox"
hitbox.Size = Vector3.new(width, height, length)
hitbox.CFrame = rig.HumanoidRootPart.CFrame * CFrame.new(offset)
local weld = Instance.new("WeldConstraint")
weld.Part0 = rig.HumanoidRootPart
weld.Part1 = hitbox
weld.Parent = hitbox
weld.Enabled = true
hitbox.Parent = workspace
local connection = game:GetService("RunService").Heartbeat:Connect(function()
for i, v in pairs(workspace:GetPartsInPart(hitbox, params)) do
if v.Parent:FindFirstChildWhichIsA("Humanoid") and not hit[v.Parent] then
hit[v.Parent] = true
damagemodule.BluntDamage(v.Parent, damage, blockable, posturedamage, attacker)
damagemodule.Stun(v.Parent, stunduration)
end
end
end)
task.spawn(function()
task.wait(duration)
connection:Disconnect()
hitbox:Destroy()
end)
end
How about you just don’t weld the hitbox to the player, which in itself sounds unconventional. Not to mention, it’s creating a new instance for each attack, which sounds inefficient.
You already have the .Heartbeat connection set up for the hitreg, so what you can do is position the hitbox in front of the player via the LookAt vector before it does the spatial querying.
I create a new instance for each attack so that I can have multiple people attacking at the same time, and I use a weld because it is the smoothest way to keep the hitbox in position. The .Heartbeat connection appears jumpy and a lot less smooth than welding, even with the hitbox anchored to stop constant gravity.
Unless each player needs to be able to attack multiple times simultaneously, you only need one hitbox for each player, for each unique attack.
I really don’t see why there would be an issue with just CFraming the hitbox. The hitbox itself should be anchored, massless, not collideable, not touchable, not queryable (IIRC CanQuery only matters for parts that are trying to be detected), and be positioned relative to the HumanoidRootPart. You can perhaps show us a video recording demonstrating the problem of “jumpy” hitboxes.
It might be worth building a simplified reproduction of the problem, see if the problem occurs when the part is welded to the player for a longer period of time. It might be a good bug to report if “Massless” is not working as expected.
The same hitbox function as before, except I commented out the weld section and changed the .Heartbeat connection to include 1 more line at the top (Don’t mind that the damage function name changed, I made some small changes to the system)
local connection = game:GetService("RunService").Heartbeat:Connect(function()
hitbox.CFrame = rig.HumanoidRootPart.CFrame * CFrame.new(offset)
for i, v in pairs(workspace:GetPartsInPart(hitbox, params)) do
if v.Parent:FindFirstChildWhichIsA("Humanoid") and not hit[v.Parent] then
hit[v.Parent] = true
damagemodule.Damage(v.Parent, damage, damagetype, blockable, posturedamage, attacker)
damagemodule.Stun(v.Parent, stunduration)
end
end
end)
Here’s the thing: using welds appears to work better, but that’s because you’re giving the player themselves NetworkOwnership over the hitbox by welding it to their character, which in turn opens up a new door of vulnerability where the player can theoretically freely manipulate the hitbox to exploit/cheat.
There really isn’t a perfect solution to this. The only way to fix the latency is by making the client in charge of the hitreg, which is just as vulnerable as welding the hitbox to their character. But you could then implement a relatively simple anticheat where there’s a certain threshold to how far apart the player can be from its target (due to latency) and reject attacks that are unrealistically far, as well as a threshold to how fast the hitbox can move (to prevent cheats like killaura). It can still help prevent blatant cheating.
I used weldconstraint to weld a part to the player’s character bodypart from the server, and then changed the part’s position from the client, but nothing changed on the server?