NPC Optimization / Lag reducing

Hey im trying to reduce spiking in my npcs I dont know why its spiking so much I think its because I set it up so all my NPCS in one heartbeat and that may be creating lag?

Things to note

  • all my npcs physics are turned off
local Tab = {

	Chase = function(NPC,Target,Tags,Data)
		local root = NPC.HumanoidRootPart
		local enemy = Target.Value
		local ehum = enemy:FindFirstChild("Humanoid")
		local eroot = enemy:FindFirstChild("HumanoidRootPart")
		local pos = eroot.Position
		if not ActionCheck(Tags) then
			if math.random(1, 3) == 1 and ((root.Position - eroot.Position).magnitude > 15) and Data.Type ~= "Robot" then
				return combatModule.DashNPC(NPC, "Front")
			end
			if Data.Type ~= "Robot" then
				combatModule.Run(NPC)
				NPCModule:MoveTo(NPC, pos, Data, enemy, true)
			else
				if not Data.FirstChase then
					Data.FirstChase = true
					Data.FirstMag = (root.Position - eroot.Position).magnitude
				end
				local Run = Data.LoadedAnims.Run
				if math.random(1,45) == 1 and os.clock() - Data.LastCool > 15 and (root.Position - eroot.Position).magnitude < 50 then
					Data.LastCool = os.clock()
					local Action = create("Action",nil,Data.Tags)
					Run:Stop()
					local Laser = Data.LoadedAnims.Laser
					Laser:Play()
					NPC.Humanoid.WalkSpeed = 0
					task.wait(.35)
					game:GetService("TweenService"):Create(NPC["Cube.007"],TweenInfo.new(.50,Enum.EasingStyle.Sine,Enum.EasingDirection.Out), {Color = Color3.fromRGB(255,255,255)}):Play()
					task.wait(.30)
					game:GetService("TweenService"):Create(NPC["Cube.007"],TweenInfo.new(1,Enum.EasingStyle.Sine,Enum.EasingDirection.Out), {Color = Color3.fromRGB(255,0,0)}):Play()
					Laser.Stopped:Wait()
					game:GetService("TweenService"):Create(NPC["Cube.007"],TweenInfo.new(1,Enum.EasingStyle.Sine,Enum.EasingDirection.Out), {Color = Color3.fromRGB(0,0,0)}):Play()
					NPC.Humanoid.WalkSpeed = 10
					Action:Destroy()
					return
				end
				if not Run.IsPlaying and not ActionCheck(Tags) then
					Run:Play()
					Run:GetMarkerReachedSignal("Step"):Connect(function()
						coroutine.wrap(function()
							for i,v in pairs(game.Workspace.World.Live:GetChildren()) do
								if v:IsA("Model") and v:FindFirstChild("Humanoid") and (v:FindFirstChild("HumanoidRootPart")) and game.Players:FindFirstChild(v.Name) and (v:FindFirstChild("HumanoidRootPart").Position - root.Position).Magnitude <= 250 then
									local TargetPlayer = game.Players:GetPlayerFromCharacter(v)
									if not Data.FirstChase[v.Name] then
										Data.FirstChase[v.Name] = true
										Data.FirstMag[v.Name] = (root.Position - v:FindFirstChild("HumanoidRootPart").Position).magnitude
									end
									if not Data.shakecool[v.Name] then
										Data.shakecool[v.Name] = true
										VisualEvent:FireClient(TargetPlayer,"CameraShake",10,v,root,true,Data.FirstMag[v.Name])
										task.wait()
										Data.shakecool[v.Name] = nil
									end
								end	
							end
						end)()
					end)
				end
				NPCModule:MoveTo(NPC, pos, Data, enemy, true)
			end	
		end
		return
	end,

	Block = function(NPC,Target,Tags,Data)
		Data.FirstChase = {}
		Data.FirstMag = {}
		if ActionCheck(Tags) or Data.Type == "Robot" then
			return
		end
		local root = NPC:FindFirstChild("HumanoidRootPart")
		local enemy = Target.Value
		local eroot = enemy:FindFirstChild("HumanoidRootPart")
		local pos = eroot.Position

		combatModule.Block(NPC)
		combatModule.RunStop(NPC)
		local amount = 10
		local timer = os.clock()
		while true do
			if ActionCheck2(Tags) then
				break
			end
			if not enemy.Parent or not Tags:FindFirstChild("Attacking") then
				break
			end
			if Target.Value ~= enemy then
				break
			end

			NPCModule:MoveTo(NPC, pos - CFrame.new(root.Position, pos).LookVector * amount, NPCData[NPC.Name])
			amount = amount + 1
			task.wait(2)
		end

		timer = os.clock()
		while  os.clock() - timer < 0.4 do
			if ActionCheck2(Tags) then
				break
			end

			task.wait(0)
		end

		combatModule.BlockStop(NPC)
		create("NoBlock", 1, Tags)
		create("NoSwing", 0.2, Tags)
		return
	end,

	Standby = function(NPC,Target,Tags, Data)
		Data.FirstChase = {}
		Data.FirstMag = {}
		local enemy = _G.TargetedBy[Target.Value]
		local root = NPC.HumanoidRootPart
		if enemy and Data.Type ~= "Robot" then
			local eroot = enemy:FindFirstChild("HumanoidRootPart")
			local pos = eroot.Position
			local neg = math.random(15,30)
			local pos2 = math.random(15,30)
			local distance = math.random(1, 2) == 2 and -neg or pos2

			local pos2 = pos - CFrame.new(root.Position, pos).RightVector * distance
			local sideHit = workspace:FindPartOnRayWithIgnoreList(Ray.new(pos2, CFrame.new(pos2).UpVector * -100), {workspace.World.Live, workspace.World.MouseFilter.Visuals})
			combatModule.RunStop(NPC)
			if (os.clock() - NPCData[NPC.Name]["LAST_STRAFE"] > 0.5) then
				NPCData[NPC.Name]["LAST_STRAFE"] = os.clock()
				NPCModule:MoveTo(NPC, pos2, NPCData[NPC.Name])
			end
		elseif enemy and Data.Type == "Robot" then

		end
		return
	end,

	Evade = function(NPC, Target, Tags, Data)
		Data.FirstChase = {}
		Data.FirstMag = {}
		local root = NPC.HumanoidRootPart
		local enemy = Target.Value
		local eroot = enemy:FindFirstChild("HumanoidRootPart")
		local pos = eroot.Position
		combatModule.RunStop(NPC)
		if math.random(1,3) == 1 then
			combatModule.Dodge(NPC)
		else
			NPCModule:MoveTo(NPCData[NPC.Name], pos - CFrame.new(root.Position, pos).LookVector * 50, NPCData[NPC.Name])
		end
		return
	end,


	Fight = function(NPC, Target, Tags, Data)
		Data.FirstChase = {}
		Data.FirstMag = {}
		local enemy = Target.Value
		local targetedBy = _G.TargetedBy[enemy]
		if targetedBy and targetedBy ~= NPC and not Data.EnemyTags:FindFirstChild("IsAMonster") then
			return
		end
		local root = NPC.HumanoidRootPart
		if Data.Type == "Robot" then return end
		local eroot = enemy:FindFirstChild("HumanoidRootPart")
		local distance = math.random(1, 2) == 2 and -5 or 5
		local pos = eroot.Position
		combatModule.RunStop(NPC)
		if Data.can_strafe and not ActionCheck(Data.Tags) then
			pos = eroot.Position
			local pos2 = pos - CFrame.new(root.Position, pos).RightVector * distance
			local sideHit = workspace:FindPartOnRayWithIgnoreList(Ray.new(pos2, CFrame.new(pos2).UpVector * -100), {workspace.World.Live, workspace.World.MouseFilter.Visuals})
		elseif os.clock() - Data.LastEvade > Data.ATTEMPTNUM then
			Data.LastEvade = os.clock()

			if math.random(1, 5) <= 3 then
				Data.ATTEMPTNUM = 1.5

				if distance > 0 then
					combatModule.DashNPC(NPC, "Right")
				else 
					combatModule.DashNPC(NPC, "Left")
				end
			else 
				Data.ATTEMPTNUM = 2
			end
		end

		if (root.Position - eroot.Position).magnitude > 8 or (Data.strafing and Tags:FindFirstChild("NoSwing")) then
			return
		end

		if (Data.UserAsign.Block.Active or math.random(1, 5) >= 3) and not Tags:FindFirstChild("NoHeavy") then
			combatModule.Heavy(NPC)
		else
			combatModule.Attack(NPC)
		end
		return
	end,

}

game:GetService("RunService").Heartbeat:Connect(function()
	for NPC, bool in pairs(TotalNPCS) do
		coroutine.wrap(function()
			if not NPC:FindFirstChild("Humanoid") then 
				NPCData[NPC.Name] = nil
				TotalNPCS[NPC] = false
				return
			end
			local humanoid = NPC:FindFirstChild("Humanoid")
			local Data = NPCData[NPC.Name]
			local root = NPC:FindFirstChild("HumanoidRootPart")
			if not Data or not root or not humanoid then
				NPCData[NPC.Name] = nil
				TotalNPCS[NPC] = false
				return
			end
			if Data.Tags:FindFirstChild("Gripping") or Data.Tags:FindFirstChild("BeingGripped") or Data.Tags:FindFirstChild("BeingCarried") or Data.Tags:FindFirstChild("DeadKnocked") then
				Data.last_target = os.clock()
			end

			if os.clock() - Data.last_target > 60 then
				NPC:Destroy()
				return
			end

			if humanoid.Health <= 1 or not NPC.Parent then
				for a, b in pairs(_G.TargetedBy) do 
					if b == NPC then
						_G.TargetedBy[a] = nil
					end
				end

				Data.Target.Value = nil
				return
			end

			if not Data.Tags:FindFirstChild("Carried") then
				setNetworkOwnerOfModel(NPC, nil)
				humanoid:SetStateEnabled(Enum.HumanoidStateType.FallingDown, false)
			end

			if Data.Tags:FindFirstChild("NoFunction") then
				return
			end

			if Data.Target.Value and (not Data.Target.Value:FindFirstChild("Humanoid") or Data.Target.Value.Humanoid.health <= 1) and not Data.Target.Value.Parent and not Data.EnemyTags:FindFirstChild("BeingGripped") and not Data.Target.Value:FindFirstChild("HumanoidRootPart") then
				Data.Target.Value = nil
			end
			Data.Target.Value = findTarget(NPC, root, Data.State)

			if Data.Target.Value then
				Data.EnemyAsign = GeneralAsign.GetAsign(Data.Target.Value)
				if Data.EnemyAsign == nil then
					Data.EnemyAsign = GeneralAsign.CreateAsign(Data.Target.Value.Name)
					Data.EnemyState = game.ReplicatedStorage.PlayerStates[Data.Target.Value.Name]
					Data.EnemyTags = Data.EnemyState.Tags
				end
				Data.EnemyState = game.ReplicatedStorage.PlayerStates[Data.Target.Value.Name]
				Data.EnemyTags = Data.EnemyState.Tags
				Data.last_target = os.clock()

				local enemy = Data.Target.Value		
				local ehum = enemy:FindFirstChild("Humanoid")
				local eroot = enemy:FindFirstChild("HumanoidRootPart")
				local Attacking = Data.EnemyTags:FindFirstChild("Attacking")
				if Data.Type == "Robot" then
					local magnitude = (root.Position - eroot.Position).magnitude
					if magnitude > 600 then
						Data.Target.Value = nil
						Data.State = "Wander"
						return
					end
					if magnitude > 30 then
						Data.State = "Chase"
					elseif magnitude < 30 then
						if not _G.TargetedBy[enemy] then
							_G.TargetedBy[enemy] = NPC
						end
						Data.State = "Fight"
					end
				else
					local magnitude = (root.Position - eroot.Position).magnitude
					if magnitude > 200 then
						Data.Target.Value = nil
						Data.State = "Wander"
						return
					end
					if magnitude > 5 then
						Data.State = "Chase"
					elseif magnitude < 5 then
						if not _G.TargetedBy[enemy] then
							_G.TargetedBy[enemy] = NPC
						end

						if Data.EnemyTags:FindFirstChild("Attacking") and math.random(1,2) == 1 then
							Data.State = "Block"
						else
							Data.State = "Fight"
						end

						if Data.Tags:FindFirstChild("Stunned") and Data.Type ~= "Robot" then
							Data.State = "Evade"
						end
					end
				end

				local TargetedBy = _G.TargetedBy[enemy]
				if TargetedBy and TargetedBy ~= NPC then
					if isAlly(NPC, TargetedBy) and ((TargetedBy.HumanoidRootPart.Position - eroot.Position).magnitude < 10 or TargetedBy:FindFirstChild("Attacking")) then
						if not enemy:FindFirstChild("IsAMonster") then
							Data.State = "Standby"
						end
					else 
						_G.TargetedBy[enemy] = NPC
						create("NoSwing", 0.5, Data.Tags)
					end
					local root = NPC.HumanoidRootPart				
					local Possible = findTarget(NPC, root, Data.State)
					if Possible ~= nil and Possible ~= enemy then
						Data.Target.Value = Possible
						return
					end				
				end
				if Data.EnemyTags:FindFirstChild("DeadKnocked") and Data.Type ~= "Robot" then
					if Data.Tags:FindFirstChild("Gripping") or Data.Tags:FindFirstChild("Arresting") then
						Data.Target.Value = nil
						Data.State = "Wander"
					else
						if not Data.EnemyTags:FindFirstChild("BeingGripped") and not Data.EnemyTags:FindFirstChild("BeingArrested") then
							

Data.State = "Execute"
						else 
							Data.Target.Value = nil
							Data.State = "Wander"
						end
					end
				end
			else
				Data.State = "Wander"
			end
			return Tab[Data.State](NPC,Data.Target,Data.Tags, Data)
		end)()
	end
end)

game.ServerStorage.Bindables.setNPC.Event:Connect(function(NPC)
	NPCModule:SetupNPC(NPC)
end)

Gameplay video

1 Like