I have a script that randomly chooses a map, clones it from ServerStorage, and then moves it to workspace, however the issue is that there is extreme amounts of lag when it’s moved to there. How do I resolve this?
Here’s the part of the script that does this:
gameStarted = true
warn("chooseMap")
local chosenMap = maps[math.random(1, #maps)]
local mapPlayed = table.find(playedMaps, chosenMap)
if mapPlayed == nil then
print("nil")
table.insert(playedMaps, #playedMaps + 1, chosenMap)
local map = ServerStorage.Maps:FindFirstChild(chosenMap):Clone()
map.Parent = workspace.Maps
loadPrefs(map)
else
chooseMap() --re-chooses map if it's already been played
end
I would double check and make sure that every single part in the map is anchored (assuming they’re all static parts). Unanchored parts of this amount can take a long time to calculate physics for. Run this code with the map’s parent model selected and make sure that the number it gives back is 0.
local map = game.Selection:Get()[1]
local c = 0
for i, v in pairs(map:GetDescendants()) do
if v:IsA("BasePart") and not v.Anchored then
v.Anchored = true
c+=1
end
end
print("anchored " .. c .. " parts")
One solution is to move the map a few parts at a time; I do in that with my game.
EDIT: and another solution is to spawn the map really far away (like 0, 500000, 0) then move it to where you want it, based on this optimization hack: PartCache, for all your quick part-creation needs
(I do something like this in my game too, where I move the lobby back and forth using :SetPrimaryPartCFrame from its original location; as long as you don’t move it too far, it will stay in tact. But if there’s any moving parts in your map, this solution won’t work for you)
EDIT2: actually I’m not sure about if my first edit’s solution works on the server, but it would be a nice experiment
What exactly do you mean by “lag”? And on what hardware? Spawning thousands of parts is going to take time, you can’t make it instantaneous. Memory needs to be allocated, and a lot of things are being put into the collision octree. Disposing of thousands of parts isn’t instantaneous either. But the impact on players should be minimal.Some framerate drop on low-end machines as things pop into existence, sure, but again, that’s also to be expected.
Part of the reason my module works is because the parts have already been created. PartCache will lag if you create a new cache with 1K+ parts just as your script here does.
For optimization you could see about splitting the map into chunks.
Consider this:
local originalMap = ServerStorage.Maps:FindFirstChild(chosenMap)
local map = Instance.new("Model")
map.Name = originalMap.Name
map.Parent = workspace.Maps
-- And in some loader code:
local mapChildren = originalMap:GetChildren()
for index = 1, #mapChildren do
mapChildren[index]:Clone().Parent = map
if index % 150 == 0 then wait() end -- Every 150 parts, give this loop some breathing room.
end
This is what @cjjdawg meant in Reply #1. The map may be one model, but you need to clone it by the model’s children in sections.
By lag I mean FPS, going between 1 and 0 for ~20 seconds. I’m on Macbook Air 2017 but it’s pretty new (Christmas of 2019), it is extremely laggy for my friends, who also have average devices, so if a device most players have can’t handle it, I should look for some form of optimization.
The maps should be in ReplicatedStorage as they can be downloaded by the client during run-time.
It would be the best way to store it, then parent it to workspace when needed.
This is debatable because parenting them to replicated storage will increase the client’s memory, since all the maps will be replicated to the client at once. Imo, It would be better to store them in server storage and only load them when needed.
Yeah, ReplicatedStorage is not where you want to put map models in a game that loads one map per round. Not only will it immediately start clients replicating all the map parts, they won’t be in any particular order, so it could delay the player getting into the game if the next map ( just-about-to-be-spawned map) happens to replicate last (it’s not within developer control).
That said, the OP’s situation of framerate dropping to ~1fps for 20 seconds is unexpected. I’m now wondering what the specs are for this “>1000 parts” map. How many parts exactly, and how many total instances (of any type) in the map Model? 20 seconds is crazy long for a map with around 1000 parts. I have a round-based game that loads map models, and the maps seem to take about 1 second per 1000 parts, on average, to spawn.
The only time I’ve seen FPS drops and delays like 7z99 describes is when there is something unanchored in the Workspace. It’s not enough to check that all the parts of your map model are anchored in Studio at authoring time, you need to run the check at actual runtime, to make sure nothing in the workspace is unanchored. Having either lots of small unanchored parts, or 1 or more very large unanchored parts, will absolutely destroy your framerate when loading a model into the workspace. It doesn’t matter if it’s a part in the model, or something already in the workspace that overlaps the model’s spawn area. Double check that you don’t have any scripts that unanchor things at runtime too.
Sorry for the super late reply. Your solution did technically work, however the FPS is still an unbearably low ~10fps. I ended up putting a wait() for every 50 parts instead, and it’s still quite laggy in playtesting. While in the actual game, my FPS goes to 0 for a long time again. I will try again and ensure the game is updated (it should have been), and see if that improves anything.
Also the maps are quite diverse in terms of the amount of descendants, the highest being just below 4,000, and the lowest actually being just over 450, and they both make a significant amount of FPS issues.
This is my script now, after accomodating to my maps:
local mainModel = Instance.new("Model", workspace.Maps)
mainModel.Name = map.Name
local physicalMapModel = Instance.new("Model", mainModel)
physicalMapModel.Name = "PhysicalMap"
local progressModel = Instance.new("Model", mainModel)
progressModel.Name = "Progress"
local settingFolder = map:FindFirstChild("Settings")
settingFolder.Parent = mainModel
local physicalMap = map:WaitForChild("PhysicalMap"):GetDescendants()
local partsToTransfer = {}
for i,v in pairs(physicalMap) do
if v:IsA("BasePart") then
table.insert(partsToTransfer, #partsToTransfer + 1, v)
end
end
for i,v in pairs(partsToTransfer) do
v.Parent = physicalMapModel
if i % 150 == 0 then
wait(.1)
end
end
local progress = map:WaitForChild("Progress"):GetChildren()
for index = 1, #progress do
progress[index].Parent = progressModel
if index % 150 == 0 then wait(.1) end
end
for i,v in pairs(map:GetChildren()) do
if v:IsA("BasePart") or v:IsA("SpawnLocation") then
v.Parent = mainModel
end
end
Edit: Yeah, it’s updated, I still get horrible FPS issues.
You might want to read the section above as I mention the amount of parts.
There are zero unanchored parts in the map, ran at runtime, while a map was loaded.
I did:
local unanchored = 0
for i,v in pairs(workspace(Maps:GetDescendants()) do
if v:IsA("BasePart") then
if v.Anchored == false then
unanchored = unanchored + 1
end
end
end
print(unanchored)
Without knowing more about the specifics of the maps your loading, it’s hard to say. It could just be normal for your devices? Though new, a Macbook air is still a netbook class of computer in terms of processing power, so things generally aren’t going to be as snappy as on a new-ish iPad or GPU-equipped PC.
One thing you can do, as a sort of sanity check to rule out there being something specific with how your maps are build, is to make a test map that’s just something like 4,000 anchored 4x4x4 blocks, nothing fancy. If that loads about as fast as one of the real maps that has 4,000-ish parts, then you know it’s just your computer’s normal speed. If it’s way faster, then you know you have more investigation to do.
Sorry, late reply. I didn’t see the notification.
It seems that is the issue as the lag still happens when simply using 4,000 4x4x4 blocks. I still need a way to get around it though, as this much lag can really appaul players. It’s just as bad on my phone, which is brand new. (XS Max)
I’m going to try loading it 1,000,000 studs away instead of the 50,000 I tried and see if that works without destroying the map.
Edit: nope. I do have a loading screen at the beginning though, so do you think I could replicate the maps into ReplicatedStorage while the loading screen is on-screen? It would cause lag, but it’s not like the player would be missing anything as they aren’t in-game.