Optimizing my Mob system

I’ve never really ever looked into optimization or never really knew good tactics to do them in my code, but I think as my game nears release I should start putting more attention and thought towards optimization.

In this case, I have a mob system that handles certain types of mobs. Mobs in the game follow the player and calculate a path using PathFindingService to find the player. While the mob system itself works pretty well, when multiple mobs (~5 - 10) in one area spawn and chase the player the game gets very laggy. When mobs spawn in the game also has a slight lag spike.

I’m not really sure how to start optimizing my code (mainly due to lack of knowledge) but here is a function to handle one of my mobs. Some tips and tricks to help optimize not only this script but any other script would be much appreciated.

As for the actual model of the Mobs, they just use 10 - 15 default Roblox’s parts.

Thanks

-- Function to handle skeleton behavior
local function handleSkeleton(character, player)
	setNetworkOwnerOfModel(character, nil)

	local humanoid = character.Humanoid
	local animations = character.Animations
	local LaunchDistance = character:GetAttribute("LaunchDistanceY")
	local horizontalLaunchDistance = character:GetAttribute("HorizontalLaunchDistance")
	local FallPoint = 0.3

	local attacking = false
	local walking = false

	local Range = character:GetAttribute("Range")
	local Hitbox = character:GetAttribute("Hitbox")
	local Cooldown = character:GetAttribute("Cooldown")

	local idleAnimation = humanoid:LoadAnimation(animations.Idle)
	local walkAnimation = humanoid:LoadAnimation(animations.Walk)
	local attackAnimation = humanoid:LoadAnimation(animations.Attack)

	idleAnimation.Priority = Enum.AnimationPriority.Idle
	walkAnimation.Priority = Enum.AnimationPriority.Movement
	attackAnimation.Priority = Enum.AnimationPriority.Action4

	local lastAttack = 0
	
	--Loop attacks
	while wait() do
		if character.PrimaryPart.Position.Y < -200 then
			character:SetPrimaryPartCFrame( workspace.MobSpawns[character.Name]:GetChildren()[math.random(1, #workspace.MobSpawns[character.Name]:GetChildren())].CFrame)	
		end

		if character:GetAttribute("Health") > 0 then
			if player then
				character.Head.Stars.Enabled = false

				path:ComputeAsync(character.HumanoidRootPart.Position, player.Character.HumanoidRootPart.Position)
				local waypoints = path:GetWaypoints()

				for _, waypoint in pairs(waypoints) do
					humanoid:MoveTo(waypoint.Position)
					idleAnimation:Stop()

					if not walkAnimation.IsPlaying then
						walkAnimation:Play()
						character.Torso.WalkingSound:Play()
					end

					if waypoint.Action == Enum.PathWaypointAction.Jump then
						humanoid.Jump = true
					end

					local distance = (player.Character.HumanoidRootPart.Position - character.HumanoidRootPart.Position).Magnitude

					if distance <= Hitbox and os.time() - lastAttack >= Cooldown then
						walkAnimation:Stop()
						attackAnimation:Play()
						character.Torso.Swing:Play()
						character.Torso.Hit:Play()

						player.Character.Humanoid.Sit = true

						player.Character.Humanoid:UnequipTools()

						spawn(function()
							local bodyVelocity = Instance.new("BodyVelocity", player.Character.PrimaryPart)
							bodyVelocity.Velocity = Vector3.new(math.random(-horizontalLaunchDistance, horizontalLaunchDistance), LaunchDistance, math.random(-horizontalLaunchDistance, horizontalLaunchDistance))
							bodyVelocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)

							wait(FallPoint)
							bodyVelocity:Destroy()
						end)
						lastAttack = os.time()
					end
				end
			else
				character.Head.Stars.Enabled = false

				walkAnimation:Stop()
				character.Torso.WalkingSound:Stop()

				if not idleAnimation.IsPlaying then
					idleAnimation:Play()
				end

			end

			-- Check if the skeleton has fallen over
			if humanoid:GetState() == Enum.HumanoidStateType.PlatformStanding or humanoid:GetState() == Enum.HumanoidStateType.FallingDown or  humanoid.Sit then
				humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
			end
		else
			character.Torso.WalkingSound:Stop()

			character.Head.Stars.Enabled = true

			if not idleAnimation.IsPlaying then
				idleAnimation:Play()
				character.Torso.Stuned:Play()
			end

			walkAnimation:Stop()
			player = nil
		end
	end
end

Well, first of all humanoids are very laggy so I’d suggest looking at this:
https://devforum.roblox.com/t/how-to-optimize-humanoids/389801

Another big issue I see is constantly re-calculating a path. You should instead opt for raycasting and checking if there is even anything blocking the way between the enemy and target, if there is nothing blocking the enemy should instead just walk straight forward to the target without a path made.

1 Like

You should limit how often it does the path recalculation, because it really does not need to be running as often as it is with this loop. I would recommend putting a 0.5 so that the path is only recalculated twice every second, because, realistically, not that much can change in half a second.

You could also replace the wait with task.wait and move it to the bottom of the loop to remove any initial delay once the skeleton spawns it, replacing it with a true so that it says while true do.

You should avoid long lines like these, and also use :PivotTo instead of :SetPrimaryPartCFrame because SetPrimaryPartCFrame is deprecated and has been “superseded” by PivotTo, which is also faster:

if character.PrimaryPart.Position.Y < -200 then
	local Spawns = workspace.MobSpawns[character.name]:GetChildren()
	local SelectSpawn = Spawns[math.random(1, #Spawns)]
	character:PivotTo(SelectedSpawn.CFrame)	
end

Breaking up the lines makes it easier to read, and there is less repetition by storing the result of :GetChildren() in a variable.

1 Like

Thank you! Using a raycast and only calculating a path when needed really helped.

Thanks! This helped increase performance by a bit too.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.