Jump to content

problem with the zombie system


βurak

Recommended Posts

How can I code a proper zombie system? I'm trying to do this using a single timer and loop in the code, but I'm having trouble controlling the animations and flow. This is the first time I've done something like this. If anyone has done something like this before, I'd appreciate some help

function findRotation(x1, y1, x2, y2) 
    local t = -math.deg( math.atan2(x2 - x1, y2 - y1))
    return t < 0 and t + 360 or t
end

local zombies = {}

function createZombie(x, y, z, firstTarget)
   local tx,ty = getElementPosition(firstTarget)
	local zombie = createPed(0, x, y, z, findRotation(x,y,tx,ty), true)

   zombies[zombie] = {
      target = firstTarget,
      updateFirstDelay = getTickCount(),
      state = "idle"
   }
   setTimer(doZombieGetupAnim, 50, 1, zombie)
end

function zombieLookTarget(zombie, target)
   if not isElement(target) then return end
   if not isElement(zombie) then return end
   if isPedDead(zombie) then return end
   local tx,ty = getElementPosition(target)
   local zx,zy = getElementPosition(zombie)
   setElementRotation(zombie, 0, 0, findRotation(zx,zy,tx,ty), "default", true)
end

function zombieFollowTarget(zombie)
   if not isElement(zombie) then return end
   if isPedDead(zombie) then return end
   local tx,ty,tz = getElementPosition(zombies[zombie].target)
   local zx,zy,zz = getElementPosition(zombie)
   local distance = getDistanceBetweenPoints3D(tx,ty,tz, zx,zy,zz)
   if distance > 1.5 then
      setPedAnimation(zombie, "ped", "run_fatold", -1, true, true, true, false)
   end
end

function zombieAttackTarget(zombie)
   if not isElement(zombie) then return end
   local tx,ty,tz = getElementPosition(zombies[zombie].target)
   local zx,zy,zz = getElementPosition(zombie)
   local distance = getDistanceBetweenPoints3D(tx,ty,tz, zx,zy,zz)
   if distance <= 1.5 and isPedDead(zombies[zombie].target) then
      setPedAnimation(zombie, "medic", "cpr", -1, true, true, true, false)
   end
end

function stopAnimation(zombie)
   if not isElement(zombie) then return end
   local tx,ty,tz = getElementPosition(zombies[zombie].target)
   local zx,zy,zz = getElementPosition(zombie)
   local distance = getDistanceBetweenPoints3D(tx,ty,tz, zx,zy,zz)
   if distance <= 1.5 then
      setPedAnimation(zombie)
   end
end

function doZombieGetupAnim(zombie)
   if not isElement(zombie) then return end
   if isPedDead(zombie) then return end
   setPedAnimation(zombie, "ped", "getup_front", 1000, false, true, true, false)
end

--I think this is the main problem
function updateZombies()
   for zombie,data in pairs(zombies) do
      zombieLookTarget(zombie, data.target)
      if getTickCount() - data.updateFirstDelay > 1000 then
         zombieFollowTarget(zombie)
      end
   end
end
local updateTimer = setTimer(updateZombies, 150, 0)

addCommandHandler("zombie", function()
   local x,y,z = getElementPosition(localPlayer)
   createZombie(x, y+4, z, localPlayer)
end)

 

Edited by βurak
Link to comment
  • 3 weeks later...
On 17/09/2025 at 05:33, βurak said:

How can I code a proper zombie system? I'm trying to do this using a single timer and loop in the code, but I'm having trouble controlling the animations and flow. This is the first time I've done something like this. If anyone has done something like this before, I'd appreciate some help

function findRotation(x1, y1, x2, y2) 
    local t = -math.deg( math.atan2(x2 - x1, y2 - y1))
    return t < 0 and t + 360 or t
end

local zombies = {}

function createZombie(x, y, z, firstTarget)
   local tx,ty = getElementPosition(firstTarget)
	local zombie = createPed(0, x, y, z, findRotation(x,y,tx,ty), true)

   zombies[zombie] = {
      target = firstTarget,
      updateFirstDelay = getTickCount(),
      state = "idle"
   }
   setTimer(doZombieGetupAnim, 50, 1, zombie)
end

function zombieLookTarget(zombie, target)
   if not isElement(target) then return end
   if not isElement(zombie) then return end
   if isPedDead(zombie) then return end
   local tx,ty = getElementPosition(target)
   local zx,zy = getElementPosition(zombie)
   setElementRotation(zombie, 0, 0, findRotation(zx,zy,tx,ty), "default", true)
end

function zombieFollowTarget(zombie)
   if not isElement(zombie) then return end
   if isPedDead(zombie) then return end
   local tx,ty,tz = getElementPosition(zombies[zombie].target)
   local zx,zy,zz = getElementPosition(zombie)
   local distance = getDistanceBetweenPoints3D(tx,ty,tz, zx,zy,zz)
   if distance > 1.5 then
      setPedAnimation(zombie, "ped", "run_fatold", -1, true, true, true, false)
   end
end

function zombieAttackTarget(zombie)
   if not isElement(zombie) then return end
   local tx,ty,tz = getElementPosition(zombies[zombie].target)
   local zx,zy,zz = getElementPosition(zombie)
   local distance = getDistanceBetweenPoints3D(tx,ty,tz, zx,zy,zz)
   if distance <= 1.5 and isPedDead(zombies[zombie].target) then
      setPedAnimation(zombie, "medic", "cpr", -1, true, true, true, false)
   end
end

function stopAnimation(zombie)
   if not isElement(zombie) then return end
   local tx,ty,tz = getElementPosition(zombies[zombie].target)
   local zx,zy,zz = getElementPosition(zombie)
   local distance = getDistanceBetweenPoints3D(tx,ty,tz, zx,zy,zz)
   if distance <= 1.5 then
      setPedAnimation(zombie)
   end
end

function doZombieGetupAnim(zombie)
   if not isElement(zombie) then return end
   if isPedDead(zombie) then return end
   setPedAnimation(zombie, "ped", "getup_front", 1000, false, true, true, false)
end

--I think this is the main problem
function updateZombies()
   for zombie,data in pairs(zombies) do
      zombieLookTarget(zombie, data.target)
      if getTickCount() - data.updateFirstDelay > 1000 then
         zombieFollowTarget(zombie)
      end
   end
end
local updateTimer = setTimer(updateZombies, 150, 0)

addCommandHandler("zombie", function()
   local x,y,z = getElementPosition(localPlayer)
   createZombie(x, y+4, z, localPlayer)
end)

 

Hello, when I reviewed your code I came across many issues, and I fixed them through testing. You can check again if you’d like. Let me tell you about the changes I made:
The requested "proper zombie system" is now ready:

Uses a single timer
Controlled animations
Smooth flow
Performance optimized

 

function findRotation(x1, y1, x2, y2) 
    local t = -math.deg(math.atan2(x2 - x1, y2 - y1))
    return t < 0 and t + 360 or t
end

local zombies = {}

function createZombie(x, y, z, firstTarget)
    local zombie = createPed(0, x, y, z, 0, true)
    
    zombies[zombie] = {
        target = firstTarget,
        state = "spawning",
        lastAnimation = nil,
        spawnTime = getTickCount(),
        updateDelay = getTickCount()
    }
       setPedAnimation(zombie, "ped", "getup_front", 2000, false, true, true, false)
       setTimer(function()
        if isElement(zombie) and zombies[zombie] then
            zombies[zombie].state = "idle"
        end
    end, 2000, 1)
end

function setZombieAnimation(zombie, anim)
    if not isElement(zombie) or isPedDead(zombie) then return end
    
    local data = zombies[zombie]
    if not data then return end
    if data.lastAnimation == anim then return end
        setPedAnimation(zombie)
    if anim == "idle" then
        setPedAnimation(zombie, "ped", "WALK_drunk", -1, true, true, true, false)
    elseif anim == "chase" then
        setPedAnimation(zombie, "ped", "run_fatold", -1, true, true, true, false)
    elseif anim == "attack" then
        setPedAnimation(zombie, "medic", "cpr", -1, true, true, true, false)
    end
    
    data.lastAnimation = anim
end

function updateZombieState(zombie, data)
    if not isElement(zombie) or isPedDead(zombie) then 
        zombies[zombie] = nil
        return 
    end
    
    if not isElement(data.target) or isPedDead(data.target) then
        data.target = findNearestPlayer(zombie)
        if not data.target then
            data.state = "idle"
            return
        end
    end
    
    local tx, ty, tz = getElementPosition(data.target)
    local zx, zy, zz = getElementPosition(zombie)
    local distance = getDistanceBetweenPoints3D(tx, ty, tz, zx, zy, zz)
    
    setElementRotation(zombie, 0, 0, findRotation(zx, zy, tx, ty), "default", true)
    
    if distance <= 1.5 then
        if data.state ~= "attack" then
            data.state = "attack"
            setZombieAnimation(zombie, "attack")
        end
    elseif distance <= 50 then
        if data.state ~= "chase" then
            data.state = "chase"
            setZombieAnimation(zombie, "chase")
        end

        local angle = findRotation(zx, zy, tx, ty)
        local moveX = math.cos(math.rad(angle)) * 0.1
        local moveY = math.sin(math.rad(angle)) * 0.1
        setElementPosition(zombie, zx + moveX, zy + moveY, zz)
    else
        if data.state ~= "idle" then
            data.state = "idle"
            setZombieAnimation(zombie, "idle")
        end
    end
end

function findNearestPlayer(zombie)
    local nearestPlayer = nil
    local nearestDistance = math.huge
    local zx, zy, zz = getElementPosition(zombie)
    
    for _, player in ipairs(getElementsByType("player")) do
        if player ~= zombie and isElement(player) and not isPedDead(player) then
            local px, py, pz = getElementPosition(player)
            local distance = getDistanceBetweenPoints3D(zx, zy, zz, px, py, pz)
            if distance < nearestDistance then
                nearestDistance = distance
                nearestPlayer = player
            end
        end
    end
    
    return nearestPlayer
end

function updateZombies()
    for zombie, data in pairs(zombies) do
        if data.state == "spawning" then
            if getTickCount() - data.spawnTime > 2000 then
                data.state = "idle"
                setZombieAnimation(zombie, "idle")
            end
        else
            if getTickCount() - data.updateDelay > 100 then
                updateZombieState(zombie, data)
                data.updateDelay = getTickCount()
            end
        end
    end
end

local updateTimer = setTimer(updateZombies, 200, 0)

addEventHandler("onClientElementDestroy", root, function()
    if zombies[source] then
        zombies[source] = nil
    end
end)

addCommandHandler("zombie", function()
    local x, y, z = getElementPosition(localPlayer)
    createZombie(x, y + 4, z, localPlayer)
end)

 

  • Thanks 1
Link to comment
On 04/10/2025 at 16:30, Shady1 said:

Hello, when I reviewed your code I came across many issues, and I fixed them through testing. You can check again if you’d like. Let me tell you about the changes I made:
The requested "proper zombie system" is now ready:

Uses a single timer
Controlled animations
Smooth flow
Performance optimized

 

function findRotation(x1, y1, x2, y2) 
    local t = -math.deg(math.atan2(x2 - x1, y2 - y1))
    return t < 0 and t + 360 or t
end

local zombies = {}

function createZombie(x, y, z, firstTarget)
    local zombie = createPed(0, x, y, z, 0, true)
    
    zombies[zombie] = {
        target = firstTarget,
        state = "spawning",
        lastAnimation = nil,
        spawnTime = getTickCount(),
        updateDelay = getTickCount()
    }
       setPedAnimation(zombie, "ped", "getup_front", 2000, false, true, true, false)
       setTimer(function()
        if isElement(zombie) and zombies[zombie] then
            zombies[zombie].state = "idle"
        end
    end, 2000, 1)
end

function setZombieAnimation(zombie, anim)
    if not isElement(zombie) or isPedDead(zombie) then return end
    
    local data = zombies[zombie]
    if not data then return end
    if data.lastAnimation == anim then return end
        setPedAnimation(zombie)
    if anim == "idle" then
        setPedAnimation(zombie, "ped", "WALK_drunk", -1, true, true, true, false)
    elseif anim == "chase" then
        setPedAnimation(zombie, "ped", "run_fatold", -1, true, true, true, false)
    elseif anim == "attack" then
        setPedAnimation(zombie, "medic", "cpr", -1, true, true, true, false)
    end
    
    data.lastAnimation = anim
end

function updateZombieState(zombie, data)
    if not isElement(zombie) or isPedDead(zombie) then 
        zombies[zombie] = nil
        return 
    end
    
    if not isElement(data.target) or isPedDead(data.target) then
        data.target = findNearestPlayer(zombie)
        if not data.target then
            data.state = "idle"
            return
        end
    end
    
    local tx, ty, tz = getElementPosition(data.target)
    local zx, zy, zz = getElementPosition(zombie)
    local distance = getDistanceBetweenPoints3D(tx, ty, tz, zx, zy, zz)
    
    setElementRotation(zombie, 0, 0, findRotation(zx, zy, tx, ty), "default", true)
    
    if distance <= 1.5 then
        if data.state ~= "attack" then
            data.state = "attack"
            setZombieAnimation(zombie, "attack")
        end
    elseif distance <= 50 then
        if data.state ~= "chase" then
            data.state = "chase"
            setZombieAnimation(zombie, "chase")
        end

        local angle = findRotation(zx, zy, tx, ty)
        local moveX = math.cos(math.rad(angle)) * 0.1
        local moveY = math.sin(math.rad(angle)) * 0.1
        setElementPosition(zombie, zx + moveX, zy + moveY, zz)
    else
        if data.state ~= "idle" then
            data.state = "idle"
            setZombieAnimation(zombie, "idle")
        end
    end
end

function findNearestPlayer(zombie)
    local nearestPlayer = nil
    local nearestDistance = math.huge
    local zx, zy, zz = getElementPosition(zombie)
    
    for _, player in ipairs(getElementsByType("player")) do
        if player ~= zombie and isElement(player) and not isPedDead(player) then
            local px, py, pz = getElementPosition(player)
            local distance = getDistanceBetweenPoints3D(zx, zy, zz, px, py, pz)
            if distance < nearestDistance then
                nearestDistance = distance
                nearestPlayer = player
            end
        end
    end
    
    return nearestPlayer
end

function updateZombies()
    for zombie, data in pairs(zombies) do
        if data.state == "spawning" then
            if getTickCount() - data.spawnTime > 2000 then
                data.state = "idle"
                setZombieAnimation(zombie, "idle")
            end
        else
            if getTickCount() - data.updateDelay > 100 then
                updateZombieState(zombie, data)
                data.updateDelay = getTickCount()
            end
        end
    end
end

local updateTimer = setTimer(updateZombies, 200, 0)

addEventHandler("onClientElementDestroy", root, function()
    if zombies[source] then
        zombies[source] = nil
    end
end)

addCommandHandler("zombie", function()
    local x, y, z = getElementPosition(localPlayer)
    createZombie(x, y + 4, z, localPlayer)
end)

 

It's working even worse. Anyway, thanks for answering. I guess I'll have to find it myself

  • Confused 1
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...