using Gley.UrbanSystem.Internal; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Gley.TrafficSystem.Internal { /// /// Performs waypoint operations /// internal class WaypointManager : IDestroyable { private readonly Dictionary _playerTarget; private readonly int[] _target; //contains at index the waypoint index of the target waypoint of that agent. Agent at position 2 has the target waypoint index target[2] private readonly bool[] _hasPath; private readonly Dictionary> _pathToDestination; private readonly GridDataHandler _gridDataHandler; private readonly TrafficWaypointsDataHandler _trafficWaypointsDataHandler; private List _disabledWaypoints; private SpawnWaypointSelector _spawnWaypointSelector; private bool _debugGiveWay; internal WaypointManager(TrafficWaypointsDataHandler trafficWaypointsDataHandler, GridDataHandler gridDataHandler, int nrOfVehicles, SpawnWaypointSelector spawnWaypointSelector, bool debugGIveWay) { _trafficWaypointsDataHandler = trafficWaypointsDataHandler; _gridDataHandler = gridDataHandler; Assign(); WaypointEvents.onTrafficLightChanged += TrafficLightChanged; _playerTarget = new Dictionary(); _target = new int[nrOfVehicles]; for (int i = 0; i < _target.Length; i++) { _target[i] = TrafficSystemConstants.INVALID_VEHICLE_INDEX; } _pathToDestination = new Dictionary>(); _hasPath = new bool[nrOfVehicles]; _disabledWaypoints = new List(); _debugGiveWay = debugGIveWay; SetSpawnWaypointSelector(spawnWaypointSelector); } public void Assign() { DestroyableManager.Instance.Register(this); } internal int[] GetTargetWaypoints() { return _target; } /// /// Get Target waypoint index of agent /// /// /// internal int GetTargetWaypointIndex(int agentIndex) { return _target[agentIndex]; } /// /// Get orientation of the waypoint /// /// /// internal Quaternion GetNextOrientation(int waypointIndex) { if (!_trafficWaypointsDataHandler.HasNeighbors(waypointIndex)) { return Quaternion.identity; } return Quaternion.LookRotation( _trafficWaypointsDataHandler.GetPosition(_trafficWaypointsDataHandler.GetNeighbors(waypointIndex)[0]) - _trafficWaypointsDataHandler.GetPosition(waypointIndex) ); } /// /// Get orientation of the waypoint /// /// /// internal Quaternion GetPrevOrientation(int waypointIndex) { if (!_trafficWaypointsDataHandler.HasPrevs(waypointIndex)) { return Quaternion.identity; } return Quaternion.LookRotation( _trafficWaypointsDataHandler.GetPosition(waypointIndex) - _trafficWaypointsDataHandler.GetPosition(_trafficWaypointsDataHandler.GetPrevs(waypointIndex)[0]) ); } /// /// Enables unavailable waypoints /// internal void EnableAllWaypoints() { _trafficWaypointsDataHandler.SetTemperaryDisabledValue(_disabledWaypoints, false); _disabledWaypoints = new List(); } /// /// Mark a waypoint as disabled /// /// internal void AddDisabledWaypoint(int waypointIndex) { _disabledWaypoints.Add(waypointIndex); _trafficWaypointsDataHandler.SetTemperaryDisabledValue(waypointIndex, true); } internal List GetDisabledWaypoints() { return _disabledWaypoints; } /// /// Get a free waypoint connected to the current one /// /// agent that requested the waypoint /// type of the agent that requested the waypoint /// internal int GetCurrentLaneWaypointIndex(int agentIndex, VehicleTypes vehicleType) { int waypointIndex = PeekPoint(agentIndex); if (waypointIndex != -1) { return waypointIndex; } int oldWaypointIndex = GetTargetWaypointIndex(agentIndex); //check direct neighbors if (_trafficWaypointsDataHandler.HasNeighbors(oldWaypointIndex)) { List possibleWaypoints = _trafficWaypointsDataHandler.GetNeighborsWithConditions(oldWaypointIndex, vehicleType); if (possibleWaypoints.Count > 0) { waypointIndex = possibleWaypoints[Random.Range(0, possibleWaypoints.Count)]; } } //check other lanes if (waypointIndex == -1) { if (_trafficWaypointsDataHandler.HasOtherLanes(oldWaypointIndex)) { List possibleWaypoints = _trafficWaypointsDataHandler.GetOtherLanesWithConditions(oldWaypointIndex, vehicleType); if (possibleWaypoints.Count > 0) { waypointIndex = possibleWaypoints[Random.Range(0, possibleWaypoints.Count)]; } } } //check neighbors that are not allowed if (waypointIndex == -1) { if (_trafficWaypointsDataHandler.HasNeighbors(oldWaypointIndex)) { List possibleWaypoints = _trafficWaypointsDataHandler.GetNeighborsWithConditions(oldWaypointIndex); if (possibleWaypoints.Count > 0) { waypointIndex = possibleWaypoints[Random.Range(0, possibleWaypoints.Count)]; } } } //check other lanes that are not allowed if (waypointIndex == -1) { if (_trafficWaypointsDataHandler.HasOtherLanes(oldWaypointIndex)) { List possibleWaypoints = _trafficWaypointsDataHandler.GetOtherLanesWithConditions(oldWaypointIndex); if (possibleWaypoints.Count > 0) { waypointIndex = possibleWaypoints[Random.Range(0, possibleWaypoints.Count)]; } } } return waypointIndex; } internal int GetAgentIndexAtTarget(int waypointIndex) { for (int i = 0; i < _target.Length; i++) { if (_target[i] == waypointIndex) { return i; } } return -1; } internal void RegisterPlayer(int id, int waypointIndex) { if (!_playerTarget.ContainsKey(id)) { _playerTarget.Add(id, waypointIndex); } } internal void UpdatePlayerWaypoint(int id, int waypointIndex) { _playerTarget[id] = waypointIndex; } /// /// Check if waypoint is a target for another agent /// /// /// internal bool IsThisWaypointATarget(int waypointIndex) { for (int i = 0; i < _target.Length; i++) { if (_target[i] == waypointIndex) { return true; } } return _playerTarget.ContainsValue(waypointIndex); } internal bool AreTheseWaypointsATarget(int[] waypointsToCheck) { return _target.Intersect(waypointsToCheck).Any() || _playerTarget.Values.Any(v => waypointsToCheck.Contains(v)); } internal bool HaveCommonNeighbors(int fromWaypointIndex, int toWaypointIndex, int level = 0) { if (level == 0) { if (_trafficWaypointsDataHandler.GetNeighbors(fromWaypointIndex).Intersect(_trafficWaypointsDataHandler.GetNeighbors(toWaypointIndex)).Any()) { return true; } else { return false; } } return false; } /// /// Set next waypoint and trigger the required events /// /// /// internal void SetNextWaypoint(int vehicleIndex, int waypointIndex) { bool stop = false; if (_hasPath[vehicleIndex]) { Queue queue; if (_pathToDestination.TryGetValue(vehicleIndex, out queue)) { queue.Dequeue(); if (queue.Count == 0) { stop = true; } } } SetTargetWaypoint(vehicleIndex, waypointIndex); int targetWaypointIndex = GetTargetWaypointIndex(vehicleIndex); if (stop == true) { WaypointEvents.TriggerStopStateChangedEvent(vehicleIndex, true); } if (_trafficWaypointsDataHandler.IsStop(targetWaypointIndex)) { WaypointEvents.TriggerStopStateChangedEvent(vehicleIndex, true); } if (_trafficWaypointsDataHandler.IsGiveWay(targetWaypointIndex)) { WaypointEvents.TriggerGiveWayStateChangedEvent(vehicleIndex, true); } if (_trafficWaypointsDataHandler.IsComplexGiveWay(targetWaypointIndex)) { WaypointEvents.TriggerGiveWayStateChangedEvent(vehicleIndex, true); } } /// /// Remove target waypoint for the agent at index /// /// internal void RemoveAgent(int agentIndex) { //MarkWaypointAsPassed(agentIndex); SetTargetWaypointIndex(agentIndex, -1); } /// /// Directly set the target waypoint for the vehicle at index. /// Used to set first waypoint after vehicle initialization /// /// /// internal void SetTargetWaypoint(int agentIndex, int waypointIndex) { MarkWaypointAsPassed(agentIndex); SetTargetWaypointIndex(agentIndex, waypointIndex); } internal bool CanContinueStraight(int vehicleIndex, VehicleTypes vehicleType) { int targetWaypointIndex = GetTargetWaypointIndex(vehicleIndex); if (_trafficWaypointsDataHandler.HasNeighbors(targetWaypointIndex)) { if (_hasPath[vehicleIndex]) { Queue queue; if (_pathToDestination.TryGetValue(vehicleIndex, out queue)) { if (queue.Count > 0) { int nextWaypoint = queue.Peek(); if (!_trafficWaypointsDataHandler.HasWaypointInNeighbors(targetWaypointIndex, nextWaypoint)) { return false; } } } } if (_trafficWaypointsDataHandler.HasNeighborsForVehicleType(targetWaypointIndex, vehicleType)) { return true; } } return false; } /// /// Check if can switch to target waypoint /// /// /// internal bool CanEnterIntersection(int vehicleIndex) { int waypointIdex = GetTargetWaypointIndex(vehicleIndex); var intersections = _trafficWaypointsDataHandler.GetAssociatedIntersections(waypointIdex); foreach (var intersection in intersections) { if (intersection.IsPathFree(waypointIdex) == false) { return false; } } return true; } /// /// Check if the previous waypoints are free /// /// /// /// /// internal bool AllPreviousWaypointsAreFree(int vehicleIndex, float distance, int waypointToCheck, ref int incomingCarIndex) { return IsTargetFree(waypointToCheck, distance, GetTargetWaypointIndex(vehicleIndex), vehicleIndex, ref incomingCarIndex); } /// /// Check what vehicle is in front /// /// /// /// /// 1-> if 1 is in front of 2 /// 2-> if 2 is in front of 1 /// 0-> if it is not possible to determine /// internal int IsInFront(int vehicleIndex1, int vehicleIndex2) { //compares waypoints to determine which vehicle is in front int distance = 0; int[] neighbors = _trafficWaypointsDataHandler.GetNeighbors(GetTargetWaypointIndex(vehicleIndex1)); //if no neighbors are available -> not possible to determine if (neighbors.Length == 0) { return 0; } //check next 10 waypoints to find waypoint 2 int startWaypointIndex = neighbors[0]; while (startWaypointIndex != GetTargetWaypointIndex(vehicleIndex2) && distance < 10) { distance++; if (!_trafficWaypointsDataHandler.HasNeighbors(startWaypointIndex)) { //if not found -> not possible to determine return 0; } startWaypointIndex = _trafficWaypointsDataHandler.GetNeighbors(startWaypointIndex)[0]; } int distance2 = 0; neighbors = _trafficWaypointsDataHandler.GetNeighbors(GetTargetWaypointIndex(vehicleIndex2)); if (neighbors.Length == 0) { return 0; } startWaypointIndex = neighbors[0]; while (startWaypointIndex != GetTargetWaypointIndex(vehicleIndex1) && distance2 < 10) { distance2++; if (!_trafficWaypointsDataHandler.HasNeighbors(startWaypointIndex)) { //if not found -> not possible to determine return 0; } startWaypointIndex = _trafficWaypointsDataHandler.GetNeighbors(startWaypointIndex)[0]; } //if no waypoints found -> not possible to determine if (distance == 10 && distance2 == 10) { return 0; } if (distance2 > distance) { return 2; } return 1; } /// /// Check if 2 vehicles have the same target /// /// /// /// internal bool IsSameTarget(int vehicleIndex1, int VehicleIndex2) { return GetTargetWaypointIndex(vehicleIndex1) == GetTargetWaypointIndex(VehicleIndex2); } /// /// Get rotation of the target waypoint /// /// /// internal Quaternion GetTargetWaypointRotation(int agentIndex) { int[] neighbors = _trafficWaypointsDataHandler.GetNeighbors(GetTargetWaypointIndex(agentIndex)); if (neighbors.Length == 0) { return Quaternion.identity; } return Quaternion.LookRotation(_trafficWaypointsDataHandler.GetPosition(neighbors[0]) - _trafficWaypointsDataHandler.GetPosition(GetTargetWaypointIndex(agentIndex))); } /// /// Check if a change of lane is possible /// Used to overtake and give way /// /// /// /// internal int GetOtherLaneWaypointIndex(int agentIndex, VehicleTypes vehicleType, RoadSide side = RoadSide.Any, Vector3 forwardVector = default) { int waypointIndex = PeekPoint(agentIndex); if (waypointIndex != -1) { return waypointIndex; } int currentWaypointIndex = GetTargetWaypointIndex(agentIndex); if (_trafficWaypointsDataHandler.HasOtherLanes(currentWaypointIndex)) { List possibleWaypoints = _trafficWaypointsDataHandler.GetOtherLanesWithConditions(currentWaypointIndex, vehicleType); if (possibleWaypoints.Count > 0) { return GetSideWaypoint(possibleWaypoints, currentWaypointIndex, side, forwardVector); } } return -1; } internal int GetNeighborCellWaypoint(int row, int column, int depth, VehicleTypes carType, Vector3 playerPosition, Vector3 playerDirection, bool useWaypointPriority) { //get all cell neighbors for the specified depth List neighbors = _gridDataHandler.GetCellNeighbors(row, column, depth, false); for (int i = neighbors.Count - 1; i >= 0; i--) { if (!_gridDataHandler.HasTrafficSpawnWaypoints(neighbors[i])) { neighbors.RemoveAt(i); } } //if neighbors exists if (neighbors.Count > 0) { return ApplyNeighborSelectorMethod(neighbors, playerPosition, playerDirection, carType, useWaypointPriority); } return -1; } /// /// Set the default waypoint generating method /// /// internal void SetSpawnWaypointSelector(SpawnWaypointSelector spawnWaypointSelector) { _spawnWaypointSelector = spawnWaypointSelector; } internal void SetAgentPath(int agentIndex, Queue pathWaypoints) { if (!_pathToDestination.ContainsKey(agentIndex)) { _pathToDestination.Add(agentIndex, pathWaypoints); _hasPath[agentIndex] = true; } _pathToDestination[agentIndex] = pathWaypoints; } internal void RemoveAgentPath(int agentIndex) { _pathToDestination.Remove(agentIndex); _hasPath[agentIndex] = false; } internal bool HasPath(int agentIndex) { return _hasPath[agentIndex]; } internal Queue GetPath(int agentIndex) { if (_pathToDestination.ContainsKey(agentIndex)) { return _pathToDestination[agentIndex]; } return new Queue(); } private void SetTargetWaypointIndex(int agentIndex, int waypointIndex) { //set current target _target[agentIndex] = waypointIndex; } /// /// called when a waypoint was passed /// /// private void MarkWaypointAsPassed(int vehicleIndex) { if (GetTargetWaypointIndex(vehicleIndex) != TrafficSystemConstants.INVALID_WAYPOINT_INDEX) { int waypointIndex = GetTargetWaypointIndex(vehicleIndex); if (_trafficWaypointsDataHandler.IsExit(waypointIndex)) { var intersections = _trafficWaypointsDataHandler.GetAssociatedIntersections(waypointIndex); foreach (var intersection in intersections) { intersection.VehicleLeft(vehicleIndex); } } if (_trafficWaypointsDataHandler.IsEnter(waypointIndex)) { var intersections = _trafficWaypointsDataHandler.GetAssociatedIntersections(waypointIndex); foreach (var intersection in intersections) { intersection.VehicleEnter(vehicleIndex); } } if (_trafficWaypointsDataHandler.IsTriggerEvent(waypointIndex)) { Events.TriggerWaypointReachedEvent(vehicleIndex, waypointIndex, _trafficWaypointsDataHandler.GetEventData(waypointIndex)); } } } /// /// Check if previous waypoints are free /// /// /// /// /// private bool IsTargetFree(int waypointIndex, float distance, int initialWaypointIndex, int currentCarIndex, ref int incomingCarIndex) { #if UNITY_EDITOR if (_debugGiveWay) { Debug.DrawLine(_trafficWaypointsDataHandler.GetPosition(waypointIndex), _trafficWaypointsDataHandler.GetPosition(initialWaypointIndex), Color.green, 1); } #endif if (distance <= 0) { #if UNITY_EDITOR if (_debugGiveWay) { Debug.DrawLine(_trafficWaypointsDataHandler.GetPosition(waypointIndex), _trafficWaypointsDataHandler.GetPosition(initialWaypointIndex), Color.green, 1); } #endif return true; } if (waypointIndex == initialWaypointIndex) { #if UNITY_EDITOR if (_debugGiveWay) { Debug.DrawLine(_trafficWaypointsDataHandler.GetPosition(waypointIndex), _trafficWaypointsDataHandler.GetPosition(initialWaypointIndex), Color.white, 1); } #endif return true; } if (IsThisWaypointATarget(waypointIndex)) { incomingCarIndex = GetAgentIndexAtTarget(waypointIndex); if (GetTargetWaypointIndex(currentCarIndex) == waypointIndex) { #if UNITY_EDITOR if (_debugGiveWay) { Debug.DrawLine(_trafficWaypointsDataHandler.GetPosition(waypointIndex), _trafficWaypointsDataHandler.GetPosition(initialWaypointIndex), Color.blue, 1); } #endif return true; } else { #if UNITY_EDITOR if (_debugGiveWay) { Debug.DrawLine(_trafficWaypointsDataHandler.GetPosition(waypointIndex), _trafficWaypointsDataHandler.GetPosition(initialWaypointIndex), Color.red, 1); } #endif return false; } } else { if (!_trafficWaypointsDataHandler.HasPrevs(waypointIndex)) { return true; } distance -= Vector3.Distance(_trafficWaypointsDataHandler.GetPosition(waypointIndex), _trafficWaypointsDataHandler.GetPosition(initialWaypointIndex)); var prevs = _trafficWaypointsDataHandler.GetPrevs(waypointIndex); for (int i = 0; i < prevs.Length; i++) { if (!IsTargetFree(prevs[i], distance, initialWaypointIndex, currentCarIndex, ref incomingCarIndex)) { if (_debugGiveWay) { Debug.DrawLine(_trafficWaypointsDataHandler.GetPosition(waypointIndex), _trafficWaypointsDataHandler.GetPosition(initialWaypointIndex), Color.magenta, 1); } return false; } } } return true; } private int PeekPoint(int agentIndex) { Queue queue; if (_pathToDestination.TryGetValue(agentIndex, out queue)) { if (queue.Count > 0) { return queue.Peek(); } return -2; } return -1; } private int GetSideWaypoint(List waypointIndexes, int currentWaypointIndex, RoadSide side, Vector3 forwardVector) { switch (side) { case RoadSide.Any: return waypointIndexes[Random.Range(0, waypointIndexes.Count)]; case RoadSide.Left: for (int i = 0; i < waypointIndexes.Count; i++) { if (Vector3.SignedAngle(_trafficWaypointsDataHandler.GetPosition(waypointIndexes[i]) - _trafficWaypointsDataHandler.GetPosition(currentWaypointIndex), forwardVector, Vector3.up) > 5) { return waypointIndexes[i]; } } break; case RoadSide.Right: for (int i = 0; i < waypointIndexes.Count; i++) { if (Vector3.SignedAngle(_trafficWaypointsDataHandler.GetPosition(waypointIndexes[i]) - _trafficWaypointsDataHandler.GetPosition(currentWaypointIndex), forwardVector, Vector3.up) < -5) { return waypointIndexes[i]; } } break; } return -1; } private void TrafficLightChanged(int waypointIndex, bool newValue) { if (_trafficWaypointsDataHandler.IsStop(waypointIndex) != newValue) { _trafficWaypointsDataHandler.SetStopValue(waypointIndex, newValue); for (int i = 0; i < _target.Length; i++) { if (_target[i] == waypointIndex) { WaypointEvents.TriggerStopStateChangedEvent(i, newValue); } } } } private int ApplyNeighborSelectorMethod(List neighbors, Vector3 playerPosition, Vector3 playerDirection, VehicleTypes carType, bool useWaypointPriority) { try { return _spawnWaypointSelector(neighbors, playerPosition, playerDirection, carType, useWaypointPriority); } catch (System.Exception e) { Debug.LogError(TrafficSystemErrors.NoNeighborSelectorMethod(e.Message)); return DefaultDelegates.GetRandomSpawnWaypoint(neighbors, playerPosition, playerDirection, carType, useWaypointPriority); } } /// /// Cleanup /// public void OnDestroy() { WaypointEvents.onTrafficLightChanged -= TrafficLightChanged; } } }