DunGen is a unity asset tool that I was tasked with enhancing to increase its flexibility by expanding its options and refining its limitations.
I worked on this project while interning at House of How. Me and another intern was tasked with generating Minecraft parkour maps by improving the Unity tool DunGen to give more creative freedom to the game designers using it.
DunGen already had basic boundary restrictions in place, but I was tasked with enhancing this system by creating a new restriction rule. Scene colliders would now be able to define the restricted area, allowing the dungeon to generate tiles only within those bounds. This improvement enabled the generation of tiles within multiple boxes and more intricate shapes by simply adjusting the colliders.
The restriction can find any collider but will only use the bounds for said collider. Since unitys bounds is always represented as a box that means the tiles can be generated outside of the collider if the tile is still within the collider bounds.
//Find all the colliders that are meant to be the restriction area.
//These have to be on a certain layer.
List FindGameObjectsWithLayer(int layer)
{
GameObject[] goArray = GameObject.FindObjectsOfType(typeof(GameObject)) as GameObject[];
List goList = new List();
for (int i = 0; i < goArray.Length; i++)
{
if (goArray[i].layer == layer)
goList.Add(goArray[i]);
}
if (goList.Count == 0)
return null;
return goList;
}
//Do collision check here with all colliders on layer RestrictColliderBounds
if (RestrictDungeonToColliderBounds && DungeonRestrictColliders != null && DungeonRestrictColliders.Count > 0 && TempRestrictToColliderBounds)
{
int containedIn = 0;
foreach (GameObject collider in DungeonRestrictColliders)
{
Collider restrictCollider = collider.GetComponent();
Bounds newProxyBounds = restrictCollider.bounds;
if (newProxyBounds.Contains(proxyBounds))
containedIn++;
}
if(containedIn <= 0)
{
Debug.LogWarning("Tile was placed outside of restriction. Removing and retrying");
return TilePlacementResult.OutOfBounds;
}
}
Repeat modes in DunGen is the rules for how a tile is allowed to generate e.g "Allow tiles to be placed in a row". To extend these options I implemented two more repeat modes for tiles.
Allow X tiles in a row makes sure that only x tiles can be placed in a row.
Allow X tiles maximum does not generate any more of that tile once X amount has been placed.
X can be set separetely for each tile using repeat modes where relevant by using the custom tile inspector fields that was added.
In the picture above, the red tiles are set to "allow maximum 3 tiles in a row" the yellow to "allow 4 maximum in dungeon" and the green ones follow the standard "allow anywhere". Both tile types can still be generated less than the rules say.
switch (repeatMode)
{
//Standard
case TileRepeatMode.Allow:
allowTile = true;
break;
//Allow X tiles in a row
case TileRepeatMode.AllowXInRow:
if (potentialNextTile.PrefabTile.AllowedAmount <= 0)
allowTile = false;
else if (UsedAmounts < potentialNextTile.PrefabTile.AllowedAmount)
allowTile = true;
else
allowTile = false;
break;
//Allow X total of tile in dungeon
case TileRepeatMode.AllowXTotalOfTile:
if (potentialNextTile.PrefabTile.AllowedAmount <= 0)
allowTile = false;
else if (potentialNextTile.PrefabTile.TotalAmountPlaced < potentialNextTile.PrefabTile.AllowedAmount)
allowTile = true;
else
allowTile = false;
break;
//Standard
case TileRepeatMode.DisallowImmediate:
allowTile = !isImmediateRepeat;
break;
//Standard
case TileRepeatMode.Disallow:
allowTile = !proxyDungeon.AllTiles.Where(t => t.Prefab == potentialNextTile.Prefab).Any();
break;
default:
throw new NotImplementedException("TileRepeatMode " + repeatMode + " is not implemented");
}
//Reset UsedAmounts & ForceXInRow if new tile != last tile
if (lastTile != null && (createdTile.Prefab != lastTile.Prefab))
UsedAmounts = 0;
//Add to counters if neccessary
switch (createdTile.PrefabTile.RepeatMode)
{
case TileRepeatMode.AllowXInRow:
UsedAmounts++;
break;
case TileRepeatMode.AllowXTotalOfTile:
createdTile.PrefabTile.TotalAmountPlaced++;
break;
default:
break;
}