GrandpaKartul
GrandpaKartul

Reputation: 3

Attempt to index nil with 'humanoid' (Roblox Studio)

I'm trying to code a turret, shooting enemies when it gets in the range of the turret, and the code sometimes breaks giving me the error Script:31: attempt to index nil with 'Humanoid' This happens rarely and only does so when a zombie dies. Not only that but it seems some of the bullets created seem to make way more damage than they really should.

head = script.Parent.PrimaryPart

local function triggershoot(part)
    local beam = Instance.new("Part",script.Parent)

    beam.Anchored = true
    beam.CanCollide = false
    beam.Shape = "Block"
    beam.CFrame = head.CFrame
    beam.Size = Vector3.new(0.5,0.5,0.5)
    beam.Material = "Plastic"
    beam.BrickColor = BrickColor.new("Dark taupe")

    beam.CFrame = CFrame.lookAt(head.Position,part.Position)
    for i = 0, 50, 1 do
        wait()
        beam.CFrame = beam.CFrame + beam.CFrame.LookVector      
        beam.Touched:Connect(function(plr)
            if plr.Parent:HasTag("enemy") then
                plr.Parent.Humanoid:TakeDamage(3)
                beam:Destroy()
            end
        end)
    end
end

while true do
    wait()
    local parts = workspace:GetPartsInPart(script.Parent.zone)
    for _, part in pairs(parts) do
        if part:HasTag("enemy") and part.Parent.Humanoid.Health > 0 then

            local update = CFrame.lookAt(head.Position,part.Position)
            head.CFrame = update
            coroutine.wrap(triggershoot)(part)
            wait(1)
        end
    end
end

Upvotes: 0

Views: 53

Answers (2)

Kemal
Kemal

Reputation: 19

If i am correct you’re running into two main issues in your script:

  1. “attempt to index nil with ‘Humanoid’”

This error occurs because part.Parent.Humanoid is nil when the turret tries to access it. This usually happens when a zombie dies, and its Humanoid object gets destroyed before the turret can check its health or apply damage.

Solution: Add a check to ensure Humanoid exists before trying to use it.

Modify this part:

if part:HasTag("enemy") and part.Parent.Humanoid.Health > 0 then

To this:

local humanoid = part.Parent:FindFirstChild("Humanoid")
if part:HasTag("enemy") and humanoid and humanoid.Health > 0 then
  1. Bullet Damage Applying Multiple Times

You’re connecting the beam.Touched event inside a loop, which means it gets repeatedly connected every frame. This can cause bullets to apply damage multiple times.

Solution: Move the .Touched event outside the loop so it only gets connected once.

Fixed Code:

head = script.Parent.PrimaryPart

local function triggershoot(part)
    local beam = Instance.new("Part")
    beam.Parent = script.Parent
    beam.Anchored = true
    beam.CanCollide = false
    beam.Shape = Enum.PartType.Block
    beam.CFrame = head.CFrame
    beam.Size = Vector3.new(0.5, 0.5, 0.5)
    beam.Material = Enum.Material.Plastic
    beam.BrickColor = BrickColor.new("Dark taupe")

    beam.CFrame = CFrame.lookAt(head.Position, part.Position)

    local function onHit(plr)
        if plr.Parent:HasTag("enemy") then
            local humanoid = plr.Parent:FindFirstChild("Humanoid")
            if humanoid then
                humanoid:TakeDamage(3)
                beam:Destroy()
            end
        end
    end

    beam.Touched:Connect(onHit)

    for i = 1, 50 do
        beam.CFrame = beam.CFrame + beam.CFrame.LookVector
        wait()
    end

    beam:Destroy()
end

while true do
    wait()
    local parts = workspace:GetPartsInPart(script.Parent.zone)

    for _, part in pairs(parts) do
        local humanoid = part.Parent:FindFirstChild("Humanoid")
        if part:HasTag("enemy") and humanoid and humanoid.Health > 0 then
            head.CFrame = CFrame.lookAt(head.Position, part.Position)
            coroutine.wrap(triggershoot)(part)
            wait(1)
        end
    end
end

Summary of Fixes:

  1. Check if Humanoid exists before accessing it (FindFirstChild("Humanoid")).
  2. Move the .Touched event outside the loop so that it connects only once per bullet.
  3. Destroy the bullet after the loop ends to prevent unnecessary parts from staying in the game.

I hope this fixes both of your issues! 🚀

Resources that may help:

  1. Understanding FindFirstChild to Prevent Nil Errors

    • Roblox Developer Hub - FindFirstChild Method 📖 FindFirstChild Documentation
      • This method is essential to check if an object exists before accessing its properties (like Humanoid.Health).
      • It prevents errors like "attempt to index nil with 'Humanoid'".
  2. Properly Handling Touched Events

    • Roblox Developer Hub - Touched Event 📖 Touched Event Documentation
      • The .Touched event can fire multiple times, so you need to connect it once per projectile.
      • Avoid putting .Touched:Connect() inside loops because it creates multiple event listeners, leading to unintended behavior.
    • DevForum - Why Touched Can Fire Multiple Times 📖 Thread on Touched Event Behavior
      • Explains how the event works and common mistakes, like firing multiple damage events per bullet.
  3. Using CFrame.lookAt() for Aiming

    • Roblox Developer Hub - CFrame.lookAt Method 📖 CFrame.lookAt Documentation
      • This function helps the turret rotate towards enemies automatically.
      • You can see how it works with examples.
  4. Coroutine Basics for Running Functions in Parallel

    • Roblox Developer Hub - coroutine.wrap and coroutine.resume 📖 Coroutine Documentation
      • Used to make the turret shoot without pausing the main loop.
      • Without coroutine.wrap(triggershoot)(part), the turret would wait for each bullet to finish before checking other enemies.
  5. Proper Bullet Movement Techniques

    • Roblox Developer Hub - TweenService for Smooth Bullet Movement 📖 TweenService Documentation
      • Instead of using a loop (for i = 1, 50), TweenService can move bullets smoothly without affecting performance.

Upvotes: 0

user15611379
user15611379

Reputation: 359

try moving the touch event function outside the forloop

head = script.Parent.PrimaryPart

local function triggershoot(part)
    local beam = Instance.new("Part",script.Parent)

    beam.Anchored = true
    beam.CanCollide = false
    beam.Shape = "Block"
    beam.CFrame = head.CFrame
    beam.Size = Vector3.new(0.5,0.5,0.5)
    beam.Material = "Plastic"
    beam.BrickColor = BrickColor.new("Dark taupe")

    beam.CFrame = CFrame.lookAt(head.Position,part.Position)
    beam.Touched:Connect(function(plr)
        local char=plr.Parent
        local hum=char and char:FindFirstChildOfClass"Humanoid"
        if char:HasTag("enemy") then
            hum:TakeDamage(3)
            beam:Destroy()
        end
    end)
    for i = 0, 50, 1 do
        wait()
        beam.CFrame = beam.CFrame + beam.CFrame.LookVector      
    end
    beam:Destroy()
end
while true do
    wait()
    local parts = workspace:GetPartsInPart(script.Parent.zone)
    for _, part in ipairs(parts) do
        local h=part.Parent;h=h and h:FindFirstChildOfClass"Humanoid"
        if part:HasTag"enemy"and h and h.Health > 0 then
            local update = CFrame.lookAt(head.Position,part.Position)
            head.CFrame = update
            coroutine.wrap(triggershoot)(part)
            wait(1)
            break
        end
    end
end

Upvotes: 0

Related Questions