Can someone tell me whether my code is leaking or not?

Normally i’d post something like this in code review, but there is so much of it i doubt anyone would bother

so i wanted to ask some experienced game developers if my code is leaking based off the developer console in this video

i don’t really have knowledge on memory leaks and can’t tell if that memory usage is good or not


TL;DW

  • at the start, the game starts on wave 1 and has a memory usage of ~1100MB
  • at the end of the video, the game is on wave 6 (which is not the max) and has a memory usage of ~1300MB

idk if those are good stats, so i’d appreciate an answer

3 Likes

First of all, show us your code, from video and console we can’t see any memory leaks!

Also remember that game can use more memory with time, always remember to clear instances from tables manually when you remove them and call :Destroy() on instances

2 Likes

eep sorry

i should probably move this to code review but i might aswell just reply
i’ll just post the scripts that are responsible for the core game:

the script that spawns enemies in the blue box (which is seen for the entirety of that video)

local enemyStats = require(game:GetService("ServerScriptService"):WaitForChild("Enemies").EnemyStats)

local difficulty = game:GetService("ServerScriptService"):WaitForChild("Phases"):WaitForChild("Waves").Difficulty
local waveNumber = game:GetService("ServerScriptService"):WaitForChild("Phases"):WaitForChild("Waves").Wave
local spawnCap = game:GetService("ServerScriptService"):WaitForChild("Phases"):WaitForChild("Waves").SpawnCap
local spawnParts = workspace:WaitForChild("SpawnParts")

local SS = game:GetService("ServerStorage")

local meleeEnemies = {
	[SS.Enemies.WaveEnemies.Shambler] = 100
}

local rangedEnemies = { -- UNUSED FOR NOW

}

function RNG(enemy)
	local totalChance = 0

	for _,chance in enemy do
		totalChance += chance
	end

	local rng = math.random(1, totalChance)

	for option,chance in enemy do
		rng -= chance
		if rng <= 0 then return option end
	end
end

local wave = {}

-- // SPAWNING ENEMIES FUNCTION

local totalEnemies = 0
 
function wave.SpawnEnemies()
	totalEnemies = 0
	workspace.Enemies:ClearAllChildren()

	if difficulty.Value >= 5 and waveNumber.Value >= 5 then
		-- // SPAWN RANGED AND MELEE ENEMIES 
	end

	for _,part in spawnParts:GetChildren() do
		if part.Name == "WaveSpawn" then
			while totalEnemies < spawnCap.Value do
				local size = part.Size
				local position = part.Position
				local halfSize = size * 0.5

				local randomX = math.random(-halfSize.X, halfSize.X)
				local randomZ = math.random(-halfSize.Z, halfSize.Z)

				local randomPosition = position + Vector3.new(randomX, 0, randomZ)

				local enemy = RNG(meleeEnemies)
				local enemyClone = enemy:Clone()
				enemyClone.Parent = workspace.Enemies
				enemyClone.HumanoidRootPart.CFrame = CFrame.new(randomPosition)
				enemyClone.DetectionPart.Size = Vector3.new(1000,250,1000)
				
				local isWaveEnemy = Instance.new("BoolValue")
				isWaveEnemy.Name = "IsWaveEnemy"
				isWaveEnemy.Value = true
				isWaveEnemy.Parent = enemyClone

				totalEnemies += 1
				print("Spawned enemy, total enemies: " .. totalEnemies)
				task.wait()
			end

			return totalEnemies
		end
	end
end

-- // REDUCING ENEMY COUNT FUNCTION


function wave.ReduceEnemyCount(storm, stormStart, stormMoving, scavenging)
	local enemyCount = workspace.Enemies

	if #enemyCount:GetChildren() > 0 then
		totalEnemies = math.max(0, totalEnemies - 1)
		print("Enemies remaining: " .. totalEnemies)		
	else
		print("The wave is ending...")

		if waveNumber.Value <= 3 then
			spawnCap.Value += 3
		else
			if difficulty.Value > 12 then -- this is a total cluster of code and i wish i could've done this differently
				spawnCap.Value *= 2
			else
				spawnCap.Value += 5
			end
		end

		task.wait(3)

		waveNumber.Value += 1
		difficulty.Value += math.random(1,3)

		print("Current wave: " .. waveNumber.Value)
		print("Current difficulty: " .. difficulty.Value)
		print("Current spawncap: " .. spawnCap.Value)
		storm.CFrame = stormStart.CFrame

		stormMoving.Value = false
		scavenging.Value = true
	end
end

-- // DESTROY LOOTABLES FUNCTION
function wave.DestroyLootables()
	for _,lootable in workspace.SpawnParts:GetChildren() do
		if lootable.Name == "Locker" or lootable.Name == "Crate" then
			lootable:Destroy()
		end
	end
end

return wave

the script that handles spawning lockers and stuff (scavenging)

local SS = game:GetService("ServerStorage")
local openables = SS:WaitForChild("Openables")
local spawnParts = workspace:WaitForChild("SpawnParts")
local enemies = SS:WaitForChild("Enemies"):WaitForChild("ScavengingEnemies")


local scavenging = {}

-- // OPENABLE CHANCES

local lockerChances = {
	[openables.Locker] = 77,
	[openables.Nothing] = 23
} 

local crateChances = { 
	[openables.Crate] = 50,
	[openables.Nothing] = 50
}

-- // AMMO CHANCES

local ammoChances = { -- this is unused for now, ignore
	Light = 20,
	Medium = 20,
	Heavy = 20,
	Sniper = 20,
	Energy = 20
}

-- // ENEMY CHANCES

local enemyChances = {
	[enemies.Shambler] = 100
}

-- // RNG FUNCTION

function RNG(loot)
	local totalChance = 0

	for _,chance in loot do
		totalChance += chance
	end

	local rng = math.random(1, totalChance)

	for option,chance in loot do
		rng -= chance
		if rng <= 0 then return option end
	end
end


-- // FUNCTION RESPONSIBLE FOR SPAWNING OPENABLES

function scavenging.Lootables()
	for _,part in spawnParts:GetChildren() do
		if RNG(lockerChances) == openables.Locker then
			if part.Name == "LockerSpawn" then
				local locker = openables.Locker:Clone()
				locker.Parent = spawnParts
				locker:SetPrimaryPartCFrame(part.CFrame)
			end
		end

		if RNG(crateChances) == openables.Crate then
			if part.Name == "CrateSpawn" then
				local crate = openables.Crate:Clone()
				crate.Parent = spawnParts
				crate:SetPrimaryPartCFrame(part.CFrame)
			end
		end
	end
end

-- // SPAWNING ENEMIES
function scavenging.SpawnEnemies()
	for _,part in spawnParts:GetChildren() do
		if part.Name == "EnemySpawn" then
			local maxSpawns = part.MaxSpawns
			local randomSpawns = math.random(1,6)
			
			while maxSpawns.Value < randomSpawns do			
				local size = part.Size
				local position = part.Position
				local halfSize = size * 0.5

				local randomX = math.random(-halfSize.X, halfSize.X)
				local randomZ = math.random(-halfSize.Z, halfSize.Z)

				local randomPosition = position + Vector3.new(randomX, 0, randomZ)
				
				local enemy = RNG(enemyChances):Clone()
				enemy.Parent = workspace.Enemies
				enemy.HumanoidRootPart.CFrame = CFrame.new(randomPosition)
				maxSpawns.Value += 1
			end
		end
	end
end

return scavenging

all of these are module scripts which are then called in .Changed events

2 Likes

Hmm, from what i can see you don’t use any tables and OOP methoods, only thing i can add is to remember to call :Destroy() when you want to remove object from the game, do this for all stuff related to it or it will remain in memory

Also, it’s more of a good practice, if you have methood then use semicolon in place of dot, then you can also call self if you use OOP, but still it’s visually better, dot is especially used for object creation like .new function ect.

anyways don’t worry about memory leaks in wave defense game, you are spawning more and more enemies, soo it’s normal that with time they’ll lag a little bit more

have a nice day, i believe i helped you, good luck!

2 Likes

yeah i don’t really have all that knowledge in OOP yet, but i did start using module scripts if that counts

still made a lot of progress throughout the last 12 months and i think i’m proud of it

thanks

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.