This takes a bit of explanation: I’m trying to create a system which generates “random” buildings for my game. Right now I have a script which generates a building’s floorplan as a list of 2D arrays, and which can create a set of 3D game object walls corresponding to this floorplan. However, I would appreciate advice on some aspects I’m having trouble with:
- I want to create a room class which can reference the floorplan arrays. However, classes cannot reference variables in their parents. Is there someway I could create a “Building Manager” object/script which stores the floorplan in static and which room could be a child of?
- If I use Instantiate() to create a prefab object, how can I control variables on it or store it for later editing? I’m planning to use a prefab generic wall object with a script to choose the correct model and texture.
- How do persistent procedural environments work? As in, procedural environments the player can modify, leave, and return to with their changes intact.
- What’s a good way to manage the random seed for this sort of thing? Right now I only store the seed for the ground floor so that upper and basement floors are modified to fit.
If anything is unclear about these questions feel free to ask for more detail. Thank you.
Hi,
Any particular reason your room class has to extend the floorplan one ? What about having a Building class, which would be composed of FloorPlan[ ] which would be composed of Room[ ] ? Then you can easily pass on references if that’s useful to your logic.
My favorite way to work with a prefab that is instantiated at runtime is to have a component at its root which references components/gameObjects within the prefab that need to be worked with at runtime. And I usually use the reference to that component as the source for Instantiate().
For example:
//this goes on the prefab
public class Wall : MonoBehaviour
{
public Transform _rendererTr; //ie: for scaling if you need to stretch it to match a specific size
public Renderer _renderer; //to set the -shared- material
}
//and this goes in your spawning script
public Wall _wallPrefab; //plug the prefab in here
void SpawnWall(Vector3 position, Quaternion rotation, Transform parent)
{
Wall newWall = (Wall)Instantiate(this._wallPrefab, position, rotation);
newWall.transform.parent = parent;
newWall._renderer.sharedMaterial = blabla
//etc...
}
To answer the seed question, you can roll a random number and use that as a seed which you store with your building when its initialy created. Alternatively there are some algorithms which feed back the seed for each new roll, like LCG (Linear congruential generator - Wikipedia), it allows higher control over randomization and can prove usefull in procedural regeneration (ie: if you need to stop the generation to randomize something else and then get back to where you left it’s possible and easy with LCG).
To answer the persistence question, if your world is deterministic you only need to store the seed of the city. Otherwise depending on your context you may store the seed of each building or sequence of buildings. If some parts of your world aren’t deterministic, try to think of where it gets deterministic, and what you need to serialize on top of building seeds is what there is before that point. (ie: if the terrain is procedural and changes based on some settings from start of game you should save those and restore the same world).
Do you plan on having a few buildings spread out like in the sims or many clogged together like in simcity ?
In the later you’ll want inner parts of buildings to be loaded when entering/getting close by so that’s something to keep in mind. Also try to consider that you’ll probably need to use pooling and LOD at some point.
Hope this helps !
A lot of this will be very helpful, thank you! I still have some questions about some of what you said, however.
Won’t this still run into the static issue? From what I’ve read static variables are constant across all instances of an object, and what I need to do is to tell rooms to reference the floorplan their associated with.
I plan to have buildings generated as separate levels. Do I still need pooling and LOD? And what are pooling and LOD?
In what I suggested there is no static field/class/property involved so each instance will be able to hold the reference to whatever object it has to work with !
Your Room class may very well have a constructor that takes a Floorplan as parameter and store it like that:
Floorplan _floorplan;
public Room(Floorplan floorplan)
{
this._floorplan = floorplan;
}
If each building is like a dungeon instance then you’ll probably get away without lods since you wont have a very long viewing distance and i guess no need to zoom out ? Pooling however can always prove usefull but its purely for performance concerns so dont worry too much about it now.
Just so you know the principle is to re-use instantiated objects rather that constantly destroying old ones and creating new ones.
For example in your case, instead of instantiating walls, you would ask the pooling system for wall bits and when the player leaves the level instead of destroying the buildings and its walls you return the walls bit to the pool.
This way, creating the next building is faster since you dont have to instantiate anything and you avoid generating garbage memory that has to be cleaned up with stutter inducing garbage collection.