βurak Posted September 17 Share Posted September 17 (edited) 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 September 17 by βurak Link to comment
Shady1 Posted Saturday at 13:30 Share Posted Saturday at 13:30 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) 1 Link to comment
βurak Posted Sunday at 14:05 Author Share Posted Sunday at 14:05 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 1 Link to comment
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now