How can I animate all zombies on the client?

So I’m trying to optimize my game as much as possible, and I want to handle all the zombie animations on the client. How can I do this?

My current code: (doesn’t do anything)

while wait() do
	for _, child in pairs(workspace:GetDescendants()) do
		if child.Name == "Basic Zombie" and not child:FindFirstChild("Animate") then
			local AnimateScript = script.Animate:Clone()
			AnimateScript.Parent = child
			AnimateScript.Disabled = false
			print("zombie animated")
		end
	end
end

AnimateScript is the R6 animation script, but in a local script.
The script is named AnimateZombies in StarterPlayerScripts.

1 Like

Wait so your intended result is that the zombies are different for each player?, also getting the descendants of everything in workspace, is probably a bad idea

1 Like

I want to animate all the zombies on the client. Was that not obvious enough…?

It helps optimize.

1 Like

What’s wrong with the code? also use task.Wait() and searching through every descendant of the workspace is going to extremly unoptimized

1 Like
  1. It’s on the client
  2. Forgot to use task.wait() lol

Oh, also it just doesn’t work at all

1.) Im sorry if im being really dumb but whats wrong?
2.) you shouldn’t search through every descendant of the workspace and instead make a folder called zombies and search through that
3.) I don’t tthink he while loop is a optimal soultion

2 Likes

The code doesn’t work. It only copies the script, but doesn’t animate the zombies.

so the amination script doesn’t work?

Yeah. I was expecting it to because it was the normal R6 animation script though, just in a local script

local zombiesFolder = workspace:WaitForChild("ZombiesFolder")
local function applyAnimation()
        local AnimateScript = script.Animate:Clone()
	AnimateScript.Parent = child
	AnimateScript.Disabled = false
end

for _, zombie in ipairs(zombieFolder:GetChildren()) do
        applyAnimation()
end
zombiesFolder.ChildAdded:Connect(applyAnimation)

I dont know about the animation but this is still an optimization to the original script

If you want to optimize, I have a video that shows you how to run multiple things with one script.

Video

Roblox - Controlling Multiple AI with One Script (advanced) - Scripting Tutorial - YouTube

You can’t run local scripts inside the workspace. They will only work as a descendant of StarterPlayer, StarterPack, or StarterGui.

There are a few ways you could go about this, I would probably use CollectionService to ‘tag’ the models as Zombies and then get all the tagged zombie models from my local script and animate them.

It’s also never a good idea to loop through every instance of the workspace in such a tight loop like that. Instead, you should place all your zombies into a zombie folder so it doesn’t have to loop through hundreds of random building parts unnecessarily.

1 Like

I’ve already controlled each AI with a single script. I just don’t know how to make them animated with a single script, and better yet, on the client.

Also, I did this for the old script in our first revision of the game:

while wait() do
	for _, child in pairs(workspace:GetDescendants()) do
		if child.Name == "Basic Zombie" and not child:FindFirstChild("Animate") then
			function   waitForChild(parent, childName)
				local lolxd = parent:findFirstChild(childName)
				if lolxd then return lolxd end
				while true do
					lolxd = parent.ChildAdded:wait()
					if lolxd.Name==childName then return lolxd end
				end
			end

			local Figure = child
			local Torso = waitForChild(Figure, "Torso")
			local RightShoulder = waitForChild(Torso, "Right Shoulder")
			local LeftShoulder = waitForChild(Torso, "Left Shoulder")
			local RightHip = waitForChild(Torso, "Right Hip")
			local LeftHip = waitForChild(Torso, "Left Hip")
			local Neck = waitForChild(Torso, "Neck")
			local Humanoid = waitForChild(Figure, "Humanoid")
			local pose = "Standing"

			local currentAnim = ""
			local currentAnimTrack = nil
			local currentAnimKeyframeHandler = nil
			local currentAnimSpeed = 1.0
			local animTable = {}
			local animNames = { 
				idle = 	{	
					{ id = "http://www.roblox.com/asset/?id=125750544", weight = 9 },
					{ id = "http://www.roblox.com/asset/?id=125750618", weight = 1 }
				},
				walk = 	{ 	
					{ id = "http://www.roblox.com/asset/?id=125749145", weight = 10 } 
				}, 
				run = 	{
					{ id = "run.xml", weight = 10 } 
				}, 
				jump = 	{
					{ id = "http://www.roblox.com/asset/?id=125750702", weight = 10 } 
				}, 
				fall = 	{
					{ id = "http://www.roblox.com/asset/?id=125750759", weight = 10 } 
				}, 
				climb = {
					{ id = "http://www.roblox.com/asset/?id=125750800", weight = 10 } 
				}, 
				toolnone = {
					{ id = "http://www.roblox.com/asset/?id=125750867", weight = 10 } 
				},
				toolslash = {
					{ id = "http://www.roblox.com/asset/?id=129967390", weight = 10 } 
					--				{ id = "slash.xml", weight = 10 } 
				},
				toollunge = {
					{ id = "http://www.roblox.com/asset/?id=129967478", weight = 10 } 
				},
				wave = {
					{ id = "http://www.roblox.com/asset/?id=128777973", weight = 10 } 
				},
				point = {
					{ id = "http://www.roblox.com/asset/?id=128853357", weight = 10 } 
				},
				dance = {
					{ id = "http://www.roblox.com/asset/?id=130018893", weight = 10 }, 
					{ id = "http://www.roblox.com/asset/?id=132546839", weight = 10 }, 
					{ id = "http://www.roblox.com/asset/?id=132546884", weight = 10 } 
				},
				dance2 = {
					{ id = "http://www.roblox.com/asset/?id=160934142", weight = 10 }, 
					{ id = "http://www.roblox.com/asset/?id=160934298", weight = 10 }, 
					{ id = "http://www.roblox.com/asset/?id=160934376", weight = 10 } 
				},
				dance3 = {
					{ id = "http://www.roblox.com/asset/?id=160934458", weight = 10 }, 
					{ id = "http://www.roblox.com/asset/?id=160934530", weight = 10 }, 
					{ id = "http://www.roblox.com/asset/?id=160934593", weight = 10 } 
				},
				laugh = {
					{ id = "http://www.roblox.com/asset/?id=129423131", weight = 10 } 
				},
				cheer = {
					{ id = "http://www.roblox.com/asset/?id=129423030", weight = 10 } 
				},
			}

			-- Existance in this list signifies that it is an emote, the value indicates if it is a looping emote
			local emoteNames = { wave = false, point = false, dance = true, dance2 = true, dance3 = true, laugh = false, cheer = false}

			math.randomseed(tick())

			function configureAnimationSet(name, fileList)
				if (animTable[name] ~= nil) then
					for _, connection in pairs(animTable[name].connections) do
						connection:disconnect()
					end
				end
				animTable[name] = {}
				animTable[name].count = 0
				animTable[name].totalWeight = 0	
				animTable[name].connections = {}

				-- check for config values
				local config = script:FindFirstChild(name)
				if (config ~= nil) then
					--		print("Loading anims " .. name)
					table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
					table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
					local idx = 1
					for _, childPart in pairs(config:GetChildren()) do
						if (childPart:IsA("Animation")) then
							table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
							animTable[name][idx] = {}
							animTable[name][idx].anim = childPart
							local weightObject = childPart:FindFirstChild("Weight")
							if (weightObject == nil) then
								animTable[name][idx].weight = 1
							else
								animTable[name][idx].weight = weightObject.Value
							end
							animTable[name].count = animTable[name].count + 1
							animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
							--			print(name .. " [" .. idx .. "] " .. animTable[name][idx].anim.AnimationId .. " (" .. animTable[name][idx].weight .. ")")
							idx = idx + 1
						end
					end
				end

				-- fallback to defaults
				if (animTable[name].count <= 0) then
					for idx, anim in pairs(fileList) do
						animTable[name][idx] = {}
						animTable[name][idx].anim = Instance.new("Animation")
						animTable[name][idx].anim.Name = name
						animTable[name][idx].anim.AnimationId = anim.id
						animTable[name][idx].weight = anim.weight
						animTable[name].count = animTable[name].count + 1
						animTable[name].totalWeight = animTable[name].totalWeight + anim.weight
						--			print(name .. " [" .. idx .. "] " .. anim.id .. " (" .. anim.weight .. ")")
					end
				end
			end

			-- Setup animation objects
			function scriptChildModified(child)
				local fileList = animNames[child.Name]
				if (fileList ~= nil) then
					configureAnimationSet(child.Name, fileList)
				end	
			end

			script.ChildAdded:connect(scriptChildModified)
			script.ChildRemoved:connect(scriptChildModified)


			for name, fileList in pairs(animNames) do 
				configureAnimationSet(name, fileList)
			end	

			-- ANIMATION

			-- declarations
			local toolAnim = "None"
			local toolAnimTime = 0

			local jumpAnimTime = 0
			local jumpAnimDuration = 0.3

			local toolTransitionTime = 0.1
			local fallTransitionTime = 0.3
			local jumpMaxLimbVelocity = 0.75

			-- functions

			function stopAllAnimations()
				local oldAnim = currentAnim

				-- return to idle if finishing an emote
				if (emoteNames[oldAnim] ~= nil and emoteNames[oldAnim] == false) then
					oldAnim = "idle"
				end

				currentAnim = ""
				if (currentAnimKeyframeHandler ~= nil) then
					currentAnimKeyframeHandler:disconnect()
				end

				if (currentAnimTrack ~= nil) then
					currentAnimTrack:Stop()
					currentAnimTrack:Destroy()
					currentAnimTrack = nil
				end
				return oldAnim
			end

			function setAnimationSpeed(speed)
				if speed ~= currentAnimSpeed then
					currentAnimSpeed = speed
					currentAnimTrack:AdjustSpeed(currentAnimSpeed)
				end
			end

			function keyFrameReachedFunc(frameName)
				if (frameName == "End") then
					--		print("Keyframe : ".. frameName)
					local repeatAnim = stopAllAnimations()
					local animSpeed = currentAnimSpeed
					playAnimation(repeatAnim, 0.0, Humanoid)
					setAnimationSpeed(animSpeed)
				end
			end

			-- Preload animations
			function playAnimation(animName, transitionTime, humanoid)
				local idleFromEmote = (animName == "idle" and emoteNames[currentAnim] ~= nil)
				if (animName ~= currentAnim and not idleFromEmote) then		 

					if (currentAnimTrack ~= nil) then
						currentAnimTrack:Stop(transitionTime)
						currentAnimTrack:Destroy()
					end

					currentAnimSpeed = 1.0
					local roll = math.random(1, animTable[animName].totalWeight) 
					local origRoll = roll
					local idx = 1
					while (roll > animTable[animName][idx].weight) do
						roll = roll - animTable[animName][idx].weight
						idx = idx + 1
					end
					--		print(animName .. " " .. idx .. " [" .. origRoll .. "]")
					local anim = animTable[animName][idx].anim

					-- load it to the humanoid; get AnimationTrack
					currentAnimTrack = humanoid:LoadAnimation(anim)

					-- play the animation
					currentAnimTrack:Play(transitionTime)
					currentAnim = animName

					-- set up keyframe name triggers
					if (currentAnimKeyframeHandler ~= nil) then
						currentAnimKeyframeHandler:disconnect()
					end
					currentAnimKeyframeHandler = currentAnimTrack.KeyframeReached:connect(keyFrameReachedFunc)
				end
			end

			-------------------------------------------------------------------------------------------
			-------------------------------------------------------------------------------------------

			local toolAnimName = ""
			local toolAnimTrack = nil
			local currentToolAnimKeyframeHandler = nil

			function toolKeyFrameReachedFunc(frameName)
				if (frameName == "End") then
					--		print("Keyframe : ".. frameName)
					local repeatAnim = stopToolAnimations()
					playToolAnimation(repeatAnim, 0.0, Humanoid)
				end
			end


			function playToolAnimation(animName, transitionTime, humanoid)
				if (animName ~= toolAnimName) then		 

					if (toolAnimTrack ~= nil) then
						toolAnimTrack:Stop()
						toolAnimTrack:Destroy()
						transitionTime = 0
					end

					local roll = math.random(1, animTable[animName].totalWeight) 
					local origRoll = roll
					local idx = 1
					while (roll > animTable[animName][idx].weight) do
						roll = roll - animTable[animName][idx].weight
						idx = idx + 1
					end
					--		print(animName .. " * " .. idx .. " [" .. origRoll .. "]")
					local anim = animTable[animName][idx].anim

					-- load it to the humanoid; get AnimationTrack
					toolAnimTrack = humanoid:LoadAnimation(anim)

					-- play the animation
					toolAnimTrack:Play(transitionTime)
					toolAnimName = animName

					currentToolAnimKeyframeHandler = toolAnimTrack.KeyframeReached:connect(toolKeyFrameReachedFunc)
				end
			end

			function stopToolAnimations()
				local oldAnim = toolAnimName

				if (currentToolAnimKeyframeHandler ~= nil) then
					currentToolAnimKeyframeHandler:disconnect()
				end

				toolAnimName = ""
				if (toolAnimTrack ~= nil) then
					toolAnimTrack:Stop()
					toolAnimTrack:Destroy()
					toolAnimTrack = nil
				end


				return oldAnim
			end

			-------------------------------------------------------------------------------------------
			-------------------------------------------------------------------------------------------


			function onRunning(speed)
				if speed>0.01 then
					playAnimation("walk", 0.1, Humanoid)
					pose = "Running"
				else
					playAnimation("idle", 0.1, Humanoid)
					pose = "Standing"
				end
			end

			function onDied()
				pose = "Dead"
			end

			function onJumping()
				playAnimation("jump", 0.1, Humanoid)
				jumpAnimTime = jumpAnimDuration
				pose = "Jumping"
			end

			function onClimbing(speed)
				playAnimation("climb", 0.1, Humanoid)
				setAnimationSpeed(speed / 12.0)
				pose = "Climbing"
			end

			function onGettingUp()
				pose = "GettingUp"
			end

			function onFreeFall()
				if (jumpAnimTime <= 0) then
					playAnimation("fall", fallTransitionTime, Humanoid)
				end
				pose = "FreeFall"
			end

			function onFallingDown()
				pose = "FallingDown"
			end

			function onSeated()
				pose = "Seated"
			end

			function onPlatformStanding()
				pose = "PlatformStanding"
			end

			function onSwimming(speed)
				if speed>0 then
					pose = "Running"
				else
					pose = "Standing"
				end
			end

			function getTool()	
				for _, kid in ipairs(Figure:GetChildren()) do
					if kid.className == "Tool" then return kid end
				end
				return nil
			end

			function getToolAnim(tool)
				for _, c in ipairs(tool:GetChildren()) do
					if c.Name == "toolanim" and c.className == "StringValue" then
						return c
					end
				end
				return nil
			end

			function animateTool()

				if (toolAnim == "None") then
					playToolAnimation("toolnone", toolTransitionTime, Humanoid)
					return
				end

				if (toolAnim == "Slash") then
					playToolAnimation("toolslash", 0, Humanoid)
					return
				end

				if (toolAnim == "Lunge") then
					playToolAnimation("toollunge", 0, Humanoid)
					return
				end
			end

			function moveSit()
				RightShoulder.MaxVelocity = 0.15
				LeftShoulder.MaxVelocity = 0.15
				RightShoulder:SetDesiredAngle(3.14 /2)
				LeftShoulder:SetDesiredAngle(-3.14 /2)
				RightHip:SetDesiredAngle(3.14 /2)
				LeftHip:SetDesiredAngle(-3.14 /2)
			end

			local lastTick = 0

			function move(time)
				local amplitude = 1
				local frequency = 1
				local deltaTime = time - lastTick
				lastTick = time

				local climbFudge = 0
				local setAngles = false

				if (jumpAnimTime > 0) then
					jumpAnimTime = jumpAnimTime - deltaTime
				end

				if (pose == "FreeFall" and jumpAnimTime <= 0) then
					playAnimation("fall", fallTransitionTime, Humanoid)
				elseif (pose == "Seated") then
					stopAllAnimations()
					moveSit()
					return
				elseif (pose == "Running") then
					playAnimation("walk", 0.1, Humanoid)
				elseif (pose == "Dead" or pose == "GettingUp" or pose == "FallingDown" or pose == "Seated" or pose == "PlatformStanding") then
					--		print("Wha " .. pose)
					amplitude = 0.1
					frequency = 1
					setAngles = true
				end

				if (setAngles) then
					desiredAngle = amplitude * math.sin(time * frequency)

					RightShoulder:SetDesiredAngle(desiredAngle + climbFudge)
					LeftShoulder:SetDesiredAngle(desiredAngle - climbFudge)
					RightHip:SetDesiredAngle(-desiredAngle)
					LeftHip:SetDesiredAngle(-desiredAngle)
				end

				-- Tool Animation handling
				local tool = getTool()
				if tool then

					animStringValueObject = getToolAnim(tool)

					if animStringValueObject then
						toolAnim = animStringValueObject.Value
						-- message recieved, delete StringValue
						animStringValueObject.Parent = nil
						toolAnimTime = time + .3
					end

					if time > toolAnimTime then
						toolAnimTime = 0
						toolAnim = "None"
					end

					animateTool()		
				else
					stopToolAnimations()
					toolAnim = "None"
					toolAnimTime = 0
				end
			end

			-- connect events
			Humanoid.Died:connect(onDied)
			Humanoid.Running:connect(onRunning)
			Humanoid.Jumping:connect(onJumping)
			Humanoid.Climbing:connect(onClimbing)
			Humanoid.GettingUp:connect(onGettingUp)
			Humanoid.FreeFalling:connect(onFreeFall)
			Humanoid.FallingDown:connect(onFallingDown)
			Humanoid.Seated:connect(onSeated)
			Humanoid.PlatformStanding:connect(onPlatformStanding)
			Humanoid.Swimming:connect(onSwimming)

			-- setup emote chat hook


			-- main program

			local runService = game:service("RunService");

			-- initialize to idle
			playAnimation("idle", 0.1, Humanoid)
			pose = "Standing"

			while Figure.Parent~=nil do
				local _, time = wait(0.1)
				move(time)
			end



		end
	end
end

It doesn’t work because there’s a while loop:
image

How could I make this not yield the code??