Extreme amounts of lag when maps (>1000 parts) are parented to workspace

Hello!

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

Thanks in advance!

1 Like

Instead of directly parenting the map, which will cause a lag spike, load in sections of the map at a time.

2 Likes

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")
1 Like

Yup, it’s 0.
(30charsssssssss)

The issue is that the map is one model.

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

2 Likes

I will test this out and see if it works (:crossed_fingers:) I will update the post here. Thanks!

Edit: Still super laggy.

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.

2 Likes

(cc @B_rcode on this too)

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.

3 Likes

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.

I will try this after. Thank you!

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.

1 Like

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.

1 Like

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.

2 Likes

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)

0 is printed.

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.

1 Like

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.

Edit: I still can’t get anything to work.

what if u dont clone it? im sure theres another way… i just dont know what

1 Like

Same issue. I think parenting it to workspace is the issue.

Hoping a resolution can be found soon as the maps are literally the main part of the game.