Is there a more efficient way to random model generation?

Hi, everyone. So, I’m gonna get to the point.

I’m making a sports game, and I need to generate rigs. I’ve been making a “rig table”, as so:

1/2


2/2
image

After generating these tables, I have a function which generates the rigs by looping through the tables:
image

Is there any more efficient way to do this? Here is my code block to generate the player table:

function generateTeam()
	local team = {}
	local teamToIterate = {}
	local availablePositions = {"QB", "RB", "WR", "SS", "TE", "OL", "DL", "CB", "FS", "MLB", "ROLB", "LOLB"}
	local available = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}
	
	for i = 0, 25, 1 do
		local basePlrModels = baseModels:GetChildren()
		local hairBases = baseHairs:GetChildren()
		
		local baseNum = utilities.random(1, #basePlrModels)
		local SkinBaseModel = baseModels:FindFirstChild("base_"..baseNum)
	
		if not SkinBaseModel then return end
	
		local hairBaseNum = math.random(1, #hairBases)
		local charHair = baseHairs:FindFirstChild("hair_"..hairBaseNum)
		
		if not charHair then return end
		
		local player = utilities.createTable()
		player.cleetColor = CleetColors[utilities.random(1, #CleetColors)]
		player.baseModel = SkinBaseModel
		player.hair = charHair
		
		local randomPosNum = utilities.random(1, #availablePositions)
		player.position = availablePositions[randomPosNum]
		available[randomPosNum] -= 1
		
		local first_name_Num = utilities.random(1, #firstNames)
		local firstName = firstNames[first_name_Num]
		
		local lastName_Num = utilities.random(1, #lastNames)
		local lastName = lastNames[lastName_Num]
		
		player.name = firstName.." "..lastName
		
		if available[randomPosNum] == 0 then
			table.remove(availablePositions, randomPosNum)
		end
		
		player.overalls = {}
		local len
		
		for i, v in pairs(statsNeeded[player.position]) do
			if v == "Injury" then
				player.overalls[v] = utilities.random(85, 99)
			else
				local min
				local max
				if minStat[player.position] and minStat[player.position][v] then min = minStat[player.position][v] else min = 65 end
				if maxStat[player.position] and maxStat[player.position][v] then max = maxStat[player.position][v] else max = 99 end
				
				player.overalls[v] = utilities.random(min, max)
			end
			len = i
		end
		
		player.totalOverall = math.round(utilities.calculateTable(player.overalls)/len)
		player.importance = importance[player.position]
		
		player.morale = utilities.random(50, 100)
		
		player.contract = {}
		player.contract.length = utilities.random(1, 5)
		player.contract.salary = utilities.random(10000000 + (10000000 * player.totalOverall/100) + (10000000 * player.importance/100) - 1000000 * player.morale/10 , 30000000 + (10000000 * player.totalOverall/100) + (10000000 * player.importance/100) - 1000000 * player.morale/10)
		player.contract.total = player.contract.length * player.contract.salary
		
		table.insert(teamToIterate, player)
	end
	
	team.offense = {}
	team.defense = {}
	
	for i, v in pairs(teamToIterate) do
		if table.find(offensive, v.position) then
			table.insert(team.offense, v)
		elseif table.find(defensive, v.position) then
			table.insert(team.defense, v)
		end
	end
	
	warn("A team has just been generated!")
	return team
end

Here are the folders for bases, hairs, and such, they are structured as followed
image

Anything is appreciated, thank you!

1 Like

Bumping post, still looking for an answer, sorry if I sound impatient.

It’s good that you want to optimize :+1: Luau is powerful, so unless you really overdo bad practices (which you haven’t done) you won’t notice a difference most of the time, except maybe with functions bound to Heartbeat and similar.

I can’t fully help if I don’t fully know what your needs are and what your resources are exactly, but I tried to understand as much as I can. But I also assumed a lot, e.g. that the Hair Folder contains Accessories, that Cleets are an Accessory, and so on.

local Models: Folder = game:GetService('ServerStorage').Models

local characterModels: {Model} = Models.Characters:GetChildren()
local hairAccessories: {Accessory} = Models.Hair:GetChildren()

local cleetColors = {Color3.new(0, 0, 0), Color3.new(1, 0, 0), Color3.new(1, 1, 1)}

local firstNames = {'John', 'Bob', 'Michael'}
local lastNames = {'McDonald', 'Poe', 'Flintstone'}

local minStatistics = { injury = 85, speed = 65, whatever = 50 }
local maxStatistics = { injury = 99, speed = 99, whatever = 80 }

local offensivePositions = { QB = 1, RB = 2, WR = 3, SS = 4, TE = 5, OL = 6 }
local defensivePositions = { DL = 7, CB = 8, FS = 9, MLB = 10, ROLB = 11, LOLB = 12 }

local statisticsCount = 0

local function generatePlayer(position, positionImportance)

	local Character = characterModels[math.random(#characterModels)]:Clone()
	Character.Humanoid:AddAccessory(hairAccessories[math.random(#hairAccessories)]:Clone())
	Character.Humanoid:AddAccessory(Models.Cleets:Clone())
	Character.Cleets.Handle.Color = cleetColors[math.random(#cleetColors)]

	local player = { Character = Character,
		position = position, importance = positionImportance,
		name = `{firstNames[math.random(#firstNames)]} {lastNames[math.random(#lastNames)]}`,
		morale = math.random(50, 100), statisticsAverage = 0,
		statistics = {} }
	
	for statistic, value in minStatistics do
		value = math.random(value, maxStatistics[statistic])
		player.statistics[statistic] = value
		player.statisticsAverage += value
	end
	player.statisticsAverage /= statisticsCount

	local contractLength = math.random(5)
	local salaryOffset = (player.statisticsAverage + player.importance - player.morale) * 100000
	local salary = math.random(10000000 + salaryOffset, 30000000 + salaryOffset)
	player.contract = { length = contractLength, salary = salary, totalSalary = salary * contractLength }
	
	return player

end

local function generateTeam()
	local offense, defense = {}, {}
	for position, importance in offensivePositions do
		for j = 1, 2 do table.insert(offense, generatePlayer(position, importance)) end
	end
	for position, importance in defensivePositions do
		for j = 1, 2 do table.insert(defense, generatePlayer(position, importance)) end
	end
	return { offense = offense, defense = defense }
end

for _ in minStatistics do statisticsCount += 1 end

Look through the code and tell me if I misunderstood something, or ask if you don’t understand something.

A few notes:

  • the statistics calculation assumes that you’ve manually entered values for every statistic
  • I have no idea which positions are offensive and which defensive, and the numbers next to them represent the “importance”
  • from your code I assumed that you need 2 players for every position you had in the list
  • you could further optimize by not storing values that you won’t need later (in the player table), I didn’t want to assume that part so I left all of them in
  • I replaced the custom utilities.random function with the regular math.random, if the custom one did something different than the built-in one you should account for that
  • I assumed the custom utilities.CountTable function just sums all numbers in a table

The only really concerning part about your code was the two returns (if the models weren’t found), I assume you meant continue? Returning would stop generating the team. Continuing would keep going through the loop, which would also be problematic - if a model is not found multiple times your team would be short of players. Though the only reason why a model wouldn’t be found in that case is if you didn’t name them correctly (from ‘base_1’ to however many you have), and there’s no need to rely on that in this case.

Sorry for the late reply, however thank you so much for the reply! However, I have a few questions:

  • Since this game will be single player, would optimization really be needed, unless I am optimizing for low performance devices like mobile?

  • Currently, the cleats are a model, and when generating the outfits of the team, I weld the cleats to the legs of the plays. Does this cost more resources than having the cleats as accessories?

For something like this that is done only once in a while, optimization won’t do much (that is, the speed difference won’t be noticeable, and the memory that you didn’t use would’ve been freed up eventually anyways). That’s not to say that you shouldn’t, you should constantly be getting accustomed to better and better practices, because they do matter when talking about the entirety of a project. I haven’t seen anyone who writes optimal codes in crucial places and sub-optimal in other places, it’s always the same thing everywhere.

Welding is fine, that’s what happens to Accessories as well, but with an Accessory you get automatic scaling (provided that everything is set up correctly). If all your players have the same height and proportions, and you don’t intend to change that, then you can stick to your current system.

Thank you so much! One last question:

In the code block you sent, was the last line meant to be outside the function? Also, why is the type checking in a table at all times?

Yes, the last line just counts the number of stats so it can later be used to get a player’s average stats. If you prefer, you can put the kind of stuff like that right below or next to where you declare the variable.

Not sure what you mean by type checking; if you’re talking about the type annotations for the first 2 tables: I use type anotations whenever the interpreter won’t know exactly what the type will be. In case of :GetChildren() it knows that you’ll get a table of Instances, and sometimes that’s enough (if you’ll only use their Name, Parent, etc.), but you can specify what it is exactly if you want accurate auto-complete suggestions, and that’s why that’s there, it’s not necessary, just convenient.

Ok, thank you so much! character limit

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