#if GLEY_TRAFFIC_SYSTEM using Gley.UrbanSystem.Internal; using System.Collections.Generic; using System.Linq; using Unity.Collections; using UnityEngine; using UnityEngine.Events; using Unity.Mathematics; namespace Gley.TrafficSystem.Internal { /// /// Controls the number of active vehicles /// internal class DensityManager { private readonly Queue _requestedVehicles; private readonly AllVehiclesDataHandler _trafficVehicles; private readonly IdleVehiclesDataHandler _idleVehiclesDataHandler; private readonly PositionValidator _positionValidator; private readonly GridDataHandler _gridDataHandler; private readonly WaypointManager _waypointManager; private readonly TrafficWaypointsDataHandler _trafficWaypointsDataHandler; private readonly bool _useWaypointPriority; private readonly bool _debugDensity; private int _maxNrOfVehicles; private int _currentNrOfVehicles; private int _activeSquaresLevel; private class RequestVehicle { internal UnityAction CompleteMethod; internal List Path; internal VehicleComponent Vehicle; internal VehicleTypes Type; internal Category Category; internal int Waypoint; internal RequestVehicle(int waypoint, VehicleTypes type, Category category, VehicleComponent vehicle, UnityAction completeMethod, List path) { Waypoint = waypoint; Type = type; Category = category; Vehicle = vehicle; CompleteMethod = completeMethod; Path = path; } } private enum Category { Idle, Ignored, } internal DensityManager(AllVehiclesDataHandler trafficVehicles, WaypointManager waypointManager, TrafficWaypointsDataHandler trafficWaypointsDataHandler, GridDataHandler gridDataHandler, PositionValidator positionValidator, NativeArray activeCameraPositions, int maxNrOfVehicles, Vector3 playerPosition, Vector3 playerDirection, int activeSquaresLevel, bool useWaypointPriority, int initialDensity, Area disableWaypointsArea, bool debugDensity) { _positionValidator = positionValidator; _trafficVehicles = trafficVehicles; _waypointManager = waypointManager; _activeSquaresLevel = activeSquaresLevel; _gridDataHandler = gridDataHandler; _maxNrOfVehicles = maxNrOfVehicles; _useWaypointPriority = useWaypointPriority; _trafficWaypointsDataHandler = trafficWaypointsDataHandler; _requestedVehicles = new Queue(); _debugDensity = debugDensity; //disable loaded vehicles var idleVehicles = new List(); for (int i = 0; i < maxNrOfVehicles; i++) { var vehicle = _trafficVehicles.GetVehicle(i); if (!vehicle.excluded) { idleVehicles.Add(vehicle); } } _idleVehiclesDataHandler = new IdleVehiclesDataHandler(new IdleVehiclesData(idleVehicles)); var gridCells = new List(); for (int i = 0; i < activeCameraPositions.Length; i++) { gridCells.Add(_gridDataHandler.GetCell(activeCameraPositions[i].x, activeCameraPositions[i].z)); } if (initialDensity >= 0) { SetTrafficDensity(initialDensity); } if (disableWaypointsArea.radius > 0) { DisableAreaWaypoints(new Area(disableWaypointsArea)); } LoadInitialVehicles(gridCells, playerPosition, playerDirection); } /// /// Change vehicle density /// /// cannot be greater than max vehicle number set on initialize internal void SetTrafficDensity(int nrOfVehicles) { _maxNrOfVehicles = nrOfVehicles; } internal void UpdateActiveSquares(int newLevel) { _activeSquaresLevel = newLevel; } /// /// Ads new vehicles if required /// internal void UpdateVehicleDensity(Vector3 playerPosition, Vector3 playerDirection, Vector3 activeCameraPosition) { if (_currentNrOfVehicles < _maxNrOfVehicles) { CellData gridCell = _gridDataHandler.GetCell(activeCameraPosition); BeginAddVehicleProcess(playerPosition, playerDirection, gridCell, false); } } internal void AddExcludedVehicle(int vehicleIndex, Vector3 position, UnityAction completeMethod) { if (position == Vector3.zero) { return; } if (!_trafficVehicles.VehicleIsExcluded(vehicleIndex)) { Debug.LogWarning($"vehicleIndex {vehicleIndex} is not marked as ignored, it will not be instantiated"); return; } VehicleComponent vehicle = _trafficVehicles.GetExcludedVehicle(vehicleIndex); VehicleTypes type = vehicle.VehicleType; int waypointIndex = GetClosestSpawnWaypoint(position, type); if (waypointIndex != TrafficSystemConstants.INVALID_WAYPOINT_INDEX) { _requestedVehicles.Enqueue(new RequestVehicle(waypointIndex, type, Category.Ignored, _trafficVehicles.GetExcludedVehicle(vehicleIndex), completeMethod, null)); } else { Debug.LogWarning("No waypoint found!"); } } internal void AddVehicleAtPosition(Vector3 position, VehicleTypes type, UnityAction completeMethod, List path) { int waypointIndex = GetClosestSpawnWaypoint(position, type); if (waypointIndex == TrafficSystemConstants.INVALID_WAYPOINT_INDEX) { Debug.LogWarning("There are no free waypoints in the current cell"); return; } _requestedVehicles.Enqueue(new RequestVehicle(waypointIndex, type, Category.Idle, null, completeMethod, path)); } /// /// Remove a vehicle if required /// /// vehicle to remove /// remove the vehicle even if not all conditions for removing are met /// true if a vehicle was really removed internal void RemoveVehicle(int vehicleIndex) { _trafficVehicles.RemoveVehicle(vehicleIndex); var vehicle = _trafficVehicles.GetVehicle(vehicleIndex); _idleVehiclesDataHandler.AddVehicle(vehicle); _currentNrOfVehicles--; } /// /// Update the active camera used to determine if a vehicle is in view /// /// internal void UpdateCameraPositions(Transform[] activeCameras) { _positionValidator.UpdateCamera(activeCameras); } internal void ExcludeVehicleFromSystem(int vehicleIndex) { _trafficVehicles.SetExcludedValue(vehicleIndex, true); _idleVehiclesDataHandler.RemoveVehicle(_trafficVehicles.GetVehicle(vehicleIndex)); } internal void AddExcludecVehicleToSystem(int vehicleIndex) { _trafficVehicles.SetExcludedValue(vehicleIndex, false); _idleVehiclesDataHandler.AddVehicle(_trafficVehicles.GetVehicle(vehicleIndex)); } /// /// Makes waypoints on a given radius unavailable /// internal void DisableAreaWaypoints(Area area) { Debug.Log(area.radius); CellData cell = _gridDataHandler.GetCell(area.center); List neighbors = _gridDataHandler.GetCellNeighbors(cell.CellProperties.Row, cell.CellProperties.Column, Mathf.CeilToInt(area.radius * 2 / _gridDataHandler.GetCellSize()), false); for (int i = neighbors.Count - 1; i >= 0; i--) { cell = _gridDataHandler.GetCell(neighbors[i]); for (int j = 0; j < cell.TrafficWaypointsData.Waypoints.Count; j++) { int waypointIndex = cell.TrafficWaypointsData.Waypoints[j]; if (Vector3.SqrMagnitude(area.center - _trafficWaypointsDataHandler.GetPosition(waypointIndex)) < area.sqrRadius) { _waypointManager.AddDisabledWaypoint(waypointIndex); } } } } internal int GetClosestSpawnWaypoint(Vector3 position, VehicleTypes type) { List possibleWaypoints = _gridDataHandler.GetTrafficSpawnWaypoipointsAroundPosition(position, (int)type); if (possibleWaypoints.Count == 0) return -1; float distance = float.MaxValue; int waypointIndex = -1; for (int i = 0; i < possibleWaypoints.Count; i++) { float newDistance = Vector3.SqrMagnitude(_trafficWaypointsDataHandler.GetPosition(possibleWaypoints[i].WaypointIndex) - position); if (newDistance < distance) { distance = newDistance; waypointIndex = possibleWaypoints[i].WaypointIndex; } } return waypointIndex; } /// /// Add all vehicles around the player even if they are inside players view /// /// /// private void LoadInitialVehicles(List gridCells, Vector3 playerPosition, Vector3 playerDirection) { for (int i = 0; i < _maxNrOfVehicles; i++) { int cellIndex = UnityEngine.Random.Range(0, gridCells.Count); BeginAddVehicleProcess(playerPosition, playerDirection, gridCells[cellIndex], true); } } private void BeginAddVehicleProcess(Vector3 playerPosition, Vector3 playerDirection, CellData gridCell, bool ignorLOS) { if (_requestedVehicles.Count == 0) { AddVehicleOnArea(playerPosition, playerDirection, gridCell, ignorLOS); } else { //add specific vehicle on position var requested = _requestedVehicles.Peek(); switch (requested.Category) { case Category.Idle: if (requested.Vehicle == null) { int idleVehicleIndex = _idleVehiclesDataHandler.GetIdleVehicleIndex(requested.Type); //if an idle vehicle does not exists if (idleVehicleIndex == TrafficSystemConstants.INVALID_VEHICLE_INDEX) { if (_debugDensity) { Debug.Log($"Density: No vehicle of type {requested.Type} is idle"); } AddVehicleOnArea(playerPosition, playerDirection, gridCell, ignorLOS); return; } requested.Vehicle = _idleVehiclesDataHandler.GetAndRemoveVehicle(idleVehicleIndex); if (requested.Vehicle == null) { if (_debugDensity) { Debug.Log($"Density: Vehicle with index {idleVehicleIndex} is null"); } return; } } break; case Category.Ignored: if (requested.Vehicle.gameObject.activeSelf) { AddVehicleOnArea(playerPosition, playerDirection, gridCell, ignorLOS); return; } break; } if (AddVehicle(true, requested.Waypoint, requested.Vehicle)) { var request = _requestedVehicles.Dequeue(); request.CompleteMethod?.Invoke(request.Vehicle, request.Waypoint); if (request.Path != null) { _waypointManager.SetAgentPath(request.Vehicle.ListIndex, new Queue(request.Path)); } } else { AddVehicleOnArea(playerPosition, playerDirection, gridCell, ignorLOS); } } } private void AddVehicleOnArea(Vector3 playerPosition, Vector3 playerDirection, CellData gridCell, bool ignorLOS) { //add any vehicle on area int idleVehicleIndex = _idleVehiclesDataHandler.GetRandomIndex(); //if an idle vehicle does not exists if (idleVehicleIndex == TrafficSystemConstants.INVALID_VEHICLE_INDEX) { if (_debugDensity) { Debug.Log("Density: No idle vehicle found"); } return; } int freeWaypointIndex = _waypointManager.GetNeighborCellWaypoint(gridCell.CellProperties.Row, gridCell.CellProperties.Column, _activeSquaresLevel, _idleVehiclesDataHandler.GetIdleVehicleType(idleVehicleIndex), playerPosition, playerDirection, _useWaypointPriority); //Debug.Log(freeWaypointIndex); //freeWaypointIndex = 3; if (freeWaypointIndex == TrafficSystemConstants.INVALID_WAYPOINT_INDEX) { if (_debugDensity) { Debug.Log("Density: No free waypoint found"); } return; } AddVehicle(ignorLOS, freeWaypointIndex, _idleVehiclesDataHandler.PeakIdleVehicle(idleVehicleIndex)); } /// /// Trying to load an idle vehicle if exists /// private bool AddVehicle(bool firstTime, int freeWaypointIndex, VehicleComponent vehicle) { //Debug.Log(freeWaypointIndex); //if a valid waypoint was found, check if it was not manually disabled if (_trafficWaypointsDataHandler.IsTemporaryDisabled(freeWaypointIndex)) { if (_debugDensity) { Debug.Log("Density: waypoint is disabled"); } return false; } //check if the car type can be instantiated on selected waypoint if (!_positionValidator.IsValid(_trafficWaypointsDataHandler.GetPosition(freeWaypointIndex), vehicle.length * 2, vehicle.coliderHeight, vehicle.ColliderWidth, firstTime, vehicle.frontTrigger.localPosition.z, _waypointManager.GetNextOrientation(freeWaypointIndex))) { return false; } Quaternion trailerRotaion = Quaternion.identity; if (vehicle.trailer != null) { trailerRotaion = _waypointManager.GetPrevOrientation(freeWaypointIndex); if (trailerRotaion == Quaternion.identity) { trailerRotaion = _waypointManager.GetNextOrientation(freeWaypointIndex); } if (!_positionValidator.CheckTrailerPosition(_trafficWaypointsDataHandler.GetPosition(freeWaypointIndex), _waypointManager.GetNextOrientation(freeWaypointIndex), trailerRotaion, vehicle)) { return false; } } _currentNrOfVehicles++; int vehicleIndex = vehicle.ListIndex; _waypointManager.SetTargetWaypoint(vehicleIndex, freeWaypointIndex); _trafficVehicles.ActivateVehicle(vehicle, _trafficWaypointsDataHandler.GetPosition(_waypointManager.GetTargetWaypointIndex(vehicleIndex)), _waypointManager.GetTargetWaypointRotation(vehicleIndex), trailerRotaion); _idleVehiclesDataHandler.RemoveVehicle(vehicle); Events.TriggerVehicleAddedEvent(vehicleIndex); return true; } public void InstantiateVehicle(int vehicleIndex, int targetWaypointIndex, Vector3 position, Quaternion rotation) { var vehicleComponent = API.GetVehicleComponent(vehicleIndex); _waypointManager.SetTargetWaypoint(vehicleIndex, targetWaypointIndex); _trafficVehicles.ActivateVehicle(vehicleComponent, position,rotation, Quaternion.identity); _idleVehiclesDataHandler.RemoveVehicle(vehicleComponent); Events.TriggerVehicleAddedEvent(vehicleIndex); } } } #endif