// Place objects from the generated BSP public void Build() { if (nodes == null) { return; } // Destroy all children in base plane for (int i = objectContainer.childCount - 1; i >= 0; i--) { DestroyImmediate(objectContainer.GetChild(i).gameObject); } // Scale base plane correctly basePlane.localScale = new Vector3(width / 10f, 1, height / 10f); lampLights = new List <Light>(); for (int i = 0; i < nodes.Count; i++) { BSPNode node = nodes[i]; int x = (int)(node.x - width / 2f + borderSize); int y = (int)(node.y - height / 2f + borderSize); // Add roads if (node.y != 0) { // TODO: Add function that adds a road and sets the grid types all in one // Roads and pavements are raised by a small random value to avoid graphical glitches // when point light interacts with overlapping geometry = needs changing GameObject roadH = Instantiate(roadPrefab, new Vector3(x + node.width / 2f, 0.01f + Random.value * 0.001f, y), Quaternion.Euler(90, 0, 0), objectContainer); roadH.transform.localScale = new Vector3(node.width, 2 /*Random.Range(1.5f, 2.5f)*/, 1); setGridPointsFromRect(node.x + borderSize, node.y + borderSize - 1, node.width, 2, GridPoint.Type.Path); } if (node.x != 0) { GameObject roadV = Instantiate(roadPrefab, new Vector3(x, 0.01f + Random.value * 0.001f, y + node.height / 2f), Quaternion.Euler(90, 0, 0), objectContainer); roadV.transform.localScale = new Vector3(2, node.height, 1); setGridPointsFromRect(node.x + borderSize - 1, node.y + borderSize, 2, node.height, GridPoint.Type.Path); } // Add pavements float pavementWidth = 0.8f; GameObject pavementL = Instantiate(pavementPrefab, new Vector3(x + 1 + pavementWidth / 2f, Random.value * 0.001f, y + node.height / 2f), Quaternion.identity, objectContainer); GameObject pavementR = Instantiate(pavementPrefab, new Vector3(x + node.width - 1 - pavementWidth / 2f, Random.value * 0.001f, y + node.height / 2f), Quaternion.identity, objectContainer); GameObject pavementT = Instantiate(pavementPrefab, new Vector3(x + node.width / 2f, Random.value * 0.001f, y + node.height - 1 - pavementWidth / 2f), Quaternion.identity, objectContainer); GameObject pavementB = Instantiate(pavementPrefab, new Vector3(x + node.width / 2f, Random.value * 0.001f, y + 1 + pavementWidth / 2f), Quaternion.identity, objectContainer); pavementL.transform.localScale = pavementR.transform.localScale = new Vector3(pavementWidth, pavementL.transform.localScale.y, node.height - 2); pavementT.transform.localScale = pavementB.transform.localScale = new Vector3(node.width - 2, pavementL.transform.localScale.y, pavementWidth); setGridPointsFromRect(node.x + borderSize + 1, node.y + borderSize + 1, 1, node.height - 2, GridPoint.Type.Pavement); setGridPointsFromRect(node.x + borderSize + 1, node.y + borderSize + node.height - 2, node.width - 2, 1, GridPoint.Type.Pavement); setGridPointsFromRect(node.x + borderSize + node.width - 2, node.y + borderSize + 1, 1, node.height - 2, GridPoint.Type.Pavement); setGridPointsFromRect(node.x + borderSize + 1, node.y + borderSize + 1, node.width - 2, 1, GridPoint.Type.Pavement); // Add buildings float border = 2.5f; float gapMin = 0.5f, gapMax = 2.5f; Rect buildingArea = new Rect(x + border, y + border, node.width - border * 2, node.height - border * 2); Building buildingPrefabFirst = buildingPrefabs[Random.Range(0, buildingPrefabs.Length)]; Building buildingNext; // Left edge buildingNext = placeBuildingsAlongEdge(buildingPrefabFirst, new Vector3(buildingArea.xMin, 0, buildingArea.yMin), new Vector3(buildingArea.x, 0, buildingArea.yMax), gapMin, gapMax); // Top edge buildingNext = placeBuildingsAlongEdge(buildingNext, new Vector3(buildingArea.x, 0, buildingArea.yMax), new Vector3(buildingArea.xMax, 0, buildingArea.yMax), gapMin, gapMax); // Right edge buildingNext = placeBuildingsAlongEdge(buildingNext, new Vector3(buildingArea.xMax, 0, buildingArea.yMax), new Vector3(buildingArea.xMax, 0, buildingArea.y), gapMin, gapMax); // Bottom edge placeBuildingsAlongEdge(buildingNext, new Vector3(buildingArea.xMax, 0, buildingArea.y), new Vector3(buildingArea.xMin, 0, buildingArea.yMin), gapMin, gapMax); // Add lampposts int corner = Random.Range(0, 4); // 0 is bottom left, increases clockwise float indent = 2.2f; Vector3 pos = new Vector3(x + indent + (corner > 1 ? node.width - indent * 2 : 0), 0, y + indent + (corner == 1 || corner == 2 ? node.height - indent * 2 : 0)); GameObject lamppost = Instantiate(lamppostPrefab, pos, Quaternion.Euler(0, -135 + 90 * corner, 0), objectContainer); setGridPointFromWorldPos(pos, GridPoint.Type.Obstacle); lampLights.Add(lamppost.GetComponentInChildren <Light>(true)); } int roadAreaWidth = width - borderSize * 2; int roadAreaHeight = height - borderSize * 2; // Add border roads GameObject roadL = Instantiate(roadPrefab, new Vector3(-roadAreaWidth / 2f, 0.01f + Random.value * 0.001f, 0), Quaternion.Euler(90, 0, 0), objectContainer); GameObject roadR = Instantiate(roadPrefab, new Vector3(roadAreaWidth / 2f, 0.01f + Random.value * 0.001f, 0), Quaternion.Euler(90, 0, 0), objectContainer); GameObject roadT = Instantiate(roadPrefab, new Vector3(0, 0.01f + Random.value * 0.001f, roadAreaHeight / 2), Quaternion.Euler(90, 0, 0), objectContainer); GameObject roadB = Instantiate(roadPrefab, new Vector3(0, 0.01f + Random.value * 0.001f, -roadAreaHeight / 2), Quaternion.Euler(90, 0, 0), objectContainer); roadL.transform.localScale = roadR.transform.localScale = new Vector3(2, roadAreaHeight + 2, 1); roadT.transform.localScale = roadB.transform.localScale = new Vector3(roadAreaWidth + 2, 2, 1); setGridPointsFromRect(borderSize - 1, borderSize - 1, 2, roadAreaHeight + 2, GridPoint.Type.Path); setGridPointsFromRect(width - borderSize - 1, borderSize - 1, 2, roadAreaHeight + 2, GridPoint.Type.Path); setGridPointsFromRect(borderSize - 1, borderSize - 1, roadAreaWidth + 2, 2, GridPoint.Type.Path); setGridPointsFromRect(borderSize - 1, height - borderSize - 1, roadAreaWidth + 2, 2, GridPoint.Type.Path); // Add gates bool gate1Left = Random.value > 0.5f; // Gate on left or right wall bool gate2Bottom = Random.value > 0.5f; // Gate on top or bottom wall int gate1Pos = Random.Range((int)(-roadAreaHeight / 2 * 0.8f), (int)(roadAreaHeight / 2 * 0.8f)); int gate2Pos = Random.Range((int)(-roadAreaWidth / 2 * 0.8f), (int)(roadAreaWidth / 2 * 0.8f)); GameObject gate1 = Instantiate(gatePrefab, new Vector3(width / 2f * (gate1Left ? -1 : 1), 0, gate1Pos), Quaternion.Euler(0, 90, 0), objectContainer); GameObject gate2 = Instantiate(gatePrefab, new Vector3(gate2Pos, 0, height / 2f * (gate2Bottom ? -1 : 1)), Quaternion.identity, objectContainer); GridPoint gate1GridPoint = GridPointFromWorldPos(gate1.transform.position); GridPoint gate2GridPoint = GridPointFromWorldPos(gate2.transform.position); // Add border buildings Bounds gate1Bounds = new Bounds(gate1.transform.position, MathHelper.AbsVector3(gate1.transform.rotation * new Vector3(8, 1, borderSize + 12))); Bounds gate2Bounds = new Bounds(gate2.transform.position, MathHelper.AbsVector3(gate2.transform.rotation * new Vector3(8, 1, borderSize + 12))); placeBuildingsAlongEdge(null, new Vector3(-roadAreaWidth / 2, 0, -roadAreaHeight / 2 - 2), new Vector3(roadAreaWidth / 2, 0, -roadAreaHeight / 2 - 2), 0.5f, 2.5f, false, gate2Bounds); placeBuildingsAlongEdge(null, new Vector3(roadAreaWidth / 2 + 2, 0, -roadAreaHeight / 2), new Vector3(roadAreaWidth / 2 + 2, 0, roadAreaHeight / 2 - 2), 0.5f, 2.5f, false, gate1Bounds); placeBuildingsAlongEdge(null, new Vector3(roadAreaWidth / 2, 0, roadAreaHeight / 2 + 2), new Vector3(-roadAreaWidth / 2, 0, roadAreaHeight / 2 + 2), 0.5f, 2.5f, false, gate2Bounds); placeBuildingsAlongEdge(null, new Vector3(-roadAreaWidth / 2 - 2, 0, roadAreaHeight / 2), new Vector3(-roadAreaWidth / 2 - 2, 0, -roadAreaHeight / 2), 0.5f, 2.5f, false, gate1Bounds); // Add walls (TODO: Put this into functions to reduce number of lines) if (gate1Left) { GameObject wallL1 = Instantiate(wallPrefab, new Vector3(-width / 2f, 0, (-height / 2f + gate1Pos) / 2f - 1), Quaternion.Euler(0, 90, 0), objectContainer); GameObject wallL2 = Instantiate(wallPrefab, new Vector3(-width / 2f, 0, (height / 2f + gate1Pos) / 2f + 1), Quaternion.Euler(0, 90, 0), objectContainer); GameObject wallR = Instantiate(wallPrefab, new Vector3(width / 2f, 0, 0), Quaternion.Euler(0, 90, 0), objectContainer); wallL1.transform.localScale = new Vector3(height / 2f + gate1Pos - 4, 1, 1); wallL2.transform.localScale = new Vector3(height / 2f - gate1Pos - 4, 1, 1); wallR.transform.localScale = new Vector3(height - 2, 1, 1); } else { GameObject wallL = Instantiate(wallPrefab, new Vector3(-width / 2f, 0, 0), Quaternion.Euler(0, 90, 0), objectContainer); GameObject wallR1 = Instantiate(wallPrefab, new Vector3(width / 2f, 0, (-height / 2f + gate1Pos) / 2f - 1), Quaternion.Euler(0, 90, 0), objectContainer); GameObject wallR2 = Instantiate(wallPrefab, new Vector3(width / 2f, 0, (height / 2f + gate1Pos) / 2f + 1), Quaternion.Euler(0, 90, 0), objectContainer); wallL.transform.localScale = new Vector3(height - 2, 1, 1); wallR1.transform.localScale = new Vector3(height / 2f + gate1Pos - 4, 1, 1); wallR2.transform.localScale = new Vector3(height / 2f - gate1Pos - 4, 1, 1); } if (gate2Bottom) { GameObject wallB1 = Instantiate(wallPrefab, new Vector3((-width / 2f + gate2Pos) / 2f - 1, 0, -height / 2f), Quaternion.identity, objectContainer); GameObject wallB2 = Instantiate(wallPrefab, new Vector3((width / 2f + gate2Pos) / 2f + 1, 0, -height / 2f), Quaternion.identity, objectContainer); GameObject wallT = Instantiate(wallPrefab, new Vector3(0, 0, height / 2f), Quaternion.identity, objectContainer); wallB1.transform.localScale = new Vector3(width / 2f + gate2Pos - 4, 1, 1); wallB2.transform.localScale = new Vector3(width / 2f - gate2Pos - 4, 1, 1); wallT.transform.localScale = new Vector3(width - 2, 1, 1); } else { GameObject wallB = Instantiate(wallPrefab, new Vector3(0, 0, -height / 2f), Quaternion.identity, objectContainer); GameObject wallT1 = Instantiate(wallPrefab, new Vector3((-width / 2f + gate2Pos) / 2f - 1, 0, height / 2f), Quaternion.identity, objectContainer); GameObject wallT2 = Instantiate(wallPrefab, new Vector3((width / 2f + gate2Pos) / 2f + 1, 0, height / 2f), Quaternion.identity, objectContainer); wallB.transform.localScale = new Vector3(width - 2, 1, 1); wallT1.transform.localScale = new Vector3(width / 2f + gate2Pos - 4, 1, 1); wallT2.transform.localScale = new Vector3(width / 2f - gate2Pos - 4, 1, 1); } // Add roads from the gates GameObject roadGate1 = Instantiate(roadPrefab, new Vector3((width / 2f - borderSize / 2f) * (gate1Left ? -1 : 1), 0.01f + Random.value * 0.001f, gate1Pos), Quaternion.Euler(90, 0, 0), objectContainer); GameObject roadGate2 = Instantiate(roadPrefab, new Vector3(gate2Pos, 0.01f + Random.value * 0.001f, (height / 2f - borderSize / 2f) * (gate2Bottom ? -1 : 1)), Quaternion.Euler(90, 0, 0), objectContainer); roadGate1.transform.localScale = new Vector3(borderSize, 4, 1); roadGate2.transform.localScale = new Vector3(4, borderSize, 1); setGridPointsFromRect(gate1Left ? 1 : width - borderSize, gate1Pos + height / 2 - 2, borderSize, 4, GridPoint.Type.Path); setGridPointsFromRect(gate2Pos + width / 2 - 2, gate2Bottom ? 1 : height - borderSize, 4, borderSize, GridPoint.Type.Path); // Add wall corners GameObject wallCorner1 = Instantiate(wallCornerPrefab, new Vector3(-width / 2f, 0, -height / 2f), Quaternion.Euler(0, 90, 0), objectContainer); GameObject wallCorner2 = Instantiate(wallCornerPrefab, new Vector3(width / 2f, 0, -height / 2f), Quaternion.Euler(0, 0, 0), objectContainer); GameObject wallCorner3 = Instantiate(wallCornerPrefab, new Vector3(width / 2f, 0, height / 2f), Quaternion.Euler(0, -90, 0), objectContainer); GameObject wallCorner4 = Instantiate(wallCornerPrefab, new Vector3(-width / 2f, 0, height / 2f), Quaternion.Euler(0, 180, 0), objectContainer); // Add lampposts at all 4 corners of the map for (int i = 0; i < 4; i++) { float indent = 1.8f; Vector3 pos = new Vector3(-roadAreaWidth / 2f - indent + (i > 1 ? roadAreaWidth + indent * 2 : 0), 0, -roadAreaHeight / 2f - indent + (i == 1 || i == 2 ? roadAreaHeight + indent * 2 : 0)); GameObject lamppost = Instantiate(lamppostPrefab, pos, Quaternion.Euler(0, 45 + 90 * i, 0), objectContainer); setGridPointFromWorldPos(pos, GridPoint.Type.Obstacle); lampLights.Add(lamppost.GetComponentInChildren <Light>(true)); } // Add trees for (int j = 0; j < height; j += 2) { for (int i = 0; i < width; i += 2) { // Make sure there are no obstacles in the 2x2 area around the tree if (gridPoints[i, j].type != GridPoint.Type.Grass || gridPoints[i + 1, j].type != GridPoint.Type.Grass || gridPoints[i, j + 1].type != GridPoint.Type.Grass || gridPoints[i + 1, j + 1].type != GridPoint.Type.Grass) { continue; } float noiseValue = Mathf.PerlinNoise(i * treeNoiseScale, j * treeNoiseScale); if (noiseValue > treeNoiseThreshold) { Vector3 offset = MathHelper.RandomVector2(0.2f, 0.8f); Vector3 pos = new Vector3(-width / 2f + i + offset.x, 0, -height / 2f + j + offset.y); GameObject treeObject = Instantiate(treePrefabs[Random.Range(0, treePrefabs.Length)], pos, Quaternion.Euler(0, Random.Range(0f, 360f), 0), objectContainer); treeObject.transform.localScale = Vector3.one * Random.Range(1f, 3f); // treeObject.transform.localScale = Vector3.one * 3f * noiseValue; gridPoints[i, j].type = GridPoint.Type.Obstacle; } } } // Add bushes for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { if (gridPoints[i, j].type != GridPoint.Type.Grass) { continue; } float noiseValue = Mathf.PerlinNoise(i * bushNoiseScale, j * bushNoiseScale); if (noiseValue > bushNoiseThreshold) { Vector3 offset = MathHelper.RandomVector2(0.2f, 0.8f); Vector3 pos = new Vector3(-width / 2f + i + offset.x, 0, -height / 2f + j + offset.y); GameObject bushObject = Instantiate(bushPrefabs[Random.Range(0, bushPrefabs.Length)], pos, Quaternion.Euler(0, Random.Range(0f, 360f), 0), objectContainer); bushObject.transform.Translate(new Vector3(Random.Range(-.25f, .25f), 0, Random.Range(-.25f, .25f))); bushObject.transform.localScale = Vector3.one * Random.Range(0.7f, 1.3f); // treeObject.transform.localScale = Vector3.one * 3f * noiseValue; gridPoints[i, j].type = GridPoint.Type.Obstacle; } } } // Add grass for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { if (gridPoints[i, j].type != GridPoint.Type.Grass) { continue; } float noiseValue = Mathf.PerlinNoise(i * grassNoiseScale, j * grassNoiseScale); int grassNumber = 0; if (noiseValue > grassNoiseThresholdSmall) { grassNumber = 1; } if (noiseValue > grassNoiseThresholdBig) { grassNumber = 2; } for (int g = 0; g < grassNumber; g++) { Vector3 offset = MathHelper.RandomVector2(0.2f, 0.8f); Vector3 pos = new Vector3(-width / 2f + i + offset.x, 0, -height / 2f + j + offset.y); GameObject grassObject = Instantiate(grassPrefab, pos, Quaternion.Euler(0, Random.Range(0f, 360f), 0), objectContainer); grassObject.transform.localScale = Vector3.one * Random.Range(0.8f, 2.0f); } } } }