Issues with hitbox detection

I’m currently making a Hollow Knight inspired roguelike and am in the process of creating the core movement/attacking mechanics.

In both Hollow Knight and Hollow Knight: Silksong, landing a downslash, or pogo, launches the player upwards. While trying to fix several bugs with my spaghetti code, I found that the downslash hitbox is acting very strange.

The weirdest thing is that this seems random. Sometimes it works completely fine, and other times it not only wont register downslashes that should hit, but also regular and up slashes, but only on certian dummies???

Heres my code along with a short video of the issue. I’m using workspace:GetPartsInPart(hitbox, overlapParams) to detect hits.

local PS = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")
local RuS = game:GetService("RunService")

local eventsFolder = RS:WaitForChild("Events")
local remoteEventsFolder = eventsFolder:WaitForChild("Remote_Events")
local slashEvent = remoteEventsFolder:WaitForChild("Slash")

local slashAnimID = "rbxassetid://123066251585839"
local downslashAnimID = "rbxassetid://123066251585839" -- same as default slash
local upslashAnimID = "rbxassetid://123066251585839"-- same as default slash

local function newHitbox(character, size, offset, damage, linger, pogo)
	local db = false
	local hrp = character:FindFirstChild("HumanoidRootPart")
	if hrp == nil then return end
	local weld = Instance.new("WeldConstraint", hrp)
	local hitbox = Instance.new("Part")
	weld.Part0 = hrp
	weld.Part1 = hitbox

	hitbox.Transparency = 0.5
	hitbox.Material = "ForceField"
	hitbox.Color = Color3.new(1, 0, 0)
	hitbox.CanCollide = false
	hitbox.CanQuery = false
	hitbox.Massless = true

	hitbox.Size = size
	hitbox.CFrame = hrp.CFrame + hrp.CFrame.LookVector * offset.X + Vector3.new(0, offset.Y)
	hitbox.Parent = character
	
	local connection
	connection = RuS.Heartbeat:Connect(function(dt)
		if not hitbox or db == true then
			connection:Disconnect()
			return
		end
		
		local overlapParams = OverlapParams.new()
		local touchingparts = workspace:GetPartsInPart(hitbox, overlapParams)
		for _, part in ipairs(touchingparts) do
			if part.Parent:FindFirstChild("Humanoid") == nil then return end
			print(part)
			if part.Parent.Name ~= character.Name and not db then
				db = true
				
				part.Parent.Humanoid:TakeDamage(damage)

				if pogo then
					print("bounce")

					local hrp = character:WaitForChild("HumanoidRootPart")
					hrp.Velocity = Vector3.new(hrp.Velocity.X, 75, hrp.Velocity.Z)
				end
			end
		end
	end)
	
	task.delay(linger, function()
		if hitbox then
			hitbox:Destroy()
			connection:Disconnect()
		end
	end)
end

slashEvent.OnServerEvent:Connect(function(player, Type)
	Type = Type or "slash"
	
	local humanoid = player.Character:WaitForChild("Humanoid")
	local animator = humanoid:WaitForChild("Animator")
	local slashAnim = Instance.new("Animation")
	
	if Type == "upslash" then
		print("upslash server")
		
		slashAnim.AnimationId = upslashAnimID
		newHitbox(player.Character, Vector3.new(5, 5, 5), Vector3.new(0, 4, 0), 20, 0.25, false)
	elseif Type == "downslash" then
		print("downslash server")
		
		slashAnim.AnimationId = downslashAnimID
		newHitbox(player.Character, Vector3.new(5, 5, 5), Vector3.new(0, -3, 0), 20, 0.25, true)
	else
		print("slash server")
		
		slashAnim.AnimationId = slashAnimID
		newHitbox(player.Character, Vector3.new(5, 5, 5), Vector3.new(3, 0, 0), 20, 0.25, false)
	end
	
	local slashAnimTrack = animator:LoadAnimation(slashAnim)
	slashAnimTrack:Play()
end)

Im going to bump this because without this fixed I don’t know how to continue working on the game

Your problem is that you’re using workspace:GetPartsInPart() which is extremely unoptimized, so it’s likely that the dummy moves away from the hitbox and so it can’t be detected. Instead you could use FindPartsInRange(), which will give you a list of all parts that are within a certain distance of something. This will drastically improve performance.

1 Like
for _, part in ipairs(touchingparts) do
			if part.Parent:FindFirstChild("Humanoid") == nil then return end

The player is bound to hit something that isn’t going to be directly part.Parent:FindFirstChild(“Humanoid”) and that will kick the flow out of the function, resulting in a seemingly random issue.

Maybe something like this would be a more solid approach for what you’re doing here.

 if part.Parent:FindFirstChild("Humanoid") == nil then 
        continue  -- Skip to next part 
 end
Possible Fix
for _, part in ipairs(touchingparts) do
    local humanoid = part.Parent:FindFirstChild("Humanoid")
    if humanoid then
        if part.Parent.Name ~= character.Name and not db then
            db = true
            humanoid:TakeDamage(damage)
            if pogo then -- this isn't really needed
                hrp.Velocity = Vector3.new(hrp.Velocity.X, 75, hrp.Velocity.Z)
            end
        end
    end
end

Yes, I did change my mind on that.. this is a bit smoother, doing the same thing.
Not really sure about the rest of that without testing, this is the main fix..

for _, part in ipairs(touchingparts) do
    local humanoid = part.Parent:FindFirstChild("Humanoid")
    if humanoid then

Avoiding the return there while it keeps checking for the hit.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.