Where we are coming from
In the previous article we added a roof top to our buildings, see here. This article is just about adding some meta data to the building.
Target
There is more information linked to a building than only the visual one - let's take the address or a special name as an example. To be able to reference and use this information, it must of course be stored.
We will do this as an extra data class to be flexible in terms of enhancing.
Implementation
Differences
The main differences are mostly two:
- adding a 'BuildingMetaInfo' Property to the building generator
- create the class and add some first infos to it.
Full Script
And here the full script to replace the one from the previous article:
SimpleBuildingGenerator.cs
using System;
using System.Collections.Generic;
using UnityEngine;
public class SimpleBuildingGenerator : BaseEditorEnabledBehaviour
{
#region Properties
[Header("Floor Information")]
[Tooltip("The floor templates to be used")]
public List<BuildingFloorInfoObject> Floors = new List<BuildingFloorInfoObject>();
[Header("Optional Rooftop")]
[Tooltip("Shall a Rooftop be added on top of the floors?")]
public bool DoAddRoofTop;
[Tooltip("If a roof top should be added, which prefab to use, if 'DoAddRoofTop' is false this will be ignored.")]
public GameObject RoofTopPrefab;
[Header("Common Properties")]
[Tooltip("The number of floors to create")]
[Range(1, 250)]
public int NumberOfFloors;
[Tooltip("Important Meta Info")]
public BuildingMetaInfo MetaInfo;
#endregion Properties
#region Construction
#endregion Construction
#region Methods
#region Awake: the building generation is being executed here
/// <summary>
/// the building generation is being executed here
/// </summary>
private void Awake()
{
this.Generate(this.Floors, this.NumberOfFloors);
}
#endregion Awake
#region Generate: The building is being created by instantiating the floor Prefab numberOfFloors times
/// <summary>
/// The building is being created by instantiating the floor Prefab numberOfFloors times
/// </summary>
/// <param name="floorPrefab">the prefab for a floor</param>
/// <param name="floorHeight">the height of one floor (needs to be greater than zero)</param>
/// <param name="numberOfFloors">the number of floors to iterate over (needs to be greater than one)</param>
private void Generate(List<BuildingFloorInfoObject> floors, int numberOfFloors)
{
if (floors == null || floors.Count.Equals(0)) //make sure the floors are not null and not empty
{
//floor prefabs need to be filled
//TODO: Log error for the above
}
else if (numberOfFloors < 1) //ensure the number of floors is greater than zero
{
// number of floors needs to be greater than one
//TODO: Log error for the above
}
else
{
Vector3 currentSpawnPosition = this.transform.position; //start at the current position
for (int currentFloor = 0; currentFloor < this.NumberOfFloors; currentFloor++) //go from 0 to numberOfFloors-1
{
int usedPrefabsForThisFloor = 0; //counter for used prefabs for one floor, to be able to check of anything has been placed
float greatestHeight = 0f; //if more than one floor prefabs are set, determine the greatest height, probably subject to change
foreach (BuildingFloorInfoObject floor in floors)
{
if (floor.FromFloor <= currentFloor && floor.ToFloor > currentFloor)
{
if (floor.FloorPrefabs == null)
{
//floor prefabs has to be filled
//TODO: Log error for the above
}
else if (floor.FloorPrefabs.Count.Equals(0)) //NEW
{
//floor prefabs is an empty list
//TODO: Log error for the above
}
else if (floor.FloorHeight < 0f)
{
// floor needs to have a height
//TODO: Log error for the above
}
else
{
int selectedPrefabIndex = UnityEngine.Random.Range(0, floor.FloorPrefabs.Count);
GameObject selectedPrefab = floor.FloorPrefabs[selectedPrefabIndex];
if (selectedPrefab != null)
{
GameObject newFloor = GameObject.Instantiate(selectedPrefab, currentSpawnPosition, Quaternion.identity, this.transform); //instantiate the prefab on the desired position and as child of the current GameObject
if (newFloor != null)
{
newFloor.name += " - Floor " + currentFloor.ToString("D4"); //add floor name for the visual tree
usedPrefabsForThisFloor++; //increase counter for instantiated prefabs for this particular floor
if (greatestHeight < floor.FloorHeight)
{
greatestHeight = floor.FloorHeight; //set the used height to the greatest height of used prefabs, is being called once as a minimum
}
}
else
{
//gameobject could not be instantiated
//TODO: Log error for the above
}
}
else
{
//selected prefab is null
//TODO: Log error for the above
}
}
}
else
{
//no reaction needed as only the current iterated floor has not been applicable, others could be, we don't know here
}
}
if (usedPrefabsForThisFloor.Equals(0)) //no floor prefab could be found that can be used for this floor
{
//error that one floor was skipped plus infos
//TODO: Log error for the above
}
else
{
//only iterate next spawn position if a prefab could be applied
currentSpawnPosition += new Vector3(0, greatestHeight, 0); //adding height to the next spawn position
}
}
if (this.DoAddRoofTop)
{
//add rooftop
GameObject roofTop = GameObject.Instantiate(this.RoofTopPrefab, currentSpawnPosition, Quaternion.identity, this.transform); //instantiate the prefab on the desired position and as child of the current GameObject
if (roofTop != null)
{
roofTop.name += " - Rooftop"; //add name for the visual tree
}
else
{
//gameobject could not be instantiated
//TODO: Log error for the above
}
}
}
}
#endregion Generate
#region EditorValidateCallback: all children are being destroyed and the building is being regenerated
/// <summary>
/// all children are being destroyed and the building is being regenerated
/// </summary>
protected override void EditorValidateCallback()
{
this.gameObject.DestroyAllChildren(true); //destroy all children to have no artifacts / leftover floors
this.Generate(this.Floors, this.NumberOfFloors); //restart creation to get potentially new parameters
}
#endregion EditorValidateCallback
#endregion Methods
}
You can see two new Properties at the top, 'DoAddRoofTop' and 'RoofTopPrefab'.
- DoAddRoofTop: bool property asking, if the developer wants to add a roof top
- RoofTopPrefab: the prefab to be used as a roof top, will be ignored when above property is false.
In the 'Generate()' method at the end, we add the part where the bool property is being checked and if true, a roof top will be added. This could be a subject for refactoring, as it doubles some code from the original instantiation.
BuildingFloorInfoObject.cs
No changes needed.
BuildingMetaInfo.cs
This is the newly added data class, containing only two pieces of information, for now:
using System;
[Serializable]
public class BuildingMetaInfo
{
#region Properties
public string Name;
public string Address;
#endregion Properties
#region Methods
#endregion Methods
}
Results
With this feature being added, we now can move on in this series!
Next Steps
Now, if you want to have attachments or decoration details, you need to include them into the given floor prefabs. To achieve more independency, we try to seperate floors from decoration to offer another layer of detail.
-
how to use the script to add other elements to the building as well
-
how to remember generated buildings by a simple seeding mechanism
No responses yet