OOP menu script?

hello, so below I have my first completed version of an OOP system. It’s not completely OOP, i struggled with some of the aspects of a typical OOP because I wanted everything organized in folders and I wasn’t sure how to do a sort of table inside a table thing. So instead I went with folders in the replicated storage etc.
basically, Im looking at this code and thinking its very messy, and not very modular at all. the goal was for it to be object oriented like I said, But i wasn’t sure how else to do it. If someone could look through it and give me some pointers or something I should work on that would be great.
another thing is, as is explained in the code description, if the use of replicated storage was proper or not.

code description
I use a script to create a folder for every player that joins the game,
I have a folder with a folder of every location in the map, with three parts connected to touched events, all inside of the workspace
I use said folder in the workspace filled with every coin, and basically make a copy of that folder system in every players designated folder. I then use bool values to show if the player has touched the ‘coin’ or not.

--module script
local coin = {}
coin.__index = coin

local playerFolder = game.ReplicatedStorage:WaitForChild("playerFolder")

coin.New_Player = function(player)--create a table for every player upon joining
	local newFolder = setmetatable({
		folder = Instance.new("Folder",playerFolder)
		
	},coin)
	newFolder.folder.Name = "Player_"..tostring(player.UserId)
	
	for i,v in pairs(game.Workspace.Locations:GetChildren()) do --create a folder for every location and puts it in player folder
		newFolder["loc_"..tostring(v)] = Instance.new("Folder",newFolder.folder)
		newFolder["loc_"..tostring(v)].Name = "loc_"..tostring(v)
		for i = 1,3 do
			local num = Instance.new("BoolValue")
			num.Name = i
			num.Value = false
			num.Parent = newFolder["loc_"..tostring(v)]
		end
	end
	return newFolder
end

coin.New_Coin = function(newCoinIn)
	newCoinIn.Touched:Connect(function(part)
		local player =game.Players:GetPlayerFromCharacter(part.Parent)
		if game.ReplicatedStorage.playerFolder["Player_"..tostring(player.UserId)]["loc_"..tostring(newCoinIn.Parent.Name)][newCoinIn.Name] ~=true then
			game.ReplicatedStorage.playerFolder["Player_"..tostring(player.UserId)]["loc_"..tostring(newCoinIn.Parent.Name)]:WaitForChild(tostring(newCoinIn.Name)).Value = true
		end
		
	end)
end

return coin
--script
local coin = require(script.ModuleScript)

game.Players.PlayerAdded:Connect(function(player)
	local newPlayer = coin.New_Player(player)
end)

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function()
		print("character added")
	end)
	wait(2)
	player:LoadCharacter()

end)

for i,v in pairs(game.Workspace.Locations:GetChildren()) do
	for i,newCoin in pairs(v:GetChildren()) do
		coin.New_Coin(newCoin)
	end
end



5 Likes

The only thing I can see you improving is not relying on instances. I recommend making the modules not create folders and store these values in tables, which are dictionaries or arrays.

5 Likes

in this case it was helpful to have each location seperated, and for me I found it easiest to organize it via a folder system with values inside of them. If I wanted to do this I would have to do some sort of table inside of a table system, unless I’m wrong. How would I go about doing that?

You would do everything you do with folders, but this time organize them into global table variables. This means using playerListTable[playerInstance] = true, because indexing by instances will automatically free up memory when the instance is deleted.

3 Likes

i did not know I could index a table using an instance. Is it possible for you to explain how this works or what is going on?

playerListTable[playerInstance] = true

from what I’m understading what your saying is by making the playertable indexed via instances, when a player leaves it will just delete the player. I could also do this with the current system and just delete the folder with the player?

also, one last note, I have it all stored in folders so that both the server and and client can access the information, and I don’t know if sending a table back in forth is better or whatnot

Yes, this is correct, and you can do this with the current system.

I see. In fact, it is not better, you are correct. However, I think you can remove the folders entirely if you add attributes to the player, which will be faster. However, the more I think about the system you implemented, the better it seems.

1 Like

thank you. I appreciate all the help. :slight_smile:

I would probably use the CollectionService for something like this.

The following prevents a coin from being collected more than once.

Just copy this into a modulescript named Coin, require it on the server, and tag your instances with Coin. It should print “Coin collected!” only once for each coin.

local Collection = game:GetService("CollectionService")
local Players = game:GetService("Players")

local Coin = {}

local collected = {}
local connection = {}

function Coin.new(self)
	connection[self] = self.Touched:Connect(function (hit)
		local plr = Players:GetPlayerFromCharacter(hit.Parent)
		
		if not plr then return end
		
		Coin.Collect(self, plr)
	end)
end

function Coin:Destroy()
	for _, plr in ipairs(Players:GetPlayers()) do
		collected[plr][self] = nil
	end
	
	connection[self]:Disconnect()
	connection[self] = nil
end

function Coin:Collect(plr)
	if Coin.IsCollected(self, plr) then return end
	
	collected[plr][self] = true
	
	print("Coin collected!")
end

function Coin:IsCollected(plr)
	return collected[plr][self] or false
end

Collection:GetInstanceAddedSignal("Coin"):Connect(Coin.new)
Collection:GetInstanceRemovedSignal("Coin"):Connect(Coin.Destroy)

for _, coin in ipairs(Collection:GetTagged("Coin")) do
	Coin.new(coin)
end

local function playerAdded(plr)
	collected[plr] = {}
end

Players.PlayerAdded:Connect(playerAdded)
Players.PlayerRemoving:Connect(function (plr)
	collected[plr] = nil
end)

for _, plr in ipairs(Players:GetPlayers()) do
	playerAdded(plr)
end

return Coin

You will need to create another module to handle client behavior. You can then use a RemoteEvent to communicate between the two.

I hope this at least provides some insight on how OOP can be used.

Can you give an example to the syntax of this?

local array = {1, 2, 3, 4}

How do you “assign” it to a player so the script knows that table A is played 1’s data instead of player 2’s?

With more tables!

local data = {}
---
local playerDataTable = {leaderstats.Kills.Value, leaderstats.Deaths.Value}
--to get kills, do this:
--local kills = playerDataTable[1]
--put the table into another table!
data[player] = playerDataTable

--access it later:
local playerkills = data[player][1]
1 Like

Is it possible to do this same method but if an individual player? I’m going to create a character selection script (hair color, skin shade, face) but store the data as a table and want to be able to reference it easily.

I just said how. You could alternatively create a new table for each player and store it that way.

How do I store a new table for each different player and be able to reference them? Sorry to be asking so many questions.

Do you mean like this? I’m not sure what you’re referring to otherwise.

Ohhhh I was not thinking straight I understand it now.

Is a system like this good for storing player information and is it a good idea to store it in datastores?

Ideally, you should encode the data into JSON format before saving it. Additionally, each player should have their own key. Yes, it should be stored in a table.

1 Like