예제 #1
0
        private void InitializeLane(Street street, int lane, bool directionTowardsEnd)
        {
            float speedInLane    = speedPerLaneMph[lane] * PhysicsHelper.milesPerHourToMetersPerSecond;
            float safetyDistance = safetyDistanceTime * speedInLane;

            var emptySections = new List <(float, float)>();

            emptySections.Add((safetyDistance, street.length - safetyDistance));

            int carsCount = Mathf.RoundToInt(density * street.length + Random.value);

            if (density == 0)
            {
                carsCount = 0;
            }

            for (int i = 0; i < carsCount; i++)
            {
                // Stop if there are no more empty sections.
                if (emptySections.Count == 0)
                {
                    break;
                }

                // Place a car at a random position in a random empty section.
                int emptySectionIndex = Random.Range(0, emptySections.Count);
                (float start, float end) = emptySections[emptySectionIndex];
                emptySections.RemoveAt(emptySectionIndex);

                float position = Mathf.Lerp(start, end, Random.value);
                AddCar(street, lane, position, directionTowardsEnd);

                // Create two new empty sections, if they're long enough.
                if (position - safetyDistance > start)
                {
                    emptySections.Add((start, position - safetyDistance));
                }
                if (position + safetyDistance < end)
                {
                    emptySections.Add((position + safetyDistance, end));
                }
            }
        }
예제 #2
0
        private void StopSimulatingStreet(Street street)
        {
            simulatedStreets.Remove(street);

            foreach (CarTracker carTracker in trafficCarTrackers)
            {
                if (carTracker.street == street)
                {
                    Destroy(carTracker.gameObject);
                    carTrackersToBeRemoved.Add(carTracker);
                }
            }

            foreach (CarTracker carTracker in carTrackersToBeRemoved)
            {
                trafficCarTrackers.Remove(carTracker);
            }

            carTrackersToBeRemoved.Clear();
        }
예제 #3
0
        private void StartSimulatingStreet(Street street)
        {
            simulatedStreets.Add(street);

            // Initialize traffic on this street.
            if (street.isOneWay)
            {
                for (int lane = 1; lane <= street.lanesCount; lane++)
                {
                    InitializeLane(street, lane, !street.oneWayDirectionGoesToStart);
                }
            }
            else
            {
                for (int lane = 1; lane <= street.lanesCount / 2; lane++)
                {
                    InitializeLane(street, lane, true);
                    InitializeLane(street, lane, false);
                }
            }
        }
예제 #4
0
        public Vector2 GetSize()
        {
            int widthLanesCount  = 2;
            int heightLanesCount = 2;

            Street northSouthStreet = northStreet ?? southStreet;
            Street eastWestStreet   = eastStreet ?? westStreet;

            if (northSouthStreet != null)
            {
                widthLanesCount = northSouthStreet.lanesCount;
            }

            if (eastWestStreet != null)
            {
                heightLanesCount = eastWestStreet.lanesCount;
            }

            float width  = 2 * City.sidewalkWidth + widthLanesCount * City.laneWidth;
            float height = 2 * City.sidewalkWidth + heightLanesCount * City.laneWidth;

            return(new Vector2(width, height));
        }
예제 #5
0
        private void AddCar(Street street, int lane, float positionAlongStreet, bool directionTowardsEnd)
        {
            Vector3 positionStreet;
            Vector3 directionStreet;

            float sidePosition = City.sidewalkWidth + (lane - 0.5f) * City.laneWidth;

            if (directionTowardsEnd)
            {
                positionStreet  = new Vector3(sidePosition, 0, positionAlongStreet);
                directionStreet = Vector3.forward;
            }
            else
            {
                positionStreet  = new Vector3(street.width - sidePosition, 0, street.length - positionAlongStreet);
                directionStreet = Vector3.back;
            }

            Vector3    position  = street.transform.TransformPoint(positionStreet);
            Vector3    direction = street.transform.TransformDirection(directionStreet);
            Quaternion rotation  = Quaternion.LookRotation(direction, Vector3.up);

            GameObject car = Instantiate(trafficCarPrefab, position, rotation, carTrafficRoot);

            CarTracker carTracker = car.GetComponent <CarTracker>();

            carTracker.city = city;
            trafficCarTrackers.Add(carTracker);

            CarAudio carAudio = car.GetComponent <CarAudio>();

            carAudio.gameMode = gameMode;

            float initialSpeed = speedPerLaneMph[lane] * PhysicsHelper.milesPerHourToMetersPerSecond;

            car.GetComponent <Car>().InitializeSpeed(initialSpeed);
        }
예제 #6
0
        protected Street AddStreet(Vector2Int fromBlock, Vector2Int toBlock, int lanesCount, bool isOneWay = false)
        {
            // Assert lane restrictions.
            if (lanesCount != 2 && lanesCount != 4 && lanesCount != 6)
            {
                Debug.LogError($"Street with invalid lanes count ({lanesCount}) added.");
                return(null);
            }

            if (isOneWay && lanesCount != 2)
            {
                Debug.LogError($"One way streets can only have 2 lanes.");
                return(null);
            }

            // Create the street.
            var street = new Street
            {
                lanesCount = lanesCount,
                isOneWay   = isOneWay
            };

            streets.Add(street);

            // Determine one way direction and set start/end locations (converted from blocks to meters).
            Vector2Int start = fromBlock * 100;
            Vector2Int end   = toBlock * 100;

            void FlipDirection()
            {
                (start, end) = (end, start);
                if (isOneWay)
                {
                    street.oneWayDirectionGoesToStart = true;
                }
            }

            if (fromBlock.x == toBlock.x)
            {
                // This is a N-S road. Flip direction if necessary.
                if (fromBlock.y > toBlock.y)
                {
                    FlipDirection();
                }
            }
            else
            {
                // This is an E-W road. Flip direction if necessary.
                if (fromBlock.x > toBlock.x)
                {
                    FlipDirection();
                }
            }

            // Create intersections if necessary and assign them to the street.
            Intersection CreateIntersection(Vector2Int location)
            {
                var intersection = new Intersection {
                    position = location
                };

                intersections[location] = intersection;
                return(intersection);
            }

            street.startIntersection = intersections.ContainsKey(start) ? intersections[start] : CreateIntersection(start);
            street.endIntersection   = intersections.ContainsKey(end) ? intersections[end] : CreateIntersection(end);

            // Assign the streets to the intersections.
            if (fromBlock.x == toBlock.x)
            {
                // This is a N-S road.
                street.startIntersection.northStreet = street;
                street.endIntersection.southStreet   = street;
            }
            else
            {
                // This is an E-W road.
                street.startIntersection.eastStreet = street;
                street.endIntersection.westStreet   = street;
            }

            return(street);
        }
예제 #7
0
        public int GetLanesCountInDrection(CardinalDirection direction)
        {
            Street street = GetStreetInOrientation(DirectionHelpers.cardinalDirectionStreetOrientations[direction]);

            return(street != null ? street.lanesCount : 2);
        }
예제 #8
0
        public float GetStreetWidthInDrection(CardinalDirection direction)
        {
            Street street = GetStreetInOrientation(DirectionHelpers.cardinalDirectionStreetOrientations[direction]);

            return(street != null ? street.width : (City.sidewalkWidth + City.laneWidth) * 2);
        }
예제 #9
0
        public bool HasStopLineForStreet(Street street)
        {
            if (isFourWayIntersection)
            {
                // Intersections with traffic lights (4-6 lanes) always have stop lines.
                if (hasTrafficLights)
                {
                    return(true);
                }

                // When two 4-lane streets cross, east-west has priority;
                Street northSouthStreet = northStreet ?? southStreet;
                Street eastWestStreet   = eastStreet ?? westStreet;

                if (northSouthStreet.lanesCount == 4 && eastWestStreet.lanesCount == 4)
                {
                    return(street == northStreet || street == southStreet);
                }

                // When two one-way streets cross, east-west has priority;
                if (northSouthStreet.isOneWay && eastWestStreet.isOneWay)
                {
                    return(street == northStreet || street == southStreet);
                }

                // One-way streets crossing non-one way streets always have stop signs if entering the intersection.
                if (street.isOneWay)
                {
                    bool streetIsEnteringIfGointToStart = street == northStreet || street == eastStreet;
                    return(streetIsEnteringIfGointToStart && street.oneWayDirectionGoesToStart);
                }

                // Street with 2 lanes always has a stop sign except if the other is a one-way street.
                if (street.lanesCount == 2)
                {
                    if (street == northStreet || street == southStreet)
                    {
                        return(!eastWestStreet.isOneWay);
                    }
                    else
                    {
                        return(!northSouthStreet.isOneWay);
                    }
                }

                // This must be a higher priority road so it has no stop sign.
                return(false);
            }
            else if (isTIntersection)
            {
                bool hasStopSign = false;

                // Street that doesn't continue has the stop sign.
                if (street == northStreet && southStreet == null)
                {
                    hasStopSign = true;
                }
                if (street == southStreet && northStreet == null)
                {
                    hasStopSign = true;
                }
                if (street == eastStreet && westStreet == null)
                {
                    hasStopSign = true;
                }
                if (street == westStreet && eastStreet == null)
                {
                    hasStopSign = true;
                }

                if (hasStopSign)
                {
                    // Make sure this is not a one-way street that is exiting the intersection.
                    if (street.isOneWay)
                    {
                        bool streetIsEnteringIfGointToStart = street == northStreet || street == eastStreet;
                        return(streetIsEnteringIfGointToStart && street.oneWayDirectionGoesToStart);
                    }
                    else
                    {
                        return(true);
                    }
                }

                return(false);
            }
            else
            {
                return(false);
            }
        }
예제 #10
0
        public void Generate(City city)
        {
            // Create the intersection game object as a child of Intersections.
            gameObject = new GameObject($"Intersection ({position.x}, {position.y})");
            gameObject.transform.parent = city.transform.Find("Intersections");

            // Position in the game world.
            Vector2 size = GetSize();

            gameObject.transform.localPosition = new Vector3
            {
                x = position.x - size.x / 2,
                z = position.y - size.y / 2
            };

            // Set bounds.
            bounds = new Bounds
            {
                center = new Vector3(position.x, City.boundsBaseY + City.boundsHeight / 2, position.y),
                size   = new Vector3(size.x, City.boundsHeight, size.y)
            };

            // Place prefabs.
            StreetPieces streetPieces = city.streetPieces;

            GameObject road = streetPieces.Instantiate(streetPieces.roadPrefab, gameObject);

            road.transform.localScale = new Vector3(size.x, 1, size.y);

            // Place sidewalk corners or sides.
            void CreateCorner(float rotation, float x, float z)
            {
                GameObject corner = streetPieces.Instantiate(streetPieces.sidewalkCornerPrefab, gameObject);

                corner.transform.localScale    = new Vector3(City.sidewalkWidth, 1, City.sidewalkWidth);
                corner.transform.localRotation = Quaternion.Euler(0, rotation, 0);
                corner.transform.localPosition = new Vector3 {
                    x = x, z = z
                };
            }

            if (southStreet != null && westStreet != null)
            {
                CreateCorner(0, 0, 0);
            }
            if (northStreet != null && westStreet != null)
            {
                CreateCorner(90, 0, size.y);
            }
            if (northStreet != null && eastStreet != null)
            {
                CreateCorner(180, size.x, size.y);
            }
            if (southStreet != null && eastStreet != null)
            {
                CreateCorner(270, size.x, 0);
            }

            if (northStreet == null)
            {
                GameObject sidewalk = streetPieces.Instantiate(streetPieces.sidewalkPrefab, gameObject);
                sidewalk.transform.localScale    = new Vector3(size.x, 1, City.sidewalkWidth);
                sidewalk.transform.localPosition = new Vector3 {
                    z = size.y - City.sidewalkWidth
                };
            }

            if (southStreet == null)
            {
                GameObject sidewalk = streetPieces.Instantiate(streetPieces.sidewalkPrefab, gameObject);
                sidewalk.transform.localScale = new Vector3(size.x, 1, City.sidewalkWidth);
            }

            if (eastStreet == null)
            {
                GameObject sidewalk = streetPieces.Instantiate(streetPieces.sidewalkPrefab, gameObject);
                sidewalk.transform.localScale    = new Vector3(City.sidewalkWidth, 1, size.y);
                sidewalk.transform.localPosition = new Vector3 {
                    x = size.x - City.sidewalkWidth
                };
            }

            if (westStreet == null)
            {
                GameObject sidewalk = streetPieces.Instantiate(streetPieces.sidewalkPrefab, gameObject);
                sidewalk.transform.localScale = new Vector3(City.sidewalkWidth, 1, size.y);
            }

            // Place lines if one street has priority.
            bool hasLinesNorthSouth = false;
            bool hasLinesEastWest   = false;

            Street northSouthStreet = northStreet ?? southStreet;
            Street eastWestStreet   = eastStreet ?? westStreet;

            if (isFourWayIntersection)
            {
                // 4-way intersections have lines where there are no stop signs on both ends.
                hasLinesEastWest   = !HasStopLineForStreet(eastStreet) && !HasStopLineForStreet(westStreet);
                hasLinesNorthSouth = !HasStopLineForStreet(northStreet) && !HasStopLineForStreet(southStreet);
            }
            else if (isTIntersection)
            {
                // T intersections always have priority on the street that doesn't end.
                hasLinesEastWest   = northStreet == null || southStreet == null;
                hasLinesNorthSouth = eastStreet == null || westStreet == null;
            }

            if (hasLinesNorthSouth)
            {
                var brokenLineXCoordinates = new List <float>();

                if (northSouthStreet.isOneWay)
                {
                    brokenLineXCoordinates.Add(size.x / 2);
                }
                else
                {
                    GameObject centerLine = streetPieces.Instantiate(streetPieces.solidLinePrefab, gameObject);
                    centerLine.transform.localScale    = new Vector3(1, 1, size.y);
                    centerLine.transform.localPosition = new Vector3 {
                        x = size.x / 2
                    };
                }

                for (int i = 2; i < northSouthStreet.lanesCount; i += 2)
                {
                    float sideOffset = City.sidewalkWidth + City.laneWidth * (i / 2);
                    brokenLineXCoordinates.Add(sideOffset);
                    brokenLineXCoordinates.Add(size.x - sideOffset);
                }

                foreach (float xCoordinate in brokenLineXCoordinates)
                {
                    GameObject laneLine = streetPieces.Instantiate(streetPieces.brokenLinePrefab, gameObject);
                    laneLine.transform.localScale    = new Vector3(1, 1, size.y);
                    laneLine.transform.localPosition = new Vector3 {
                        x = xCoordinate
                    };
                    StreetPieces.ChangeBrokenLineTiling(laneLine);
                }
            }

            if (hasLinesEastWest)
            {
                var brokenLineZCoordinates = new List <float>();

                if (eastWestStreet.isOneWay)
                {
                    brokenLineZCoordinates.Add(size.y / 2);
                }
                else
                {
                    GameObject centerLine = streetPieces.Instantiate(streetPieces.solidLinePrefab, gameObject);
                    centerLine.transform.localScale    = new Vector3(1, 1, size.x);
                    centerLine.transform.localRotation = Quaternion.Euler(0, 90, 0);
                    centerLine.transform.localPosition = new Vector3 {
                        z = size.y / 2
                    };
                }

                for (int i = 2; i < eastWestStreet.lanesCount; i += 2)
                {
                    float sideOffset = City.sidewalkWidth + City.laneWidth * (i / 2);
                    brokenLineZCoordinates.Add(sideOffset);
                    brokenLineZCoordinates.Add(size.y - sideOffset);
                }

                foreach (float zCoordinate in brokenLineZCoordinates)
                {
                    GameObject laneLine = streetPieces.Instantiate(streetPieces.brokenLinePrefab, gameObject);
                    laneLine.transform.localScale    = new Vector3(1, 1, size.x);
                    laneLine.transform.localRotation = Quaternion.Euler(0, 90, 0);
                    laneLine.transform.localPosition = new Vector3 {
                        z = zCoordinate
                    };
                    StreetPieces.ChangeBrokenLineTiling(laneLine);
                }
            }

            // Place buildings in 4 corners.
            void CreateBuilding(float x, float z, float width = City.minBuildingLength, float depth = City.minBuildingLength)
            {
                GameObject building = streetPieces.Instantiate(streetPieces.buildingPrefab, gameObject);

                building.transform.localScale    = new Vector3(width, City.buildingHeights[1], depth);
                building.transform.localPosition = new Vector3(x, 0, z);
            }

            CreateBuilding(-City.minBuildingLength, -City.minBuildingLength);
            CreateBuilding(-City.minBuildingLength, size.y);
            CreateBuilding(size.x, -City.minBuildingLength);
            CreateBuilding(size.x, size.y);

            // Place buildings in directions where there is no street.
            if (northStreet == null)
            {
                CreateBuilding(0, size.y, width: size.x);
            }
            if (southStreet == null)
            {
                CreateBuilding(0, -City.minBuildingLength, width: size.x);
            }
            if (eastStreet == null)
            {
                CreateBuilding(size.x, 0, depth: size.y);
            }
            if (westStreet == null)
            {
                CreateBuilding(-City.minBuildingLength, 0, depth: size.y);
            }
        }
예제 #11
0
        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;
        }
예제 #12
0
        private void UpdateTargetLaneAndDirection(Driver.Sensors sensors, Driver.Actuators actuators)
        {
            Street       currentStreet          = sensors.carTracker.street;
            Intersection currentIntersection    = sensors.carTracker.intersection;
            float        distanceToIntersection = GetDistanceToTargetIntersection(sensors);
            float        turningTargetDistance  = Math.Max(turningTargetMinimumDistance, turningTargetTime * sensors.car.speed);

            // See if we should start performing a turn.
            bool performingTurn = false;

            switch (nextIntersectionTurningIntent)
            {
            case Driver.TurningIntent.Left:
            case Driver.TurningIntent.Right:
                // When turning left and right, start turning when close enough to the intersection.
                performingTurn = currentIntersection != null || distanceToIntersection < turningTargetDistance;
                break;

            case Driver.TurningIntent.UTurn:
                // When performing a U-turn, start in the intersection.
                performingTurn = currentIntersection != null;
                break;
            }

            if (performingTurn)
            {
                DrawDebugIntersectionArc();

                switch (nextIntersectionTurningIntent)
                {
                case Driver.TurningIntent.Left:
                case Driver.TurningIntent.Right:
                    // For right turns, follow an arc.
                    float xEnter = Vector3.Dot(nextIntersectionExitingDirection, nextIntersectionEnteringPoint);
                    float xExit  = Vector3.Dot(nextIntersectionExitingDirection, nextIntersectionExitingPoint);
                    float xDelta = xExit - xEnter;

                    float zEnter = Vector3.Dot(nextIntersectionEnteringDirection, nextIntersectionEnteringPoint);
                    float zExit  = Vector3.Dot(nextIntersectionEnteringDirection, nextIntersectionExitingPoint);
                    float zDelta = zExit - zEnter;

                    // Place a target in front of the car.
                    Vector3 carPosition = sensors.car.transform.position;
                    Vector3 target      = carPosition + sensors.car.transform.forward * turningTargetDistance;
                    Debug.DrawLine(carPosition + Vector3.up * 2, target + Vector3.up * 2, Color.magenta);

                    float xTarget = Vector3.Dot(nextIntersectionExitingDirection, target) - xEnter;
                    float zTarget = Vector3.Dot(nextIntersectionEnteringDirection, target) - zEnter;

                    // Move target onto the arc.
                    float zTargetFraction = zTarget / zDelta;
                    float xTargetFraction = xTarget / xDelta;

                    float angle = Mathf.Atan2(zTargetFraction, 1 - xTargetFraction);

                    xTargetFraction = 1 - Mathf.Cos(angle);
                    zTargetFraction = xTargetFraction > 1 ? 1 : Mathf.Sin(angle);

                    xTarget = xTargetFraction * xDelta;
                    zTarget = zTargetFraction * zDelta;
                    target  = nextIntersectionEnteringPoint + nextIntersectionExitingDirection * xTarget + nextIntersectionEnteringDirection * zTarget;

                    // Direct the car towards the adjusted target.
                    actuators.targetDirection = (target - carPosition).normalized;

                    Debug.DrawLine(carPosition + Vector3.up * 2, target + Vector3.up * 2, Color.blue);

                    break;

                case Driver.TurningIntent.UTurn:
                    // For U-turns, start by turning right as tight as possible.
                    if (Vector3.Angle(nextIntersectionEnteringDirection, sensors.car.transform.forward) < 45)
                    {
                        actuators.targetDirection = Quaternion.AngleAxis(90, Vector3.up) * nextIntersectionEnteringDirection;
                    }
                    else
                    {
                        actuators.targetDirection = nextIntersectionExitingDirection;
                    }
                    break;
                }

                actuators.targetLane = nextIntersectionExitingLane;
            }
            else if (currentStreet != null)
            {
                // If we're on a street, go to correct turning lane.
                float angleToTarget = Vector3.SignedAngle(sensors.carTracker.streetDirection, DirectionHelpers.cardinalDirectionVectors[nextIntersectionExitingCardinalDirection], Vector3.up);

                CardinalDirection currentDirection  = sensors.carTracker.streetCardinalDirection;
                CardinalDirection oppositeDirection = DirectionHelpers.GetOppositeDirection(currentDirection);

                int currentLane = sensors.carTracker.currentLane;
                int desiredLane = nextIntersectionEnteringLane;

                if (desiredLane != currentLane)
                {
                    // Start moving to the desired lane when close enough to the intersection.
                    int   laneDifference = desiredLane - currentLane;
                    float minimumDistanceToIntersection = sensors.driverProfile.distanceForChangingLane * Mathf.Abs(laneDifference);

                    // For U-turns from the sidewalk, only drive onto the sidewalk in the last meters.
                    if (nextIntersectionTurningIntent == Driver.TurningIntent.UTurn && currentLane == 1)
                    {
                        minimumDistanceToIntersection = uTurnDriveToSidewalkDistance;
                    }

                    if (distanceToIntersection < minimumDistanceToIntersection)
                    {
                        actuators.turningIntent = nextIntersectionTurningIntent;

                        // Make sure the neighbor lane is free in the safe distance region before and after the car.
                        int neighborLane = currentLane + Math.Sign(laneDifference);

                        float   safeDistance    = CalculateSafeDistance(sensors);
                        Vector3 streetDirection = sensors.carTracker.streetDirection;
                        float   carLength       = sensors.car.bounds.size.z;
                        Vector3 origin          = sensors.carTracker.GetCenterOfLanePosition(neighborLane) + (safeDistance + carLength / 2) * streetDirection + Vector3.up * sensors.car.bounds.extents.y;
                        float   checkDistance   = 2 * safeDistance + carLength;

                        if (!Physics.Raycast(origin, -streetDirection, checkDistance))
                        {
                            actuators.targetLane = desiredLane;
                            Debug.DrawRay(origin, -streetDirection * checkDistance, Color.green, 1);
                        }
                        else
                        {
                            Debug.DrawRay(origin, -streetDirection * checkDistance, Color.red);
                        }
                    }
                }

                actuators.targetDirection = sensors.carTracker.streetDirection;
            }
        }
예제 #13
0
        private void Update()
        {
            // See if game camera is in the same location as before.
            CarTracker focusedCarTracker = gameMode.currentCamera.trackedCar;

            if (focusedCarTracker.street != null && focusedCarTracker.street != focusedStreet || focusedCarTracker.intersection != null && focusedCarTracker.intersection != focusedIntersection)
            {
                // We have moved to a different street or intersection so we need to recalculate the simulated streets and intersections.
                focusedIntersection = focusedCarTracker.intersection;
                focusedStreet       = focusedCarTracker.street;

                void AddIntersectionVicinity(Intersection intersection, int distance)
                {
                    if (distance == 0)
                    {
                        return;
                    }

                    newSimulatedIntersections.Add(intersection);
                    if (intersection.northStreet != null)
                    {
                        AddStreetVicinity(intersection.northStreet, distance);
                    }
                    if (intersection.southStreet != null)
                    {
                        AddStreetVicinity(intersection.southStreet, distance);
                    }
                    if (intersection.eastStreet != null)
                    {
                        AddStreetVicinity(intersection.eastStreet, distance);
                    }
                    if (intersection.westStreet != null)
                    {
                        AddStreetVicinity(intersection.westStreet, distance);
                    }
                }

                void AddStreetVicinity(Street street, int distance)
                {
                    if (distance == 0)
                    {
                        return;
                    }

                    newSimulatedStreets.Add(street);
                    AddIntersectionVicinity(street.startIntersection, distance - 1);
                    AddIntersectionVicinity(street.endIntersection, distance - 1);
                }

                if (focusedStreet != null)
                {
                    AddStreetVicinity(focusedStreet, 3);
                }
                else
                {
                    AddIntersectionVicinity(focusedIntersection, 2);
                }

                // Update simulated streets.
                foreach (Street street in simulatedStreets)
                {
                    if (!newSimulatedStreets.Contains(street))
                    {
                        // This street is no longer being simulated, so we can remove it, including all the cars left in it.
                        streetsToBeStopped.Add(street);
                    }
                }

                foreach (Street street in streetsToBeStopped)
                {
                    StopSimulatingStreet(street);
                }

                streetsToBeStopped.Clear();

                foreach (Street street in newSimulatedStreets)
                {
                    if (!simulatedStreets.Contains(street))
                    {
                        // This street isn't being simulated, so start doing it.
                        StartSimulatingStreet(street);
                    }
                }

                newSimulatedStreets.Clear();

                // Update simulated intersections.
                foreach (Intersection intersection in simulatedIntersections)
                {
                    if (!newSimulatedIntersections.Contains(intersection))
                    {
                        // This street is no longer being simulated, so we can remove it, including all the cars left in it.
                        intersectionsToBeStopped.Add(intersection);
                    }
                }

                foreach (Intersection intersection in intersectionsToBeStopped)
                {
                    StopSimulatingIntersection(intersection);
                }

                intersectionsToBeStopped.Clear();

                foreach (Intersection intersection in newSimulatedIntersections)
                {
                    if (!simulatedIntersections.Contains(intersection))
                    {
                        // This street isn't being simulated, so start doing it.
                        StartSimulatingIntersection(intersection);
                    }
                }

                newSimulatedIntersections.Clear();
            }

            // Remove cars that left the simulation area.
            foreach (CarTracker carTracker in trafficCarTrackers)
            {
                if (carTracker.street != null && !simulatedStreets.Contains(carTracker.street) ||
                    carTracker.intersection != null && !simulatedIntersections.Contains(carTracker.intersection))
                {
                    Destroy(carTracker.gameObject);
                    carTrackersToBeRemoved.Add(carTracker);
                }
            }

            foreach (CarTracker carTracker in carTrackersToBeRemoved)
            {
                trafficCarTrackers.Remove(carTracker);
            }

            carTrackersToBeRemoved.Clear();

            // Add cars at the edges of the simulation areas.
            foreach (Street street in simulatedStreets)
            {
                if (street.isOneWay)
                {
                    if (street.oneWayDirectionGoesToStart)
                    {
                        if (!simulatedIntersections.Contains(street.endIntersection))
                        {
                            SimulateStreetEntrance(street, false);
                        }
                    }
                    else
                    {
                        if (!simulatedIntersections.Contains(street.startIntersection))
                        {
                            SimulateStreetEntrance(street, true);
                        }
                    }
                }
                else
                {
                    if (!simulatedIntersections.Contains(street.endIntersection))
                    {
                        SimulateStreetEntrance(street, false);
                    }

                    if (!simulatedIntersections.Contains(street.startIntersection))
                    {
                        SimulateStreetEntrance(street, true);
                    }
                }
            }
        }