// Initialize the store layout and make the navigational graph. void Start() { // Place down the visible objects. Build the navigational graph around // the aisles and checkouts as they are placed. // position and then scale, we don't need to keep a reference around for these Instantiate(floor, storeArea.position, Quaternion.identity).transform.localScale = storeArea.size; Instantiate(staffFloor, staffArea.position, Quaternion.identity).transform.localScale = staffArea.size; // the aisles and checkouts DO need references kept, and also need to be tiled // checkouts first since they are simpler and illustrate the principles for aisles UnityEngine.Debug.Assert((checkoutArea.height + MathConstants.floatTolerance) >= (checkoutTopSpace + checkoutHeight + checkoutBottomSpace), "Config ERROR: Checkout area height too small!"); numCheckouts = (int)Math.Min(Math.Floor((checkoutArea.width + MathConstants.floatTolerance) / (checkoutBetweenSpace + checkoutWidth)), // each checkout and its lane beside it maxCheckoutLanes); UnityEngine.Debug.Assert((numCheckouts > 0), "Config ERROR: Checkout area width too small!"); float checkoutYPosition = checkoutArea.y + checkoutBottomSpace; // remember, Y increases upwards so it is convenient to start from the bottom and work upwards checkouts = new GameObject[numCheckouts]; // automatically initializes to null checkoutLanes = new Zone[numCheckouts]; // likewise for (int currentCheckout = 0; currentCheckout < numCheckouts; currentCheckout++) // for each checkout in the row { float laneXPosition = checkoutArea.x + currentCheckout * (checkoutBetweenSpace + checkoutWidth); // shift over by the number of preexisting lanes checkoutLanes[currentCheckout] = new Zone("checkout lane " + currentCheckout.ToString(), new Rect(laneXPosition, checkoutYPosition, checkoutBetweenSpace, checkoutHeight)); // add lane to navigational graph float checkoutXPosition = laneXPosition + checkoutBetweenSpace; checkouts[currentCheckout] = Instantiate(checkout, new Vector2(checkoutXPosition, checkoutYPosition), Quaternion.identity); checkouts[currentCheckout].transform.localScale = new Vector2(checkoutWidth, checkoutHeight); } // add top and bottom checkout zones to navigational graph checkoutBottom = new Zone("zone below checkouts", new Rect(checkoutArea.x, checkoutArea.y, checkoutArea.width, checkoutBottomSpace)); checkoutTop = new Zone("zone above checkouts", new Rect(checkoutArea.x, checkoutYPosition + checkoutHeight, checkoutArea.width, checkoutArea.height - checkoutHeight - checkoutBottomSpace)); // add edges within checkout area to navigational graph foreach (Zone lane in checkoutLanes) { // each checkout lane joins to the top and bottom zones and nowhere else Rect topEdge = new Rect(lane.area.x, lane.area.yMax, lane.area.width, 0); Rect bottomEdge = new Rect(lane.area.x, lane.area.yMin, lane.area.width, 0); Zone.AddEdgeBetweenZones(topEdge, lane, checkoutTop); Zone.AddEdgeBetweenZones(bottomEdge, lane, checkoutBottom); } // all other edges attached to the checkout top and bottom are in other regions, so will be added further down when regions are connected // aisles follow the same principles as checkouts, but can have multiple rows (we build one row at a time) UnityEngine.Debug.Assert((aisleArea.height + MathConstants.floatTolerance) >= (aisleTopSpace + aisleShelfHeight + aisleBottomSpace), "Config ERROR: Aisle area height too small!"); numAisleRows = (int)Math.Min(Math.Floor((aisleArea.height + MathConstants.floatTolerance - aisleTopSpace - aisleBottomSpace + aisleMidSpace) / // 1 less middle space than we have rows (aisleShelfHeight + aisleMidSpace)), maxAisleRows); numAislePairColumns = (int)Math.Min(Math.Floor((aisleArea.width + MathConstants.floatTolerance) / (aislePairSpace + 2 * aisleShelfWidth)), // 2 shelves in each facing pair maxAislePairColumns); UnityEngine.Debug.Assert((numAislePairColumns > 0), "Config ERROR: Aisle area width too small!"); aisleBottom = new Zone("zone below aisles", new Rect(aisleArea.x, aisleArea.y, aisleArea.width, aisleBottomSpace)); // add bottom aisle zone to navigational graph aisles = new GameObject[numAisleRows, 2 * numAislePairColumns]; // automatically initializes to null aislePairs = new Zone[numAisleRows, numAislePairColumns]; // likewise aisleMids = new Zone[numAisleRows - 1]; // as with row calculation, 1 less middle space than we have rows float aisleRowYPosition = aisleArea.y + aisleBottomSpace; // remember, Y increases upwards for (int currentAisleRow = 0; currentAisleRow < numAisleRows; currentAisleRow++) // for each row of aisles { for (int currentAislePairColumn = 0; currentAislePairColumn < numAislePairColumns; currentAislePairColumn++) // for each pair within this row { float pairXPosition = aisleArea.x + currentAislePairColumn * (aislePairSpace + 2 * aisleShelfWidth); // shift over by the number of preexisting pairs // left side of pair aisles[currentAisleRow, 2 * currentAislePairColumn] = Instantiate(aisleShelf, new Vector2(pairXPosition, aisleRowYPosition), Quaternion.identity); aisles[currentAisleRow, 2 * currentAislePairColumn].transform.localScale = new Vector2(aisleShelfWidth, aisleShelfHeight); // add space between pairs to navigational graph aislePairs[currentAisleRow, currentAislePairColumn] = new Zone("aisle pair " + currentAislePairColumn.ToString() + " in row " + currentAisleRow.ToString(), new Rect(pairXPosition + aisleShelfWidth, aisleRowYPosition, aislePairSpace, aisleShelfHeight)); // right side of pair aisles[currentAisleRow, 2 * currentAislePairColumn + 1] = Instantiate(aisleShelf, new Vector2(pairXPosition + aisleShelfWidth + aislePairSpace, aisleRowYPosition), Quaternion.identity); aisles[currentAisleRow, 2 * currentAislePairColumn + 1].transform.localScale = new Vector2(aisleShelfWidth, aisleShelfHeight); } bool isLastRow = (currentAisleRow == (numAisleRows - 1)); if (isLastRow) { // if this row is the last, add the top space to the navigational graph aisleTop = new Zone("zone above aisles", new Rect(aisleArea.x, aisleRowYPosition + aisleShelfHeight, aisleArea.width, aisleArea.yMax - (aisleRowYPosition + aisleShelfHeight))); } else { // if this row is not the last, add the middle space to the navigational graph aisleMids[currentAisleRow] = new Zone("middle zone above aisle row" + currentAisleRow.ToString(), new Rect(aisleArea.x, aisleRowYPosition + aisleShelfHeight, aisleArea.width, aisleMidSpace)); } bool isFirstRow = (currentAisleRow == 0); // add edges between this row of aisles and other zones in the navigational graph for (int currentAislePairColumn = 0; currentAislePairColumn < numAislePairColumns; currentAislePairColumn++) // can't naturally foreach just one row { Zone aislePair = aislePairs[currentAisleRow, currentAislePairColumn]; // each space between a pair of aisles joins to the zones below and above and nowhere else Rect topEdge = new Rect(aislePair.area.x, aislePair.area.yMax, aislePair.area.width, 0); Rect bottomEdge = new Rect(aislePair.area.x, aislePair.area.yMin, aislePair.area.width, 0); if (isLastRow) { Zone.AddEdgeBetweenZones(topEdge, aislePair, aisleTop); } else { Zone.AddEdgeBetweenZones(topEdge, aislePair, aisleMids[currentAisleRow]); } if (isFirstRow) { Zone.AddEdgeBetweenZones(bottomEdge, aislePair, aisleBottom); } else { Zone.AddEdgeBetweenZones(bottomEdge, aislePair, aisleMids[currentAisleRow - 1]); } // the space between rows that the LAST row added above it } aisleRowYPosition += aisleShelfHeight + aisleMidSpace; // bump Y upwards for next row } lobby = new Zone("lobby", lobbies[0]); // add lobby to navigational graph, conveniently we already know where it is outside = new Zone("outside", new Rect(lobbies[0].x, lobbies[0].y - lobbies[0].height, lobbies[0].width, lobbies[0].height)); // add outside to navigational graph, taking size of lobby but moved down // connect up regions of navigational graph Zone.AddEdgeBetweenZones(entrances[0], lobby, outside); // entrance connects lobby and outside, and is the only way out // to the left of the lobby is the checkouts, adjacent to the top and bottom but not necessarily a lane Rect checkoutTopRightEdge = new Rect(checkoutTop.area.xMax, checkoutTop.area.y, 0, checkoutTop.area.height); Rect checkoutBottomRightEdge = new Rect(checkoutBottom.area.xMax, checkoutBottom.area.y, 0, checkoutBottom.area.height); Zone.AddEdgeBetweenZones(checkoutTopRightEdge, checkoutTop, lobby); Zone.AddEdgeBetweenZones(checkoutBottomRightEdge, checkoutBottom, lobby); // above the lobby is the bottom of the aisles Rect lobbyTopEdge = new Rect(lobby.area.x, lobby.area.yMax, lobby.area.width, 0); Zone.AddEdgeBetweenZones(lobbyTopEdge, aisleBottom, lobby); // above the checkouts is also the bottom of the aisles Rect checkoutTopTopEdge = new Rect(checkoutTop.area.x, checkoutTop.area.yMax, checkoutTop.area.width, 0); Zone.AddEdgeBetweenZones(checkoutTopTopEdge, aisleBottom, checkoutTop); // add all regions to HashSet allShopperZones = new HashSet <Zone>(); allShopperZones.Add(outside); allShopperZones.Add(lobby); allShopperZones.Add(checkoutTop); allShopperZones.Add(checkoutBottom); allShopperZones.UnionWith(checkoutLanes); // iterates over automatically allShopperZones.Add(aisleTop); allShopperZones.Add(aisleBottom); allShopperZones.UnionWith(aisleMids); // iterates over automatically foreach (Zone aislePair in aislePairs) // automatic iteration handles multidimensional arrays properly, but sadly UnionWith doesn't iterate over multidimensional arrays { allShopperZones.Add(aislePair); } }