Skip to content

Instantly share code, notes, and snippets.

@blewert
Created January 14, 2021 12:39
Show Gist options
  • Save blewert/b4c561d4d0b335b4745868399e80ad87 to your computer and use it in GitHub Desktop.
Save blewert/b4c561d4d0b335b4745868399e80ad87 to your computer and use it in GitHub Desktop.
A simple modular building generator for Unity.
/**
* @brief A simple modular building generator for Unity.
* @author Benjamin Williams
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SimpleModularBuildingGenerator : MonoBehaviour
{
/// <summary>
/// A list of building spawnpoints
/// </summary>
public List<Transform> buildingSpawnpoints;
[Header("[prefabs]")]
/// <summary>
/// The list of bottom prefabs
/// </summary>
public List<GameObject> bottomPrefabs;
/// <summary>
/// The list of top prefabs
/// </summary>
public List<GameObject> topPrefabs;
/// <summary>
/// The list of mid prefabs
/// </summary>
public List<GameObject> midPrefabs;
[Header("[generation options]")]
[Range(1,5)]
public int maxMidTiers = 1;
/// <summary>
/// The roof rotation fix (optional)
/// </summary>
public Vector3 roofRotationFix;
/// <summary>
/// Just gives us a random selection from a list
/// </summary>
/// <param name="collection">The collection</param>
/// <returns>A random item</returns>
private T RandomChoice<T>(in List<T> collection) => collection[Random.Range(0, collection.Count)];
/// <summary>
/// Spawns the top-most tier
/// </summary>
/// <returns></returns>
private GameObject SpawnTopTier(Transform spawn, GameObject lastMidTier)
{
//Choose a random prefab
var randomTop = this.RandomChoice(in topPrefabs);
//Find spawn position
var spawnPosition = lastMidTier.transform.position + Vector3.up * lastMidTier.GetComponent<Renderer>().bounds.size.y;
//Spawn it
var obj = GameObject.Instantiate(randomTop, spawnPosition, spawn.rotation, spawn) as GameObject;
//Set the name
obj.name = "bottom-section";
//OPTIONAL: Roof rotation fix
obj.transform.Rotate(roofRotationFix);
//Delete mesh renderer of the parent object so we dont see it (kinda optional)
Destroy(spawn.gameObject.GetComponent<Renderer>());
//Return the base
return obj;
}
/// <summary>
/// Spawns a random number of mid tiers on top of a base
/// </summary>
private GameObject SpawnMidTiers(Transform spawn, int numMidTiers, GameObject baseObject)
{
//Run from 0 to mid tiers
//..
//Set yOffset to base height, initially (we want to spawn on top of the base)
float yOffset = baseObject.GetComponent<Renderer>().bounds.size.y;
//The last mid tier spawned (for returning)
GameObject lastMidTier = baseObject;
for(int i = 0; i < numMidTiers; i++)
{
//Compute the place to spawn this mid tier:
//It's basically just the base position + up * height of building currently
var spawnPos = spawn.transform.position + Vector3.up * yOffset;
//Choose a prefab
var randomPrefab = this.RandomChoice(midPrefabs);
//Spawn at this location, set name
var obj = GameObject.Instantiate(randomPrefab, spawnPos, baseObject.transform.rotation, spawn);
obj.name = $"mid-tier-{i}";
//Add to yOffset the height of this spawned mid tier
yOffset += obj.GetComponent<Renderer>().bounds.size.y;
//Sete last mid tier to this
lastMidTier = obj;
}
//Return the last mid tier
return lastMidTier;
}
/// <summary>
/// Spawns a base at a particular location
/// </summary>
/// <param name="spawnTransform"></param>
private GameObject SpawnBase(Transform spawnTransform)
{
//Choose a random prefab
var randomBottom = this.RandomChoice(in bottomPrefabs);
//Spawn it
var obj = GameObject.Instantiate(randomBottom, spawnTransform.position, spawnTransform.rotation, spawnTransform) as GameObject;
//Set the name
obj.name = "bottom-section";
//Delete mesh renderer of the parent object so we dont see it (kinda optional)
Destroy(spawnTransform.gameObject.GetComponent<Renderer>());
//Return the base
return obj;
}
// Start is called before the first frame update
void Start()
{
foreach(var spawn in buildingSpawnpoints)
{
//Spawn the base
var baseObject = this.SpawnBase(spawn);
//Choose a random amount of mid tiers
int numMidTiers = Random.Range(0, maxMidTiers);
//And spawn that many
var lastMidTier = this.SpawnMidTiers(spawn, numMidTiers, baseObject);
//Spawn a roof
this.SpawnTopTier(spawn, lastMidTier);
}
}
private float lastTime = 0;
public float updateDelta = 0.25f;
void Update()
{
//Clamp one-sided
updateDelta = Mathf.Max(updateDelta, 0.05f);
if((Time.time - lastTime) > updateDelta)
{
//Set last time
lastTime = Time.time;
foreach(var spawn in buildingSpawnpoints)
{
//Kill all children in building spawn points
//..
foreach(Transform child in spawn)
Destroy(child.gameObject);
//Respawn!
//..
//Spawn the base
var baseObject = this.SpawnBase(spawn);
//Choose a random amount of mid tiers
int numMidTiers = Random.Range(0, maxMidTiers);
//And spawn that many
var lastMidTier = this.SpawnMidTiers(spawn, numMidTiers, baseObject);
//Spawn a roof
this.SpawnTopTier(spawn, lastMidTier);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment