Making Client side Animations for NPC's

Currently I’m trying to make a Enemy NPC Play Animations on a local script instead of a server script (For performance and that stuff), however I’m having problems having the server script and local script work together.

Currently i have a server script finding the players position and sending it via a remote event to a local script that uses that position to play certain animations. I do have it on a Heartbeat which i think is causing me to get a Remote event invocation queue exhausted Error. so i was hoping someone knew a better way of doing this.

For server script

function FindNearestPlayer()
   local playerList = Players:GetPlayers()

   local nearestPlayer = nil
   local distance = nil
   local direction = nil

   for _, player in pairs(playerList) do
	   local character = player.Character
	   local distanceVector = (player.Character.HumanoidRootPart.Position - root.Position)
	   if character then	
		   if not nearestPlayer then 
			   nearestPlayer = player
			   distance = distanceVector.Magnitude
			   direction = distanceVector.Unit

		   elseif distanceVector.Magnitude < distance then
			   nearestPlayer = player
			   distance = distanceVector.Magnitude
			   direction = distanceVector.Unit
		   end
	   end
   end
   AnimationRemote:FireClient(nearestPlayer, distance, direction) 
end


RunService.Heartbeat:Connect(function() 
    FindNearestPlayer()
end)

for local script

AnimationRemote.OnClientEvent:Connect(function(nearestPlayer, distance, direction)
    if nearestPlayer then
	   if distance <= TargetDistance and distance >= StopDistance then
		  --Runs when walking
		  if not WalkingTrack.IsPlaying then
			   WalkingTrack:Play()
	   else
		   --Runs when standing still
		   if WalkingTrack.IsPlaying then
			  WalkingTrack:Stop()
		   end
	   end

	   if distance <= AttackDistance and tick() - LastAttack >= AttackCooldown and Model:GetAttribute("Stunned") == false then
		   --runs when attacking

		   AttackTrack:Play()
		   LastAttack = tick()
	   end
    end
end)
1 Like

Sorry for the large amount of edits it my first time posting so i messed up alot

2 Likes

It’s better if you make this entire system on the Client Instead of Communicating it From Server To Client.

I’ve heard that having movement for NPC on the client can lead to problems with high ping and hacking so i thought it was better

Cause In this case your looping it for every character which is unneeded traffic to the server. When you could just do it on the client.

Is this script inside the character or the NPC?

its is inside the NPC Humanoid

And what your tryna do is when its near a Player make it play an animation?

That’s exactly what I’m trying to do

1 Like

So that you don’t have the same script for like many NPCs which would cause more lag. I rather you find the NPCs on the Client of the User.

What do you mean by that exactly?

I’ll write some code so I can show you what I mean

1 Like

Are you looping this or when does the Remote Get Fired?

every heartbeat so tick i guess

local PlayerService = game:GetService('Players')
local RunService = game:GetService('RunService')
-- Constants {Global Constants} --
local LocalPlayer = PlayerService.LocalPlayer
local Character = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait()
local NPCFolder = workspace:FindFirstChild('NPCs')
-- Constants {Global Booleans} --
local LoopNPCs = false
----

-- Functions {Get Models Distance} --
local function ClosestNPC()
	for _,NPCModel in NPCFolder:GetChildren() do
		-- Check {If NPC IsA Model} --
		if NPCModel:IsA('Model') and NPCModel:FindFirstChildOfClass('Humanoid') then
			local NPCHumanoidRootPart = NPCModel.HumanoidRootPart
			-- Check {Distances} --
			if (NPCHumanoidRootPart.Position - Character.HumanoidRootPart.Position).Magnitude < 9 then
				LoopNPCs = false
				-- Animate {Play Desired Animation} --
			else
				-- Animate {Stop Desired Animation} --
			end
			----
			
			-- Check {In Striking Distance} --
			if (NPCHumanoidRootPart.Position - Character.HumanoidRootPart.Position).Magnitude < 5 then
				-- Animate {Play Desired Animation} --
			end
		end
	end
end
----

-- RunService {Render Stepping} --
RunService.RenderStepped:Connect(function()
	ClosestNPC()
end)
----

So Here’s a Fully Client Sided Version Of Your Script. What I was trying to itch to you was that the whole character checking you were doing wasn’t really needed. As your looping every character the Server might experience lag spikes if there’s a lot going on while that is running.

1 Like

I replaced the Heartbeat. With RenderStepped Since we’re making it completely Client Sided.

thank you so much dude this is hella helpful

1 Like

Let me know if it works if it doesn’t I’ll help you troubleshoot.

question in this part the bottom of the script is for in range and the top is out of range right?

From what i understood i tried this but it didnt work

local function ClosestNPC()
     for _,NPCModel in NPCFolder:GetChildren() do
	    -- Check {If NPC IsA Model} --
	    if NPCModel:IsA('Model') and NPCModel:FindFirstChildOfClass('Humanoid') then
		   local NPCHumanoidRootPart = NPCModel.HumanoidRootPart
		      -- Check {Distances} --
		    if (NPCHumanoidRootPart.Position - Character.HumanoidRootPart.Position).Magnitude < 9 then
			    LoopNPCs = false
			    -- Animate {Play Desired Animation} --
			   IdleTrack:Play()
			   WalkingTrack:Stop()
			   AttackTrack:Stop()
		   else
			    -- Animate {Stop Desired Animation} --
			    IdleTrack:Stop()
			    WalkingTrack:Play()
			    AttackTrack:Stop()
		    end
		    ----

		    -- Check {In Striking Distance} --
		    if (NPCHumanoidRootPart.Position - Character.HumanoidRootPart.Position).Magnitude < 5 then
			    LoopNPCs = false
			    AttackTrack:Play()
			    AttackTrack.Ended:Wait()
			    LoopNPCs = true
		    end
	    end
    end
end