I’m currently working on a melee weapon AI. But when the AI swings its weapon, I’m not sure what kind of hitbox would be the most efficient and accurate.
What I’ve considered so far is attaching a part to the NPC’s arm and enabling the .Touched event when the NPC swings the weapon, so that it deals damage to any player who touches the part.
I’ve also considered using RaycastHitbox V4.
Which method would be the most effective and accurate, and are there any other good methods?
I personally like spartial query and magnitude
What I’ve seen a lot of games do is create several hitboxes over a timeframe and then checking whats in those hitboxes.
So, for example, a punch creates 5 hitboxes over 0.5 seconds. Each hitbox is positioned infront of the character model, and has a set size.
The first hitbox may not hit anything, but the third hitbox could hit something given the agent rotated to face a player.
Essentially, its like a touched system, but more reliable.
I’d say this method is far more accurate than .Touched, a little less performant but overall, should be a barely noticeable difference.
An example of this would be:
local hitPlayers = {}
local hitbox = script.Punch --// we'll say this is a 3,3,3 scale sphere
for i = 1, 5 do
local clone = hitbox:Clone()
clone.CFrame = rootPart.CFrame + rootPart.CFrame.LookVector * 2
clone.Parent = workspace
local partsInHitbox = workspace:GetPartsInPart(clone, overlap)
if #partsInHitbox > 0 then
for _, part in partsInHitbox do
local player = game.Players:GetPlayerFromCharacter(part.Parent)
if player and not table.find(hitPlayers, player) then
table.insert(hitPlayers, player)
end
end
end
clone:Destroy()
task.wait(0.1)
end
for _, player in hitPlayers do
hitPlayers.Character.Humanoid:TakeDamage(5)
end
^ useful
Personally, I prefer using spatial query when creating hitboxes because it’s one of the simplest and most effective methods. Some time ago, I wrote this module, and it’s been pretty useful for me—feel free to check it out:
local Hitbox = {}
local Overlap = OverlapParams.new()
Overlap.FilterType = Enum.RaycastFilterType.Exclude
function Hitbox.CastBox(CF, Size, MaxCharacters, Debug, ...)
Overlap.FilterDescendantsInstances = {...}
local CharactersOnHitbox = {}
local NearestCharacters = {}
local PartsOnHitbox = workspace:GetPartBoundsInBox(CF, Size, Overlap)
for Index, Part in PartsOnHitbox do
if CharactersOnHitbox[Part.Parent] then continue end
if not Part:IsA("BasePart") or not Part.Parent:FindFirstChild("Humanoid") or Part.Parent.Humanoid.Health <= 0 or Part.Parent:GetAttribute("IFrame") or Part.Parent:FindFirstChild("ForceField") then continue end
CharactersOnHitbox[Part.Parent] = (CF.Position - Part.Position).Magnitude
end
for key, value in CharactersOnHitbox do
NearestCharacters[#NearestCharacters+1] = {Key = key, Value = value}
end
table.sort(NearestCharacters, function(a, b)
return a.Value < b.Value
end)
local FinalCharacters = {}
for Index = 1, MaxCharacters do
if NearestCharacters[Index] then
table.insert(FinalCharacters, NearestCharacters[Index].Key)
end
end
if Debug then
local Part = Instance.new("Part", workspace)
Part.Name = "HitboxDebug"
Part.Anchored = true
Part.CanCollide = false
Part.Size = Size
Part.CFrame = CF
Part.Transparency = .9
Part.Color = Color3.fromRGB(255, 0, 0)
Part.Material = Enum.Material.Neon
game.Debris:AddItem(Part, 1)
end
if MaxCharacters == 1 then
FinalCharacters = table.unpack(FinalCharacters)
end
return FinalCharacters
end
function Hitbox.CastSphere(Position, Size, MaxCharacters, Debug, ...)
Overlap.FilterDescendantsInstances = {...}
local CharactersOnHitbox = {}
local NearestCharacters = {}
local PartsOnHitbox = workspace:GetPartBoundsInRadius(Position, Size, Overlap)
for Index, Part in PartsOnHitbox do
if CharactersOnHitbox[Part.Parent] then continue end
if not Part:IsA("BasePart") or not Part.Parent:FindFirstChild("Humanoid") or Part.Parent.Humanoid.Health <= 0 or Part.Parent:GetAttribute("IFrame") or Part.Parent:FindFirstChild("ForceField") then continue end
CharactersOnHitbox[Part.Parent] = (Position - Part.Position).Magnitude
end
for key, value in CharactersOnHitbox do
NearestCharacters[#NearestCharacters+1] = {Key = key, Value = value}
end
table.sort(NearestCharacters, function(a, b)
return a.Value < b.Value
end)
local FinalCharacters = {}
for Index = 1, MaxCharacters do
if NearestCharacters[Index] then
table.insert(FinalCharacters, NearestCharacters[Index].Key)
end
end
if Debug then
local Part = Instance.new("Part", workspace)
Part.Name = "HitboxDebug"
Part.Shape = "Ball"
Part.Anchored = true
Part.CanCollide = false
Part.Size = Vector3.new(Size * 2, Size * 2, Size * 2)
Part.Position = Position
Part.Transparency = .9
Part.Color = Color3.fromRGB(255, 0, 0)
Part.Material = Enum.Material.Neon
game.Debris:AddItem(Part, 10)
end
if MaxCharacters == 1 then
FinalCharacters = table.unpack(FinalCharacters)
end
return FinalCharacters
end
return Hitbox
Thank you all three for your excellent suggestions. I tried out each method in my game after reading your advice. Personally, I found that using spatial queries works best for my particular setup. While it’s true that creating multiple parts can give the most accurate detection when attacking and moving (like in Forsaken), in my case, the approach below seems more suitable.
I really appreciate all your input and the time you took to help me out. Thanks again.
This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.