As you can see in the footage, the enemies lag behind and teleport/go insanely fast to where they are supposed to be. How can I fix this, because when I have lots more enemies, they move across the corners and it looks super weird. I am using :MoveTo() with humanoids:
Edit: just noticed I recorded excess footage, just skip ahead a bit.
You may consider removing the Humanoids or replacing them with an Animator (see link above) and using CFrame:Lerp() to move the models as needed.
2.1 You could remove the full character Models for mobs and replace them with a SpecialMesh with the appearance of a character, when in reality it’s just a single BasePart. Unsure if using MeshParts for this would have the same reduction in computations.
2.2 You might also have some success using TweenService under the caveat that you only create a TweenInfo object for each unique tween and not each mob, and make sure to delete the actual Tween object after the mob despawns.
I already set the network ownership to server. How would I use CFrame.Lerp? I tried it but it didn’t work for me. I’ll try the other optimizations, but can I add animations to special meshes. @Niveum
--!native
local module = {}
local ServerStorage = game:GetService("ServerStorage")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local PhysicsService = game:GetService("PhysicsService")
module.spawn = function(name:string, waypointfolder:Folder, exit, spawns:CFrame, boss:BoolValue)
local model:Model = ServerStorage.Enemies:FindFirstChild(name):Clone()
local offset = math.random(-1,1)
model:PivotTo(spawns + Vector3.new(offset,0,0))
if boss then
ReplicatedStorage.AddHealthBar:FireAllClients(name,model.Health.Value,model.Health.Value)
end
model.Parent = Workspace:FindFirstChild(ReplicatedStorage.Map.Value).enemies
--local health = model.health
--health:GetPropertyChangedSignal("Value"):Connect(function()
-- if health.Value == 0 then
-- model:Destroy()
-- end
--end)
for _,part in pairs(model:GetDescendants()) do
if part:IsA("BasePart") then
part:SetNetworkOwner(nil)
end
end
for index = 1,#waypointfolder:GetChildren(),1 do
model.Humanoid:MoveTo(waypointfolder:FindFirstChild(tostring(index)).Position + Vector3.new(offset,0,0))
model.Humanoid.MoveToFinished:Wait()
end
model.Humanoid:MoveTo(exit.Position + Vector3.new(offset,0,0))
model.Humanoid.MoveToFinished:Wait()
ReplicatedStorage.BaseHealth.Value = math.clamp(ReplicatedStorage.BaseHealth.Value - model.Health.Value,0,ReplicatedStorage.MaxBaseHealth.Value)
model:Destroy()
end
return module
--!native
local module = {}
local ServerStorage = game:GetService("ServerStorage")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local PhysicsService = game:GetService("PhysicsService")
module.spawn = function(name:string, waypointfolder:Folder, exit, spawns:CFrame, boss:BoolValue)
local offset = math.random(-1,1)
local model: Model = ServerStorage.Enemies:FindFirstChild(name):Clone()
model:PivotTo(spawns + Vector3.new(offset,0,0))
if boss then
ReplicatedStorage.AddHealthBar:FireAllClients(name,model.Health.Value,model.Health.Value)
end
model.Parent = Workspace:FindFirstChild(ReplicatedStorage.Map.Value).enemies
--local health = model.health
--health:GetPropertyChangedSignal("Value"):Connect(function()
-- if health.Value == 0 then
-- model:Destroy()
-- end
--end)
model.PrimaryPart:SetNetworkOwner(nil)
local enemySpeed = model.Humanoid.WalkSpeed
local orientation, size = model:GetBoundingBox()
for index = 1, #waypointfolder:GetChildren() do
local node = waypointfolder[index]
local distance = (node.Position + Vector3.new(offset, size.Y / 2, 0) - model.PrimaryPart.Position).Magnitude
local movementTween = TweenService:Create(model.PrimaryPart, TweenInfo.new(distance / enemySpeed, Enum.EasingStyle.Linear), {
CFrame = CFrame.lookAlong(node.Position + Vector3.new(offset, size.Y / 2, 0), model.PrimaryPart.CFrame.LookVector)
})
movementTween:Play()
movementTween.Completed:Wait()
local rotationTween = TweenService:Create(model.PrimaryPart, TweenInfo.new(0.5, Enum.EasingStyle.Linear), {
CFrame = CFrame.lookAlong(model.PrimaryPart.Position, node.CFrame.LookVector)
})
rotationTween:Play()
rotationTween.Completed:Wait()
end
model.Humanoid:MoveTo(exit.Position + Vector3.new(offset,0,0))
model.Humanoid.MoveToFinished:Wait()
ReplicatedStorage.BaseHealth.Value = math.clamp(ReplicatedStorage.BaseHealth.Value - model.Health.Value,0,ReplicatedStorage.MaxBaseHealth.Value)
model:Destroy()
end
return module
By the way, to set the NetworkOwner of a model, you can just set the PrimaryPart’s NetworkOwner. This script also assumes you set up your orientation correctly, all of the nodes should be facing the direction that the enemy will face when they are on it.
Example:
To see the orientation, right click the part and click Show Orientation Indicator, then rotate your part.
You’re accessing ServerStorage so this is a server sided script. Some issues here:
It’s costly to run Tween’s on the server. Tends to be a common culprit of lag.
A possible alternative would be to use for loops combined with CFrame:Lerp() to achieve a similar effect of interpolating between two CFrames as I mentioned in my last post. Lerp works to interpolate a specified distance in a straight line between two objects.
You call Lerp() on your starting CFrame, its first Argument is your goal CFrame, and its second argument is what fraction to interpolate from 0 - 1; where 0 is 0%, which is the same as your starting CFrame. A value of 1 is 100%, equivalent to your goal CFrame (argument 1). 0.123 would put you 12.3% of the way to the goal CFrame. In the code below I put it in a for loop that runs from 1 - 10, and call Lerp using 1/10th of the ’ i ’ value equivalent to calling 0.1 - 1.0.
local c0 = CFrame.new(0,0,0)
local c1 = CFrame.new(0,10,0)
local iterator = 1 -- You can use this to specify a frame rate (e.g. 1/30), although consider performance costs
for i = 1, 10, iterator do task.wait()
local i_CFrame = c0:Lerp(c1,i/10)
print (i_CFrame.Y) -- Will print 1 - 10
end
You’re creating an entire set of new TweenInfo’s and Tweens each time you spawn a mob. Instead, you could make one TweenInfo for each type of mob and use that to create your Tweens. Additionally, once a tween finishes playing, you should destroying them.
Alternatively- you could consider a system whereupon a mob dies, it is despawned rather than destroyed, and when the game looks to spawn a new mob it first checks the ‘despawned mob pool’ to see if there are any available mobs that can be respawned. This allows you to reuse both the model and its tweens rather than always creating a new one.
Otherwise, consider:
Running the mobs and/or other intensive calculations on the Client rather than the sever.
Remove humanoids altogether and replace them with AnimationControllers.
Reducing the number of mobs you spawn. One example of this would be increasing the types of mobs such that instead of of spawning ten Lvl 1 mobs you spawn two Lvl 5’s or one Lvl 10. You could make this a performance setting available for players to set. Or you can automatically set it based on a Clients frame rate / ping / etc if you wanna be fancy.