I am trying to build a randomized city made out of tiles and so far i can build an array, set the the city edge, place a random building such as a church or factory and can place tiles with random rotations within the set area.
But what i need is some kind of algorithm or routine to create an interconnected maze of streets by selecting the right tile and rotating it the right way so as to not end up with an area that cannot be reached in some way.
The tiles i have are - feature tile (such as church or factory) straight road t-junction crossroads corner left corner right square/roundabout/courtyard
I have found tons of examples based on dungeons but nothing along the lines of using fixed tiles and matching/rotating in a set area say 10 tiles x 10 tiles.
Any script hints would be very much appreciated as i am going round in circles making horrendously complicated looping functions thatch still don't work
Code of what i have so far -
public GameObject playerPrefab;
public GameObject cullGroup;
public GameObject cullPrefab;
//tile objects
public List<GameObject> _grid = new List<GameObject>(); //general buildings
public List<GameObject> _industrial = new List<GameObject>(); //industrial
public List<GameObject> _feature = new List<GameObject>(); //city feature
public List<GameObject> _largeSpecial = new List<GameObject>(); //large special building
public List<GameObject> _smallSpecial = new List<GameObject>(); //large special building
public List<GameObject> _docks = new List<GameObject>(); //docks
public List<GameObject> _roads = new List<GameObject>(); //outgoing roads
public List<GameObject> _edge = new List<GameObject>(); //city edge
public Vector2 citySize = new Vector2(10,10); //how many tiles in total
private int[] tileRoatation = new int[5] {0,90,180,270,0};
private int[,,] _cityMap;
private int mapType = 0;
//ingress vars
public int[,] roadIngress;
//feature vars
private int _tmpFeature;
private Vector2 _tmpPlacement;
//special vars
private int _tmpFeature1;
private Vector2 _tmpPlacement1;
//docks vars
private Vector2 _tmpDock;
private string _strDock;
private float _xAdjustment;
private float _zAdjustment;
//industrial vars
private Vector2 _indSector;
private int _indTotal = 0;
// Use this for initialization
void Start ()
{
_cityMap = new int[(int)citySize.x, (int)citySize.y, 2];
CreateMap();
}
// Update is called once per frame
void Update ()
{
}
void CreateMap()
{
//calc roads into city
RoadIngress();
//calc feature
Features();
//calculate tile adjustment from center
_xAdjustment = (_grid[0].transform.localScale.x * (citySize.x / 2) - (_grid[0].transform.localScale.x)/2);
_zAdjustment = (_grid[0].transform.localScale.z * (citySize.y / 2) - (_grid[0].transform.localScale.x)/2);
//place tiles
for(int z = 0; z < citySize.y; z++)
{
for(int x = 0; x < citySize.x; x++)
{
//instantiate tile
int tmpRot = tileRoatation[Random.Range(0,5)];
GameObject go = Instantiate(_grid[Random.Range(0, _grid.Count-1)],
new Vector3(
(x * _grid[0].transform.localScale.x) - _xAdjustment,
0 ,
(z * _grid[0].transform.localScale.z) - _zAdjustment),
Quaternion.Euler(0,tmpRot, 0)
) as GameObject;
_cityMap[x ,z ,1 ] = 1;;
_cityMap[x ,z ,0 ] = tmpRot;;
go.name = x +"/"+z;
go.transform.parent = this.transform;
//instantiate culling areas
/*
GameObject go2 = Instantiate(cullPrefab,
new Vector3(
(x * _grid[0].transform.localScale.x) - _xAdjustment,
0 ,
(z * _grid[0].transform.localScale.z) - _zAdjustment),
Quaternion.Euler(0,0, 0)
) as GameObject;
go2.name = x +"/"+z;
go2.transform.parent = cullGroup.transform;
go2.transform.localScale = new Vector3(51, 20, 51);
*/
//set map edges
if(x == 0 || z == 0 || x == citySize.x-1 || z == citySize.y-1 )
{
//does map have beach
if(mapType == 1 && z == 0)
{
go.GetComponent<MeshRenderer>().material.color = Color.blue;
}else if(roadIngress[x,z] == 1){
//set exit roads
go.SetActive(false);
GameObject rp = Instantiate(_roads[0], new Vector3(go.transform.position.x, 0 ,go.transform.position.z), Quaternion.Euler(0, 0, 0)) as GameObject;
rp.name = go.name;
rp.transform.parent = this.transform;
//go.name = "disabled";
//extend road
}else{
//go.GetComponent<MeshRenderer>().material.color = Color.green;
int tmpRot1 = tileRoatation[Random.Range(0,5)];
go.SetActive(false);
GameObject ep = Instantiate(_edge[Random.Range(0, _edge.Count-1)], new Vector3(go.transform.position.x, 0 ,go.transform.position.z), Quaternion.Euler(0, tmpRot, 0)) as GameObject;
ep.name = go.name;
ep.transform.parent = this.transform;
go.name = "disabled";
}
}
}
}
Debug.Log((citySize.x * citySize.y) * 0.1f);
Industrial();
Industrial();
Debug.Log(_indTotal);
if(citySize.x <((citySize.x * citySize.y) * 0.2f))
Industrial();
PlaceFeature();
PlaceLargeSpecial();
//docks
if(mapType == 1)
{
//go.GetComponent<MeshRenderer>().material.color = Color.red;
_tmpDock = new Vector2(Random.Range(1,citySize.y-1),0);
//_strDock = _tmpDock.x +"/"+_tmpDock.y;
}
PlaceDocks();
// spawn player
Instantiate(playerPrefab, new Vector3(0,2,0),Quaternion.identity);
}
//set roads leaving city
void RoadIngress()
{
//set roads leaving city
roadIngress = new int[(int)citySize.x, (int)citySize.y];
roadIngress[(int)Random.Range(1,citySize.x-1), 0] = 1; //east road or dock
roadIngress[0, (int)Random.Range(1,citySize.y-1)] = 1; //south road
roadIngress[(int)Random.Range(1,citySize.x-1), (int)citySize.y-1] = 1; //west road
roadIngress[(int)citySize.x-1, (int)Random.Range(1,citySize.y-1)] = 1; //north road
}
// choose main feature
void Features()
{
//choose main feature and placement
_tmpFeature = Random.Range(0,4);// 5x feature tiles
_tmpPlacement = new Vector2((int)Random.Range(2,citySize.x-2),(int)Random.Range(2,citySize.y-2));
//choose special large building and placement
_tmpFeature1 = Random.Range(0,4);// 5x feature tiles
_tmpPlacement1 = new Vector2((int)Random.Range(2,citySize.x-2),(int)Random.Range(2,citySize.y-2));
if(_tmpPlacement1 == _tmpPlacement)
{
_tmpPlacement1.x = _tmpPlacement1.x + 4;
if(_tmpPlacement1.x > citySize.x-2)
_tmpPlacement1.x -= citySize.x;
_tmpPlacement1.y = _tmpPlacement1.y + 4;
if(_tmpPlacement1.y > citySize.y-2)
_tmpPlacement1.y -= citySize.y;
}
}
//place feature
void PlaceFeature()
{
int tmpRot1 = tileRoatation[Random.Range(0,5)];
GameObject go1 = Instantiate(_feature[Random.Range(0, _feature.Count-1)],
new Vector3((_tmpPlacement.x * _grid[0].transform.localScale.x - (_grid[0].transform.localScale.x *.5f) - _xAdjustment),
0,
(_tmpPlacement.y * _grid[0].transform.localScale.z + (_grid[0].transform.localScale.z*.5f) - _zAdjustment)),
Quaternion.Euler(0,tmpRot1, 0)) as GameObject;
//clear feature area
GameObject tp = GameObject.Find(_tmpPlacement.x + "/" + _tmpPlacement.y);
tp.SetActive(false);
tp.name = "disabled";
tp = GameObject.Find(_tmpPlacement.x + "/" + (_tmpPlacement.y+1));
tp.SetActive(false);
tp.name = "disabled";
tp = GameObject.Find(_tmpPlacement.x-1 + "/" + _tmpPlacement.y);
tp.SetActive(false);
tp.name = "disabled";
tp = GameObject.Find(_tmpPlacement.x-1 + "/" + (_tmpPlacement.y+1));
tp.SetActive(false);
tp.name = "disabled";
}
//place large special
void PlaceLargeSpecial()
{
//check distance between feature and large special
int dist = (int)Vector2.Distance(_tmpPlacement, _tmpPlacement1);
//if to close move away
if((int)Vector2.Distance(_tmpPlacement, _tmpPlacement1)<3)
{
_tmpPlacement1.x = _tmpPlacement1.x+3;
if(_tmpPlacement1.x > citySize.x-2)
_tmpPlacement1.x = 3;
_tmpPlacement1.y = _tmpPlacement1.y+3;
if(_tmpPlacement1.y > citySize.y-2)
_tmpPlacement1.y = 3;
}
//Debug.Log(dist);
int tmpRot2 = tileRoatation[Random.Range(0,5)];
GameObject go2 = Instantiate(_largeSpecial[Random.Range(0, _largeSpecial.Count-1)],
new Vector3((_tmpPlacement1.x * _grid[0].transform.localScale.x - (_grid[0].transform.localScale.x *.5f) - _xAdjustment),
0,
(_tmpPlacement1.y * _grid[0].transform.localScale.z + (_grid[0].transform.localScale.z*.5f)) - _zAdjustment),
Quaternion.Euler(0,tmpRot2, 0)) as GameObject;
//clear large special area
GameObject tp = GameObject.Find(_tmpPlacement1.x + "/" + _tmpPlacement1.y);
tp.SetActive(false);
tp.name = "disabled";
tp = GameObject.Find(_tmpPlacement1.x + "/" + (_tmpPlacement1.y+1));
tp.SetActive(false);
tp.name = "disabled";
tp = GameObject.Find(_tmpPlacement1.x-1 + "/" + _tmpPlacement1.y);
tp.SetActive(false);
tp.name = "disabled";
tp = GameObject.Find(_tmpPlacement1.x-1 + "/" + (_tmpPlacement1.y+1));
tp.SetActive(false);
tp.name = "disabled";
}
//place docks
void PlaceDocks()
{
if(mapType == 1)
{
GameObject go3 = Instantiate(_docks[Random.Range(0, _docks.Count-1)],
new Vector3((_tmpDock.x * _grid[0].transform.localScale.x - (_grid[0].transform.localScale.x) - _xAdjustment),
0,
(_tmpDock.y * _grid[0].transform.localScale.z) - _zAdjustment),
Quaternion.Euler(0,0, 0)) as GameObject;
//clear dock area
/*
string[] tmp = _strDock.Split("/"[0]);
int t1 = int.Parse(tmp[0]);
for(int a=0; a<3; a++)
{
GameObject.Find(t1-a + "/" + 0).SetActive(false);
}
for(int b=0; b<3; b++)
{
GameObject.Find(t1-b + "/" + 1).SetActive(false);
}
*/
}
}
//set out industrial area
void Industrial()
{
//pick random spot industrial area
_indSector = new Vector2((int)Random.Range(2,citySize.x-2),(int)Random.Range(2,citySize.y-2));
int indSizeX = Random.Range(2,5);
int indSizeY = Random.Range(2,4);
_indTotal += (indSizeX*indSizeY);
//create area
for(int y = (int)_indSector.y; y < (int)_indSector.y+indSizeY; y++)
{
for(int x = (int)_indSector.x; x < (int)_indSector.x+indSizeX; x++)
{
//GameObject.Find(x + "/" + y).GetComponent<MeshRenderer>().material.color = Color.magenta;
GameObject tp = GameObject.Find(x + "/" + y);
//Debug.Log(x + "/" + y);
GameObject go4 = Instantiate(_industrial[Random.Range(0, _industrial.Count-1)],
new Vector3(tp.transform.position.x, 0, tp.transform.position.z), Quaternion.Euler(0,0, 0)) as GameObject;
tp.SetActive(false);
tp.name = "disabled";
go4.name = x +"/"+y;
go4.transform.parent = this.transform;
}
}
}