Lag spikes when collecting food

I am making a game where you collect food to evolve into better creatures, but after a while, the game gets lags spikes when you collect food and i don’t know what to do

the script that i’m using for food collection:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")

local FoodFolder = ServerStorage:WaitForChild("Food")

local Modules = ReplicatedStorage:WaitForChild("Modules")
local FoodModule = require(Modules:WaitForChild("FoodModule"))

local connection

local function getCorners(part)
	local size = part.Size
	local cframe = part.CFrame

	local halfSize = size / 2

	local topRightOffset = Vector3.new(halfSize.X, 0, halfSize.Z)
	local topRight = cframe:PointToWorldSpace(topRightOffset)

	local bottomLeftOffset = Vector3.new(-halfSize.X, 0, -halfSize.Z)
	local bottomLeft = cframe:PointToWorldSpace(bottomLeftOffset)

	return topRight, bottomLeft
end

function spawnFood(Food, SpawnArea, ExpGiven, MaxFood, RespawnTime, RequiredLevel)
	for i = 1,MaxFood do
		wait()
		local ClonedFood = Food:Clone()
		ClonedFood.Parent = game.Workspace.Foods
		ClonedFood.Anchored = true
		ClonedFood.Transparency = 0

		if SpawnArea == "Grass" then
			local spawningPart = game.Workspace["Grass Area"]:WaitForChild("SpawningPart")
			local topRight, bottomLeft = getCorners(spawningPart)

			local randomX = math.random(math.floor(bottomLeft.X), math.ceil(topRight.X))
			local randomZ = math.random(math.floor(bottomLeft.Z), math.ceil(topRight.Z))

			ClonedFood.Position = Vector3.new(randomX, game.Workspace["Grass Area"]:WaitForChild("SpawningPart").Position.Y + 8, randomZ)
		end

		ClonedFood.Touched:Connect(function(Part)
			local Character = Part.Parent
			local Humanoid = Character:FindFirstChild("Humanoid")
			if Humanoid then
				local Player = Players:GetPlayerFromCharacter(Character)
				if Player then
					local leaderstats = Player:FindFirstChild("leaderstats")
					local Level = leaderstats:FindFirstChild("Level")
					local cooldown = Player:FindFirstChild("Cooldown")

					if Level and Level.Value >= RequiredLevel then
						if cooldown and not cooldown.Value then
							coroutine.wrap(function()
								cooldown.Value = true
								leaderstats.Exp.Value += ExpGiven
								ClonedFood.Audio:Play()
								ClonedFood.Transparency = 1
								ClonedFood.CanTouch = false
								ClonedFood.CanCollide = false
								task.wait(1.5)
								cooldown.Value = false
								task.wait(RespawnTime - 1.5)
								ClonedFood.Transparency = 0
								ClonedFood.CanTouch = true
								ClonedFood.CanCollide = true
							end)()
						end
					end
				end
			end
		end)
	end
end

for _, Food in ipairs(FoodFolder:GetChildren()) do
	local SpawnArea = FoodModule.GetSpawnArea(Food.Name)
	local ExpGiven = FoodModule.GetExpGiven(Food.Name)
	local MaxFood = FoodModule.GetMaxFood(Food.Name)
	local RespawnTime = FoodModule.GetRespawnTime(Food.Name)
	local RequiredLevel = FoodModule.GetLevelRequired(Food.Name)
	task.spawn(function()
		spawnFood(Food, SpawnArea, ExpGiven, MaxFood, RespawnTime, RequiredLevel)
	end)
end
1 Like

brother, in the spawnfood function you are copying a new model, but you are not destroying them, which means every time you call spawnfood you create a new model and after a while you’ll have thousands of them in workspace which lags the game.

to fix this, when a food is touched, just destroy it. There is no reason why it should be in workspace.
also, use collectionservice, and attributes, this way your spawnfood header would become only food & spawnarea and the rest can be attributes, also you may delete the boolean values.

example of collectionservice with your foods:

--in a random script in serverscriptservice.
--before using this script, add attributes requiredlevel & expgiven to every food.
--give every food part in your game the 'Food' tag.

--to add attributes, scroll down in the properties window on the part and click
-- 'add attribute' -> pick some type & name, and boom.
-- to add a tag, go to properties, all the way down, press + on tags and type 'Food'.
local collectionService = game:GetService("CollectionService")

local function addFood(food: Instance)
 if not food:IsA("BasePart") then return end
 food.Touched:Connect(function(hit)
      local char = hit:FindFirstAnscestoryOfClass("Model")
      if not char then return end

      local plr = game.Players:GetPlayerFromCharacter(char)
      local hum = char:FindFirstChildOfClass("Humanoid")
      if not hum or not plr then return end
       
      local lst = plr:FindFirstChild("leaderstats")
      if not lst then return end
 
      local level = lst:FindFirstChild("Level")
      if not level or level.Value < food:GetAttribute("RequiredLevel") then return end
      
      food.Audio:Play()
      food:Destroy()--destroy the food
      lst.Exp.Value += food:GetAttribute("ExpGiven")
 end)
end

CollectionService:GetInstanceAddedSignal("Food"):Connect(addFood)
for _, f in ipairs(CollectionService:GetTagged("Food")) do addFood(f) end

now, if you want to use your implementation, here is the fix:

--inside touch declaration of spawnfood
--previous code
if cooldown and not cooldown.Value then
	cooldown.Value = true
    leaderstats.Exp.Value += ExpGiven
	ClonedFood.Audio:Play()
    ClonedFood:Destroy()

	task.wait(1.5)
	cooldown.Value = false
end

Hope this helps!

So wait am i supposed to merge the 2 scripts or make 2 separate scripts

no, you don’t merge or separate any.
You pick one to use.

the first is a collection and attribute based thing that does the same as your other script.
the second is your implementation with a small change (using :destroy() in the touch handler)

you pick one.

The second implementation doesn’t work there is still lag spikes, while the first one doesn’t spawn in the food

i see that there is no spawning food in the first one maybe thats why

I was going to give you your code with scavenginbot’s fix, but I realize that you intend to reuse the food. I also noticed that you arent actively setting ‘connection’ to any actual events and disconnecting them, but I think the biggest issue is the lack of debounce specifically for the Touched event, not the player. Try this out

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")

local FoodFolder = ServerStorage:WaitForChild("Food")

local Modules = ReplicatedStorage:WaitForChild("Modules")
local FoodModule = require(Modules:WaitForChild("FoodModule"))

local connection

local function getCorners(part)
	local size = part.Size
	local cframe = part.CFrame

	local halfSize = size / 2

	local topRightOffset = Vector3.new(halfSize.X, 0, halfSize.Z)
	local topRight = cframe:PointToWorldSpace(topRightOffset)

	local bottomLeftOffset = Vector3.new(-halfSize.X, 0, -halfSize.Z)
	local bottomLeft = cframe:PointToWorldSpace(bottomLeftOffset)

	return topRight, bottomLeft
end

function spawnFood(Food, SpawnArea, ExpGiven, MaxFood, RespawnTime, RequiredLevel)
	for i = 1,MaxFood do
		wait()
		local ClonedFood = Food:Clone()
		ClonedFood.Parent = game.Workspace.Foods
		ClonedFood.Anchored = true
		ClonedFood.Transparency = 0

		if SpawnArea == "Grass" then
			local spawningPart = game.Workspace["Grass Area"]:WaitForChild("SpawningPart")
			local topRight, bottomLeft = getCorners(spawningPart)

			local randomX = math.random(math.floor(bottomLeft.X), math.ceil(topRight.X))
			local randomZ = math.random(math.floor(bottomLeft.Z), math.ceil(topRight.Z))

			ClonedFood.Position = Vector3.new(randomX, game.Workspace["Grass Area"]:WaitForChild("SpawningPart").Position.Y + 8, randomZ)
		end
		
		local canEat = true
		ClonedFood.Touched:Connect(function(Part)
			if not canEat then return end
			canEat = false
			
			local Character = Part.Parent
			local Humanoid = Character:FindFirstChild("Humanoid")
			if Humanoid then
				local Player = Players:GetPlayerFromCharacter(Character)
				if Player then
					local leaderstats = Player:FindFirstChild("leaderstats")
					local Level = leaderstats:FindFirstChild("Level")
					local cooldown = Player:FindFirstChild("Cooldown")

					if Level and Level.Value >= RequiredLevel then
						if cooldown and not cooldown.Value then
							coroutine.wrap(function()
								cooldown.Value = true
								leaderstats.Exp.Value += ExpGiven
								ClonedFood.Audio:Play()
								ClonedFood.Transparency = 1
								ClonedFood.CanTouch = false
								ClonedFood.CanCollide = false
								task.wait(1.5)
								cooldown.Value = false
								task.wait(RespawnTime - 1.5)
								ClonedFood.Transparency = 0
								ClonedFood.CanTouch = true
								ClonedFood.CanCollide = true
								canEat = true
							end)()
						end
					end
				end
			end
		end)
	end
end

for _, Food in ipairs(FoodFolder:GetChildren()) do
	local SpawnArea = FoodModule.GetSpawnArea(Food.Name)
	local ExpGiven = FoodModule.GetExpGiven(Food.Name)
	local MaxFood = FoodModule.GetMaxFood(Food.Name)
	local RespawnTime = FoodModule.GetRespawnTime(Food.Name)
	local RequiredLevel = FoodModule.GetLevelRequired(Food.Name)
	task.spawn(function()
		spawnFood(Food, SpawnArea, ExpGiven, MaxFood, RespawnTime, RequiredLevel)
	end)
end

hey so your code works but there are still lag spikes, i also changed canEat to false only after “if cooldown and not cooldown.Value then” since if it is being touched by like a tree lets say it will not work anymore