Strange hitbox script behavior

This is a continuation of another issue I’ve had. I have a script that spawns a model with a hitbox. I then have another script that detects the hitbox and applies damage. The issue is that half of the time when I start a test in studio, the hitbox script wont work unless there’s already a hitbox in existence, which is demonstrated in the video where the first test it works as intended, but the second time it has the issue. (Ignore that I don’t take damage, I hadn’t implemented it yet at the time of the video.)

Here is the script that clones/spawns the model.

game.ReplicatedStorage.RemoteEvent.OnServerEvent:Connect(function(player,mousehit)
	local model = workspace.Model:Clone()
	model.Hitbox:AddTag("hitbox")
	model.Parent = workspace
	model.PrimaryPart.Position = mousehit.Position
	model.Stars.Position = mousehit.Position
	model.Hitbox.Position = mousehit.Position
	local stuff = model:GetDescendants()
	for i, v in pairs(stuff) do 
		if v:IsA("ParticleEmitter") then 
			local emitcount = v:GetAttribute("EmitCount")
			local waittime = v:GetAttribute("EmitDelay")
			local emittime = v:GetAttribute("EmitDuration")
			task.spawn(function()
				task.wait(2.25)
				model:Destroy()
			end)
			if waittime == 0 and emittime == 0 then 
				task.spawn(function()
					task.wait(0.01)
					v:Emit(emitcount)
				end)
			elseif waittime > 0 and emittime == 0 then  task.spawn(function()
					task.wait(waittime)
					v:Emit(emitcount)
				end)
			else 
				task.spawn(function()
					v.Enabled = true 
					task.wait(emittime)
					v.Enabled = false
				end)
			end
		end
		if v:IsA("Sound") then
			v:Play()
		end
	end
end)

And here is the script that detects and handles hitbox logic:

local cs = game:GetService("CollectionService")

game.ReplicatedStorage.RemoteEvent.OnServerEvent:Connect(function()
	local hitbox = cs:GetTagged("hitbox")
	for i, v in pairs(hitbox) do 
		local parts = game.Workspace:GetPartsInPart(v)
		print(parts)
		for i, part in pairs(parts) do 
			if part.Parent:FindFirstChild("Humanoid") then
				part.Parent.Humanoid.Health = 0
			end
		end
		v:Destroy()
	end
end)

Also, let me know if there’s anything different I should be doing in my coding as I’m a new scripter with only about 2 months of experience.

If these are the same remote events, the problem is probably that it’s undetermined which block of code runs first, since they’re responding to the same event.

So for example, the GetTagged code might run first and see no hitboxes, then the Model:Clone() code would run and add the hitbox, but nothing would happen because the hitbox code already finished.

I would update the hitbox logic to start when a hitbox tag is added using events:

local cs = game:GetService("CollectionService")

local HITBOX_TAG = "hitbox"

local function onHitboxAdded(hitbox)
    -- Your hitbox code here (the get parts in stuff)
end

local initialHitboxes = cs:GetTagged(HITBOX_TAG)

-- Run the hitbox code for every already existing hitbox
for _, hitbox in ipairs(initialHitboxes) do
    onHitboxAdded(hitbox)
end

-- Run the hitbox code for every future hitbox
cs.GetInstanceAddedSignal(HITBOX_TAG):Connect(onHitboxAdded)

This way the hitbox code runs directly after a hitbox is added, instead of before or after a hitbox is added for every hitbox.

Super cool effect by the way!!

You can merge both event handlers into one. You already wrote the cloning part to not yield which is fantastic. You can just do the hitbox logic at the end of the cloning handler. Something like this:

			v:Play()
		end
	end
	local parts = game.Workspace:GetPartsInPart(model.Hitbox)
	print(parts)
	for i, part in pairs(parts) do 
		if part.Parent:FindFirstChild("Humanoid") then
			part.Parent.Humanoid.Health = 0
		end
	end
end)

You don’t need to loop through existing hitboxes or track them with tags because they can be used for part detection instantly. If you have clients that also need the hitbox for something, you can just destroy it with the rest of model. There’s no rush since this check only happens once it’s created.

nitpick

I notice you spawn new threads that wait before doing stuff. You can try using task.delay to save a line of code. If you’re just destroying something after a moment, the Debris service is even more reasonable.

task.delay(2.25, function()
	model:Destroy()
end)
-- alternatively
local Debris = game:GetService("Debris")

Debris:AddItem(model, 2.25)

Thank you both for your solutions, they both worked. If I could I’d mark both replies as solutions.

1 Like

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