Script Optimize

I have been testing my game until I realised that something’s wrong with my main game script. It works perfectly, but each round game lag becomes bigger. I’ve tried removing music functions and it helped me but I still want different music to play each round / intermission / and the end of the round. So, I really need for music to stay, but I just don’t know how can I remake it, maybe there’s any way to make the music play as one sound without creating and deleting any new Sounds?

Here’s my full main script:

local ServerStorage = game:GetService("ServerStorage")
local BadgeService = game:GetService("BadgeService")
local SoundService = game:GetService("SoundService")
local NotificationModule = require(game.ReplicatedStorage.NotificationModule)
local Players = game:GetService("Players")

local maps = ServerStorage.Maps:GetChildren()
local roundMusicPlaying = false 
local roundSound = nil
local winSound = nil
local intermissionSound = nil

local colorValue = ReplicatedStorage.FolderValue.ColorValue
local textValue = ReplicatedStorage.FolderValue.Titlevalue
local invisibleValue = ReplicatedStorage.FolderValue.InvisibleValue
local InvisibleTitle = ReplicatedStorage.FolderValue.InvisibleTitle
local MuteVal = ReplicatedStorage.FolderValue.Mute

local availableColors = {}

local availableColors = {}
local groupId = 16090520

Players.PlayerAdded:Connect(function(player)
	local function grantWinnerRewards(winner)
		local statsFolder = winner:FindFirstChild("Stats")
		if statsFolder then
			local xpStat = statsFolder:FindFirstChild("Experience")
			local RoundsStat = statsFolder:FindFirstChild("Rounds")
			local winsStat = winner:FindFirstChild("leaderstats") and winner.leaderstats:FindFirstChild("Wins")
			local coinsStat = winner:FindFirstChild("leaderstats") and winner.leaderstats:FindFirstChild("Crystals")

			if xpStat and winsStat and coinsStat then
				winsStat.Value = winsStat.Value + 1
				local crystalReward = 200

				if player.MembershipType == Enum.MembershipType.Premium or player.Name == 'Tbl_KpblcaUnU' then
					crystalReward = crystalReward * 2
				end

				if player:IsInGroup(groupId) then
					crystalReward = crystalReward * 1.5
				end

				coinsStat.Value = coinsStat.Value + crystalReward
				xpStat.Value = xpStat.Value + 100
			end
		else
			local leaderstats = Instance.new("Folder")
			leaderstats.Name = "leaderstats"
			leaderstats.Parent = winner

			local winsStat = Instance.new("IntValue")
			winsStat.Name = "Wins"
			winsStat.Value = 1
			winsStat.Parent = leaderstats

			local lvlStat = Instance.new("IntValue")
			lvlStat.Name = "Level"
			lvlStat.Value = 0
			lvlStat.Parent = leaderstats

			local coinsStat = Instance.new("IntValue")
			coinsStat.Name = "Crystals"
			local initialCrystals = 200

			if player.MembershipType == Enum.MembershipType.Premium then
				initialCrystals = initialCrystals * 2
			end

			if player:IsInGroup(groupId) then
				initialCrystals = initialCrystals * 1.5
			end

			coinsStat.Value = initialCrystals
			coinsStat.Parent = leaderstats
		end
		NotificationModule.Notify(winner, "You won! 🎉")
		BadgeService:AwardBadge(winner.UserId, 1323868225377340)
	end

	local function incrementRoundsStat(player)
		local statsFolder = player:FindFirstChild("Stats")
		if statsFolder then
			local roundsStat = statsFolder:FindFirstChild("Rounds")
			if roundsStat then
				roundsStat.Value = roundsStat.Value + 1
			else
				roundsStat = Instance.new("IntValue")
				roundsStat.Name = "Rounds"
				roundsStat.Value = 1
				roundsStat.Parent = statsFolder
			end
		else
			statsFolder = Instance.new("Folder")
			statsFolder.Name = "Stats"
			statsFolder.Parent = player

			local roundsStat = Instance.new("IntValue")
			roundsStat.Name = "Rounds"
			roundsStat.Value = 1
			roundsStat.Parent = statsFolder
		end
	end
	local function fadeOutSound(sound)
		if sound then
			local fadeDuration = 1
			local fadeIncrement = sound.Volume / (fadeDuration * 60)

			for i = 1, fadeDuration * 60 do
				sound.Volume = sound.Volume - fadeIncrement
				wait(1 / 60)
			end

			sound:Destroy()
		end
	end
	-- Function to play music of a specific type (round, win, intermission)
	local function playMusic(soundType)
		-- Fade out any currently playing sound of other types
		if soundType == "round" then
			fadeOutSound(winSound)
			fadeOutSound(intermissionSound)
		elseif soundType == "win" then
			fadeOutSound(roundSound)
			fadeOutSound(intermissionSound)
		elseif soundType == "intermission" then
			fadeOutSound(roundSound)
			fadeOutSound(winSound)
		end

		-- Check if the sound for this type is already playing
		if soundType == "round" and not roundSound then
			local roundMusic = ServerStorage.RoundMusic:GetChildren()
			local chosenMusic = roundMusic[math.random(1, #roundMusic)]
			roundSound = Instance.new("Sound")
			roundSound.SoundId = chosenMusic.SoundId
			roundSound.Parent = ReplicatedStorage
			roundSound:Play()

		elseif soundType == "win" and not winSound then
			local winMusic = ServerStorage.WinMusic:GetChildren()
			local chosenMusic = winMusic[math.random(1, #winMusic)]
			winSound = Instance.new("Sound")
			winSound.SoundId = chosenMusic.SoundId
			winSound.Parent = ReplicatedStorage
			winSound:Play()

		elseif soundType == "intermission" and not intermissionSound then
			local intermissionMusic = ServerStorage.IntermissionMusic:GetChildren()
			local chosenMusic = intermissionMusic[math.random(1, #intermissionMusic)]
			intermissionSound = Instance.new("Sound")
			intermissionSound.SoundId = chosenMusic.SoundId
			intermissionSound.Parent = ReplicatedStorage
			intermissionSound:Play()
		end
	end


	local function updateTitleTextForPlayers()
		if game.Players.NumPlayers < 2 then
			textValue.Value = "~Waiting for one more player to start...~"
		else
			textValue.Value = "Loading next round..."
		end
	end

	local function enableDisableParticle(mapName)
		local particle = nil
		if mapName == "Staircase" then
			particle = game.Workspace.STAIRCASEPARTICLE.Sparkles1
		else
			particle = game.Workspace.ENDPARTICLE.Sparkles
		end

		if particle then
			particle.Enabled = true
			wait(2)
			particle.Enabled = false
		end
	end

	local function distributeColorsRandomly(map, colors)
		for _, part in ipairs(map:GetChildren()) do
			if part:IsA("BasePart") then
				local color = colors[math.random(1, #colors)]
				part.Color = color
			end
		end
	end

	local function distributeColorBricks(map, color)
		local colorBricks = {}
		for _, part in ipairs(map:GetChildren()) do
			if part:IsA("BasePart") and part.Color ~= color then
				table.insert(colorBricks, part)
			end
		end

		local colorBricksCount = #colorBricks
		local bricksToReplace = math.min(7, colorBricksCount)
		local chosenIndices = {}

		for _ = 1, bricksToReplace do
			local index = math.random(1, #colorBricks)
			table.insert(chosenIndices, index)
			colorBricks[index].Color = color
			table.remove(colorBricks, index)
		end
	end

	local function updateAvailableColors(map)
		availableColors = {}
		for _, part in ipairs(map:GetChildren()) do
			if part:IsA("BasePart") then
				availableColors[part.Color] = true
			end
		end
	end

	local function getRandomAvailableColor()
		local colorList = {}
		for color, _ in pairs(availableColors) do
			table.insert(colorList, color)
		end
		return colorList[math.random(1, #colorList)]
	end

	while true do
		updateTitleTextForPlayers()
		repeat wait(5) until game.Players.NumPlayers >= 1

		local winnertable = {}

		for i = 15, 1, -1 do
			playMusic("intermission")
			textValue.Value = "Intermission: " .. i
			wait(1)
		end

		local chosenMapIndex = math.random(1, #maps)
		local cloneMap = maps[chosenMapIndex]:Clone()
		cloneMap.Parent = game.Workspace.CurrentMap
		local mapName = maps[chosenMapIndex].Name

		local Colortable = {
			Color3.fromRGB(34, 34, 34),
			Color3.fromRGB(234, 234, 234),
			Color3.fromRGB(255, 0, 0),
			Color3.fromRGB(0, 80, 191),
			Color3.fromRGB(113, 255, 16),
			Color3.fromRGB(252, 205, 24),
			Color3.fromRGB(255, 0, 238),
			Color3.fromRGB(251, 128, 16),
			Color3.fromRGB(117, 25, 197),
			Color3.fromRGB(0, 103, 0)
		}

		distributeColorsRandomly(cloneMap.map, Colortable)
		updateAvailableColors(cloneMap.map)

		local mapColors = {}
		for color, _ in pairs(availableColors) do
			table.insert(mapColors, color)
		end

		if #mapColors == 0 then
			error("No colors found on the map.")
		end

		local chosenColor = mapColors[math.random(1, #mapColors)]
		distributeColorBricks(cloneMap.map, chosenColor)

		textValue.Value = "The next map is: " .. mapName .. "!"
		if intermissionSound then
			fadeOutSound(intermissionSound)
			intermissionSound = nil
		end
		local function checkForPlayersInRound()
			return #winnertable > 0
		end
		wait(2)
		for _, plr in pairs(game.Players:GetPlayers()) do
			if plr.Character then
				table.insert(winnertable , plr)
				plr.Character.HumanoidRootPart.CFrame = cloneMap.Posicion.CFrame
				local confirmWinner = Instance.new("BoolValue")
				confirmWinner.Name = "confirmWinner"
				confirmWinner.Parent = plr.Character

				-- Increment the "Rounds" stat for each player
				incrementRoundsStat(plr)
			end
		end

		for i = 3, 1, -1 do
			textValue.Value = "Round starts in " .. i .. " seconds!"
			wait(1)
		end

		playMusic("round")
		invisibleValue.Value = true
		InvisibleTitle.Value = false

		while true do
			InvisibleTitle.Value = false
			local choosencolor = getRandomAvailableColor()
			colorValue.Value = choosencolor

			for i = 3, 1, -1 do
				textValue.Value = i
				wait(1)
				
			end
			
			for _, part in pairs(cloneMap.map:GetChildren()) do
				if part.Color ~= choosencolor then
					part.Transparency = 1
					part.CanCollide = false            
				end         
			end    
			
			textValue.Value = "0"
			wait(2)

			for _, part in pairs(cloneMap.map:GetChildren()) do
				local color = Colortable[math.random(1, #Colortable)]
				part.Transparency = 0
				part.CanCollide = true
				part.Color = color    
			end
			wait(1)

			for i = #winnertable, 1, -1 do
				playMusic("win")
				if roundSound then
					fadeOutSound(roundSound)
					roundSound = nil
				end
				if winnertable[i].Character then
					if not winnertable[i].Character:FindFirstChild("confirmWinner") then
						table.remove(winnertable, i)
					end
				else
					table.remove(winnertable, i)
				end
			end

			InvisibleTitle.Value = false
			
			if #winnertable < 1 then
				invisibleValue.Value = false
				InvisibleTitle.Value = true
				textValue.Value = "No players left. Ending the round."
				wait(3)
				break
			elseif #winnertable == 1 then
				invisibleValue.Value = false
				InvisibleTitle.Value = true
				textValue.Value = winnertable[1].Name .. " Is the winner!"
				grantWinnerRewards(winnertable[1])
				wait(5)
				winnertable[1].Character.HumanoidRootPart.CFrame = game.Workspace.Spawns.SpawnLocation.CFrame
				break
			end
		end

		if winSound then
			fadeOutSound(winSound)
			winSound = nil
		end

		enableDisableParticle(mapName)
		cloneMap:Destroy()
		textValue.Value = "Loading next round..."
		wait(1)
	end
end)

And only music related stuff:

local roundSound = nil
local winSound = nil
local intermissionSound = nil

local function fadeOutSound(sound)
		if sound then
			local fadeDuration = 1
			local fadeIncrement = sound.Volume / (fadeDuration * 60)

			for i = 1, fadeDuration * 60 do
				sound.Volume = sound.Volume - fadeIncrement
				wait(1 / 60)
			end

			sound:Destroy()
		end
	end
	
-- Function to play music of a specific type (round, win, intermission)
	local function playMusic(soundType)
		-- Fade out any currently playing sound of other types
		if soundType == "round" then
			fadeOutSound(winSound)
			fadeOutSound(intermissionSound)
		elseif soundType == "win" then
			fadeOutSound(roundSound)
			fadeOutSound(intermissionSound)
		elseif soundType == "intermission" then
			fadeOutSound(roundSound)
			fadeOutSound(winSound)
		end

		-- Check if the sound for this type is already playing
		if soundType == "round" and not roundSound then
			local roundMusic = ServerStorage.RoundMusic:GetChildren()
			local chosenMusic = roundMusic[math.random(1, #roundMusic)]
			roundSound = Instance.new("Sound")
			roundSound.SoundId = chosenMusic.SoundId
			roundSound.Parent = ReplicatedStorage
			roundSound:Play()

		elseif soundType == "win" and not winSound then
			local winMusic = ServerStorage.WinMusic:GetChildren()
			local chosenMusic = winMusic[math.random(1, #winMusic)]
			winSound = Instance.new("Sound")
			winSound.SoundId = chosenMusic.SoundId
			winSound.Parent = ReplicatedStorage
			winSound:Play()

		elseif soundType == "intermission" and not intermissionSound then
			local intermissionMusic = ServerStorage.IntermissionMusic:GetChildren()
			local chosenMusic = intermissionMusic[math.random(1, #intermissionMusic)]
			intermissionSound = Instance.new("Sound")
			intermissionSound.SoundId = chosenMusic.SoundId
			intermissionSound.Parent = ReplicatedStorage
			intermissionSound:Play()
		end
	end

And how my music files look like:
ice_screenshot_20240614-213336

(Sorry for my bad english)

1 Like

i havent worked with sounds in a while so forgive me if i say anything wrong but i have a basic idea of what you could do;

  • get what sound is supposed to be playing
  • clone it, and run :Play() on it ()
  • whenever music changes delete the old clone and make a new one for the current sound
  • repeat

OR

  • same as before
  • same as before
  • instead of deleting it, just pause it or stop it until you need to listen to it again

as for doing it without cloning or creating new instances, id say just move the sounds to workspace and then play whichever ones need to be played when and pause/stop the ones that shouldnt be playing.

1 Like

I’ll try pausing method, thanks for your advises!

Didn’t really help, the lag is still the same :anguished:

I see one big problem with your main game script. I would move all of the functions outside of the Players.PlayerAdded function since, when a player joins the server, a new round-system while loop is created. This means if ten players join your game, there will be ten round systems running at once. However, if you do this, you will have to modify your code, as the player variable (from the PlayerAdded event) won’t be accessible outside of the Players.PlayerAdded function.

1 Like

You could also combine a few functions if you want to increase performance. For example, the distributeColorsRandomly and updateAvailableColors functions can be combined as shown below. Now, you loop through the map’s children once rather than twice.

local function DistributeAndUpdateColors(map, colors)
    availableColors = {}
    for _, object in map:GetChildren() do
        if object:IsA("BasePart") then
            local randomColor = colors[math.random(#colors)]
            object.Color = randomColor

            if not availableColors[randomColor] then
                availableColors[randomColor] = true
            end
        end
    end
end