Data describing random level tiles: design advice?

Background info- I’m making a procedural platformer-like game that is 3d tile-based but with varying size tiles. These are organized into folders in ReplicatedStorage and cloned into the workspace. I think that’s the right thing to do- I’m more used to Unity’s ‘GameObjects’ and ‘instantiating prefabs’.

The generation script has both an [x][y][z] table of cells with references to the tile objects occupying them, and a table of sequential tables representing paths through the map chunk.

The tile master models have some properties that the generating script will need to know to place them, and their instances have properties that won’t be known until after they are placed.

My question is where to put that data about each tile object. Do I create attributes on each master model and put references to the cloned instance into those tables? Or have them all defined as an array of objects ( I know, everything’s a “table” in Lua ) in a script, and one of the object’s properties is the reference to the master model, and those are what go into the map tables?

It seems either would work, I don’t know what would be considered the right way. Mainly I have concerns about the performance of doing lots of :GetAttribute() and :SetAttribute()- should I?

Also, if I put the tile attributes in tables I’m not sure if they should go at the top of the script that generates the level or in a ModuleScript that’s nothing but that. And for interactive / animated tiles like an elevator or wall shooter, whether they should have their own script on them to do their behavior. Having a single ServerScript handle everything seems to be the preferred way but attached scripts might be better organized.

Maybe none of this stuff matters but in case it does I’d rather find out now, because there are going to be a lot of tiles so whatever broad architectural decisions I make at this stage will be a pain to rework later.

Any other tips on what not to do / how not to do it that I haven’t even thought of yet- learned the hard way by someone who’s already made something similar- welcome and appreciated.

Attributes are probably good although I prefer to have a script that has all the properties in a table, just because its easier to Ctrl-F to find something. There are many ways you can add “inheritance” (not using metatables) to this system as well for things that are shared. For tiles that have scripts, you can provide a list of functions required to initialize anything from a shared server script:

tilepath = TilesFolder.ElevatorTileModel
ElevatorTile = {
	tilepath , --Tile base path for cloning
	initFunctions = { --This is an array, the entries are keyed by numbers in order
		--These "interactive" scripts can be require() from somewhere
		function() SetupElevator(path.Elevator) end,
		function() SetupLootChest(path.LootChest1) end --Use your imagination
	}
}

--To run init scripts after placing tile
local tile = GenerateNextTile() --Could be an elevator tile or something else
for _, initFunc in ipairs(tile.initFunctions) do
	initFunc()
end

This is a specific advantage of being able to use functions as values. You can take this a lot further, for example providing a “rarity modifier” to the SetupLootChest function.

Maintenance note: Whenever you use an indirection pattern like this, you run the risk of making bugs harder to track down. If you use something like this, put plenty of checks on the inputs to the Setup functions.

1 Like