private void UpdateNextIntersectionDirection(Driver.Sensors sensors) { // We need to be on a street and not having been on a street previously (as we would have already decided where to go). Street currentStreet = sensors.carTracker.street; if (currentStreet != null && previousStreet == null) { // Determine where to go at the end of this street. CardinalDirection currentDirection = sensors.carTracker.streetCardinalDirection; Intersection targetIntersection = GetTargetIntersection(sensors); // See where we might want to go. var potentialDirections = new List <CardinalDirection>(); CardinalDirection oppositeDirection = DirectionHelpers.GetOppositeDirection(currentDirection); foreach (CardinalDirection direction in DirectionHelpers.cardinalDirections) { // Don't try to go backwards by default. if (direction == oppositeDirection) { continue; } // Direction is possible if the intersection has this street. Street street = targetIntersection.GetStreetInDirection(direction); if (street != null) { // Make sure we're not entering into a one-way street from the wrong direction. if (street.isOneWay == false || !targetIntersection.HasStopLineForStreet(street)) { potentialDirections.Add(direction); } } } // Only if no options were there, go back. if (potentialDirections.Count == 0) { potentialDirections.Add(oppositeDirection); } // Choose a random option. int randomIndex = UnityEngine.Random.Range(0, potentialDirections.Count); // Determine exiting values. nextIntersectionExitingCardinalDirection = potentialDirections[randomIndex]; nextIntersectionExitingDirection = DirectionHelpers.cardinalDirectionVectors[nextIntersectionExitingCardinalDirection]; nextIntersectionExitingStreet = targetIntersection.GetStreetInDirection(nextIntersectionExitingCardinalDirection); // Figure out which lane to go into. float angleToTarget = Vector3.SignedAngle(sensors.carTracker.streetDirection, DirectionHelpers.cardinalDirectionVectors[nextIntersectionExitingCardinalDirection], Vector3.up); Street nextStreet = targetIntersection.GetStreetInDirection(nextIntersectionExitingCardinalDirection); if (nextIntersectionExitingCardinalDirection == currentDirection) { // Be in the middle (or right) lane when going straight. nextIntersectionExitingLane = nextStreet.validLanesCount / 2 + 1; nextIntersectionTurningIntent = Driver.TurningIntent.Straight; } else if (nextIntersectionExitingCardinalDirection == oppositeDirection) { // Turn into leftmost lane for U-turns. nextIntersectionExitingLane = 1; nextIntersectionTurningIntent = Driver.TurningIntent.UTurn; } else if (angleToTarget < 0) { // Be in leftmost lane after upcoming left turns. nextIntersectionExitingLane = 1; nextIntersectionTurningIntent = Driver.TurningIntent.Left; } else { // Be in the rightmost lane after upcoming right turns. nextIntersectionExitingLane = nextStreet.validLanesCount; nextIntersectionTurningIntent = Driver.TurningIntent.Right; } nextIntersectionExitingPoint = nextIntersectionExitingStreet.GetCenterOfLanePosition(nextIntersectionExitingLane, 0, nextIntersectionExitingCardinalDirection); // Store entering values. nextIntersectionEnteringCardinalDirection = currentDirection; nextIntersectionEnteringDirection = DirectionHelpers.cardinalDirectionVectors[currentDirection]; nextIntersectionEnteringStreet = currentStreet; // Figure out which lane to enter the intersection from. switch (nextIntersectionTurningIntent) { case Driver.TurningIntent.Straight: // Be in the middle (or right) lane when going straight. nextIntersectionEnteringLane = currentStreet.validLanesCount / 2 + 1; break; case Driver.TurningIntent.Left: // Be in leftmost lane for upcoming left turns. nextIntersectionEnteringLane = 1; break; case Driver.TurningIntent.Right: // Be in the rightmost lane for upcoming right turns. nextIntersectionEnteringLane = currentStreet.validLanesCount; break; case Driver.TurningIntent.UTurn: // Turn from the sidewalk for U-turns. nextIntersectionEnteringLane = 0; break; } nextIntersectionEnteringPoint = nextIntersectionEnteringStreet.GetCenterOfLanePosition(nextIntersectionEnteringLane, nextIntersectionEnteringStreet.length, nextIntersectionEnteringCardinalDirection); } // Update street. previousStreet = currentStreet; }
public void Generate(City city) { // Create the street game object as a child of Streets. gameObject = new GameObject(); gameObject.transform.parent = city.transform.Find("Streets"); // Calculate size and position in the game world. Vector2 startIntersectionHalfSize = startIntersection.GetSize() / 2; Vector2 endIntersectionHalfSize = endIntersection.GetSize() / 2; float roadWidth = lanesCount * City.laneWidth; width = roadWidth + 2 * City.sidewalkWidth; Vector3 boundsOrigin; Vector3 boundsSize; if (orientation == StreetOrientation.EastWest) { gameObject.name = $"Street ({startIntersection.position.x}-{startIntersection.position.x}, {endIntersection.position.y})"; length = endIntersection.position.x - startIntersection.position.x - startIntersectionHalfSize.x - endIntersectionHalfSize.x; gameObject.transform.localPosition = new Vector3 { x = startIntersection.position.x + startIntersectionHalfSize.x, z = startIntersection.position.y + width / 2 }; // Rotate the road so the local Z axis goes in the positive global X direction. gameObject.transform.localRotation = Quaternion.Euler(0, 90, 0); boundsOrigin = gameObject.transform.localPosition; boundsOrigin.z -= width; boundsSize = new Vector3(length, City.boundsHeight, width); } else { gameObject.name = $"Street ({startIntersection.position.x}, {startIntersection.position.y}-{endIntersection.position.y})"; length = endIntersection.position.y - startIntersection.position.y - startIntersectionHalfSize.y - endIntersectionHalfSize.y; gameObject.transform.localPosition = new Vector3 { x = startIntersection.position.x - width / 2, z = startIntersection.position.y + startIntersectionHalfSize.y }; boundsOrigin = gameObject.transform.localPosition; boundsSize = new Vector3(width, City.boundsHeight, length); } // Set bounds. bounds = new Bounds { min = boundsOrigin + new Vector3(0, City.boundsBaseY, 0), max = boundsOrigin + boundsSize }; // Place prefabs. StreetPieces streetPieces = city.streetPieces; GameObject road = streetPieces.Instantiate(streetPieces.roadPrefab, gameObject); road.transform.localScale = new Vector3(roadWidth, 1, length); road.transform.localPosition = new Vector3 { x = City.sidewalkWidth }; GameObject sidewalkLeft = streetPieces.Instantiate(streetPieces.sidewalkPrefab, gameObject); sidewalkLeft.transform.localScale = new Vector3(City.sidewalkWidth, 1, length); GameObject sidewalkRight = streetPieces.Instantiate(streetPieces.sidewalkPrefab, gameObject); sidewalkRight.transform.localScale = new Vector3(City.sidewalkWidth, 1, length); sidewalkRight.transform.localPosition = new Vector3 { x = width - City.sidewalkWidth }; // Place lane division lines. var brokenLineXCoordinates = new List <float>(); if (isOneWay) { brokenLineXCoordinates.Add(width / 2); } else { GameObject centerLine = streetPieces.Instantiate(streetPieces.solidLinePrefab, gameObject); centerLine.transform.localScale = new Vector3(1, 1, length); centerLine.transform.localPosition = new Vector3 { x = width / 2 }; } for (int i = 2; i < lanesCount; i += 2) { float sideOffset = City.sidewalkWidth + City.laneWidth * (i / 2); brokenLineXCoordinates.Add(sideOffset); brokenLineXCoordinates.Add(width - sideOffset); } foreach (float xCoordinate in brokenLineXCoordinates) { GameObject laneLine = streetPieces.Instantiate(streetPieces.brokenLinePrefab, gameObject); laneLine.transform.localScale = new Vector3(1, 1, length); laneLine.transform.localPosition = new Vector3 { x = xCoordinate }; StreetPieces.ChangeBrokenLineTiling(laneLine); } GameObject CreateStopLine() { GameObject stopLine = streetPieces.Instantiate(streetPieces.solidLinePrefab, gameObject); float lineLength = roadWidth; if (!isOneWay) { lineLength /= 2; } stopLine.transform.localScale = new Vector3(1, 1, lineLength); return(stopLine); } // Place intersection stop lines. if (endIntersection.HasStopLineForStreet(this)) { GameObject stopLine = CreateStopLine(); stopLine.transform.localRotation = Quaternion.Euler(0, 90, 0); stopLine.transform.localPosition = new Vector3 { x = City.sidewalkWidth, z = length - City.lineWidth / 2 }; } if (startIntersection.HasStopLineForStreet(this)) { GameObject stopLine = CreateStopLine(); stopLine.transform.localRotation = Quaternion.Euler(0, 270, 0); stopLine.transform.localPosition = new Vector3 { x = width - City.sidewalkWidth, z = City.lineWidth / 2 }; } // Place buildings. void CreateBuilding(float x, float z, float depth, int heightIndex) { GameObject building = streetPieces.Instantiate(streetPieces.buildingPrefab, gameObject); building.transform.localScale = new Vector3(City.buildingWidth, City.buildingHeights[heightIndex], depth); building.transform.localPosition = new Vector3(x, 0, z); } void PlaceBuildings(float z, float depth) { CreateBuilding(-City.buildingWidth, z, depth, Random.Range(0, City.buildingHeights.Length)); CreateBuilding(width, z, depth, Random.Range(0, City.buildingHeights.Length)); } void PlaceBuildingsInRange(float minZ, float maxZ) { // See if we could put 2 or more buildings in this range. if (maxZ - minZ < City.minBuildingLength * 2) { // No, this should be 1 building. PlaceBuildings(minZ, maxZ - minZ); } else { // Yes, split into two parts. float midZ = Random.Range(minZ + City.minBuildingLength, maxZ - City.minBuildingLength); PlaceBuildingsInRange(minZ, midZ); PlaceBuildingsInRange(midZ, maxZ); } } PlaceBuildingsInRange(City.minBuildingLength, length - City.minBuildingLength); }