/** * Déclenche (via une coroutine) le mouvement du transporteur vers la maison la * plus proche (sur le réseau routier), où il sera détruit (utile pour se * débarrasser des transporteurs dont le bâtiment principal est détruit). * Le stock sera alors perdu. Si aucune maison n'est disponible, le * transporteur ne bouge pas, et la coroutine va continuer d'attendre une * maison vers laquelle se rendre. **/ private IEnumerator GoHome() { RoadRouteManager roadRouteManager = GetComponent <RoadRouteManager>(); while (true) { //TODO utiliser un findNearestObjectWithPath (Utils) pour être plus générique. GameObject[] homesArray = GameObject.FindGameObjectsWithTag("Home"); float minDistance = 0.0f; FreightAreaIn nearestHomeIn = null; foreach (GameObject home in homesArray) { FreightAreaIn homeIn = home.GetComponent <BuildingStock>().freightAreaData.freightAreaIn; float distance = RoadsPathfinding.RealDistanceBetween(homeIn.road, roadRouteManager.occupiedRoad); if (distance > 0.0f && (nearestHomeIn == null || minDistance > distance)) { minDistance = distance; nearestHomeIn = homeIn; } yield return(null); } if (nearestHomeIn != null)//Sinon, c'est qu'il n'y a AUCUNE maison satisfaisante, et la boucle principale se charge de relancer la recherche { destination = nearestHomeIn.GetComponentInParent <BuildingStock>(); roadRouteManager.MoveTo(destination.freightAreaData.freightAreaIn.road, Random.Range(0.5f, 3.0f), null); yield break; } yield return(null); } }
private IEnumerator SortAvailableKeepAsideOrderManagersByDistance(Cell <PriorityQueue <float, KeepAsideOrderManager> > rslt) { StockManager stockManager = GameManager.instance.cityBuilderData.stockManager; PriorityQueue <float, KeepAsideOrderManager> rsltQueue = new PriorityQueue <float, KeepAsideOrderManager>(); IEnumerator <KeepAsideOrderManager> keepAsideEnumerator = stockManager.AllKeepAsideOrderManagers(); while (keepAsideEnumerator.MoveNext()) { KeepAsideOrderManager keepAsideManager = keepAsideEnumerator.Current; if (keepAsideManager != null)//On vérifie s'il n'a pas été détruit { float distance = Vector2.Distance(transform.position, keepAsideManager.transform.position); if (distance <= keepAsideManager.availabilityRange)//Pour éviter les calculs inutiles ; si ce n'est pas le cas, il est de toute façon trop loin { FreightAreaIn inArea = keepAsideManager.freightAreaData.freightAreaIn; FreightAreaOut outHome = freightAreaData.freightAreaOut; float roadDistance = RoadsPathfinding.RealDistanceBetween(inArea.road, outHome.road); if (roadDistance > 0.0f && roadDistance <= keepAsideManager.availabilityRange) { rsltQueue.Push(roadDistance, keepAsideManager); } } } yield return(null); } rslt.value = rsltQueue; }
/** * Coroutine à lancer pour trier selon leur distance (sur le réseau * routier) à cette route les bâtiments capable de recevoir des commandes. **/ public IEnumerator SortOrderManagersByDistance(Cell <PriorityQueue <float, OrderManager> > rslt) { PriorityQueue <float, OrderManager> rsltQueue = new PriorityQueue <float, OrderManager>(); StockManager stockManager = GameManager.instance.cityBuilderData.stockManager; IEnumerator <OrderManager> allOrderManagers = stockManager.AllOrderManagers(); while (allOrderManagers.MoveNext()) { OrderManager orderManager = allOrderManagers.Current; if (orderManager != null) { float realDistanceToOrderManager = RoadsPathfinding.RealDistanceBetween(this, orderManager.freightAreaData.freightAreaOut.road); if (realDistanceToOrderManager > 0.0f) { rsltQueue.Push(realDistanceToOrderManager, orderManager); } } yield return(null); } rslt.value = rsltQueue; yield return(null); }
public static float RealDistanceBetween(RoadData start, RoadData goal) { SearchData searchData = new SearchData(goal, start, 4, Orientation.SOUTH);//TODO Le 4 est ici totalement arbitraire, il n'est pas utilisé. + Orientation a définir en fonction de celle du bâtiment. Stack <RoadData> path = RoadsPathfinding.AStar(searchData, -1, RouteStarNeighborhood, RouteStarCost, RouteStarRebuild, RouteStarGoal); return(path == null ? -1.0f : (float)path.Count); }
private IEnumerator MoveToWithMultipleTries(RoadData destination, float delay, RetryData retryData) { if (delay > 0.0f) { yield return(new WaitForSeconds(delay)); } string currentOrientation = _moveManager.orientation; Stack <RoadData> moveKeyPoints = null; int tries = 0; while (moveKeyPoints == null) { if (tries > 0) { yield return(new WaitForSeconds(UnityEngine.Random.Range(0.5f, 3.0f))); } moveKeyPoints = RoadsPathfinding.RouteStar(destination, occupiedRoad, _keyPointsModulo, currentOrientation); tries++; } _moveManager.Move(FollowMoveKeyPoints(moveKeyPoints, destination, retryData)); }
private IEnumerator SendWaitingCarriers() { while (true) { if (_waitingCarriers.Count > 0 && road.roadLock.IsFree(Orientation.SOUTH))//TODO orientation { ResourceCarrier candidate = _waitingCarriers.Dequeue(); ResourceCarrier firstCandidate = candidate; bool firstOk = true; while (RoadsPathfinding.RouteStar(road, candidate.destination.freightAreaData.freightAreaIn.road, 10, Orientation.SOUTH) == null && firstCandidate != candidate)//TODO orientation (et attention avec in/out :/) { firstOk = false; _waitingCarriers.Enqueue(candidate); candidate = _waitingCarriers.Dequeue(); yield return(new WaitForSeconds(0.1f)); }//TODO ATTENTION: la boucle ci-dessus veut dire que si le joueur fait n'importe quoi et coupe des chemins partout, la taille de la file va augmenter alors que des transporteurs y restent coincés!!!! Du coup, au moins mettre un genre de message d'erreur pourrait être sympa.==> TODO attention aussi au cas des destruction: que deviennent les carriers qui devaient aller à un bâtiment qui n'existe plus ? Comparer avec null pour vérifier que pas détruits ??? OK sur les children ? if (firstOk || firstCandidate != candidate)//Donc, on a trouvé un transporteur à envoyer { candidate.transform.position = new Vector3(road.transform.position.x, road.transform.position.y, candidate.transform.position.z); MoveManager candidateMoveManager = candidate.GetComponent <MoveManager>(); candidateMoveManager.orientation = Orientation.SOUTH;//TODO orientation candidate.GetComponent <RoadRouteManager>().occupiedRoad = road; road.roadLock.LockFor(candidateMoveManager.orientation, candidate.gameObject); candidate.GetComponent <Collider2D>().enabled = true; candidate.GetComponent <RoadRouteManager>().MoveTo(candidate.destination.freightAreaData.freightAreaIn.road); } } yield return(new WaitForSeconds(0.5f)); } }
public IEnumerator goOnTour(List <GameObject> toVisit) { if (prefecture != null) { List <GameObject> toReVisit = new List <GameObject>(); toVisit.Remove(prefecture); _tour = TSP.GetItinary(prefecture, toVisit); GameObject _currentDestination; while (!_tour.IsEmpty()) { _continueTour = false; _tryAgainToVisitHome = 50; _currentDestination = _tour.Pop(); goalFA = _currentDestination.GetComponentInChildren <FreightAreaIn>(); if (goalFA != null && RoadsPathfinding.RealDistanceBetween(GetComponent <RoadRouteManager>().occupiedRoad, goalFA.road) != -1) //Il existe encore une route vers ce batiment { GetComponent <RoadRouteManager>().MoveTo(goalFA.road); yield return(new WaitUntil(() => _continueTour || goalFA == null || gameObject.GetComponent <RoadRouteManager>().occupiedRoad == goalFA.road)); if (goalFA != null && _continueTour)//On a pas pu atteindre la maison et on a choisi de la skipper { toReVisit.Add(goalFA.gameObject); } if (goalFA != null && !_continueTour) { Building building = _currentDestination.GetComponent <Building>(); if (building != null) { building.RestoreHealth(); } GetComponent <SpriteRenderer>().enabled = false;//TODO il est toujours sur la FA et donc le lock est toujours activé ! yield return(new WaitForSeconds(Random.Range(2.0f, 5.0f))); GetComponent <SpriteRenderer>().enabled = true; } } } if (toReVisit.Count != 0 && _trytoRevisitCount-- != 0) { StartCoroutine(goOnTour(toReVisit)); } else { StartCoroutine(GoBack()); } } }
/** * Coroutine à lancer pour effectuer une recherche parmi les entrepôts disponibles * quel est le plus proche remplissant une condition donnée. Pour les calculs * de distance, ce sont ici les freight area d'ENTREE (in) qui sont utilisées, ATTENTION! **/ public static IEnumerator FindNearestWarehouseSatisfyingCondition(RoadData startPoint, Func <Warehouse, bool> acceptanceFunction, Cell <Warehouse> rslt, HashSet <FreightAreaIn> toIgnore) { PriorityQueue <float, Warehouse> sortedWarehouses = new PriorityQueue <float, Warehouse>(); IEnumerator <Warehouse> allWarehouses = GameManager.instance.cityBuilderData.stockManager.AllWarehouses(); while (allWarehouses.MoveNext()) { Warehouse warehouse = allWarehouses.Current; if (warehouse != null)// Car il peut avoir été détruit depuis le début de la coroutine ! { FreightAreaIn warehouseIn = warehouse.orderManager.freightAreaData.freightAreaIn; if (!toIgnore.Contains(warehouseIn) && acceptanceFunction(warehouse)) { float distanceToWarehouse = Vector2.Distance(warehouseIn.transform.position, startPoint.transform.position); sortedWarehouses.Push(distanceToWarehouse, warehouse); } } } if (!sortedWarehouses.IsEmpty()) { Warehouse bestWarehouse = sortedWarehouses.Pop(); float pathDistance = RoadsPathfinding.RealDistanceBetween(startPoint, bestWarehouse.orderManager.freightAreaData.freightAreaIn.road); yield return(null); while (!sortedWarehouses.IsEmpty() && (pathDistance > sortedWarehouses.LowestKey() || pathDistance < 0.0f)) { Warehouse nearestAsTheCrowFlies = sortedWarehouses.Pop(); float newPathDistance = RoadsPathfinding.RealDistanceBetween(startPoint, nearestAsTheCrowFlies.orderManager.freightAreaData.freightAreaIn.road); if (newPathDistance >= 0.0f && (newPathDistance < pathDistance || pathDistance < 0.0f)) { pathDistance = newPathDistance; bestWarehouse = nearestAsTheCrowFlies; } yield return(null); } if (pathDistance >= 0.0f) { rslt.value = bestWarehouse; } } }
//TODO faire une version avec une co-routine plus efficace (un pathfinding/frame) public static GameObject GetNearestObjectWithPath(RoadData origin, string tag, HashSet <GameObject> exclude) { GameObject[] objectsWithTag = GameObject.FindGameObjectsWithTag(tag); float minDistance = Mathf.Infinity; GameObject nearest = null; foreach (GameObject nearObject in objectsWithTag) { RoadData destinationRoadData = nearObject.GetComponentInChildren <FreightAreaIn>() == null ? null : nearObject.GetComponentInChildren <FreightAreaIn>().road; if (destinationRoadData != null && (exclude == null || (exclude != null && !exclude.Contains(nearObject)))) { float distance = RoadsPathfinding.RealDistanceBetween(destinationRoadData, origin); if (distance != -1.0f && distance < minDistance) { minDistance = distance; nearest = nearObject; } } } return(nearest); }
//TODO pour l'instant, pas de TSP, c'est un algo qui établi un itinéraire en prenant à chaque fois le gameObject le plus proche à vol d'oiseau du précédent. /** * Pré : origin doit avoir un RoadData * */ public static PriorityQueue <int, GameObject> GetItinary(GameObject origin, List <GameObject> toVisit) { PriorityQueue <int, GameObject> rslt = new PriorityQueue <int, GameObject>(); GameObject previous = origin; int i = 0; while (toVisit.Count > 0) { GameObject nearest = GetNearestObject(previous, toVisit); if (previous.GetComponentInChildren <RoadData>() != null && nearest.GetComponentInChildren <RoadData>() != null && RoadsPathfinding.RealDistanceBetween(previous.GetComponentInChildren <RoadData>(), nearest.GetComponentInChildren <RoadData>()) != -1)//On checke qu'il y a quand même bien un passage { rslt.Push(i, nearest); previous = nearest; i++; } toVisit.Remove(nearest); } return(rslt); }
public bool SendOrderIfPossible() { FreightAreaOut freightOut = freightAreaData.freightAreaOut; if (freightAreaData.availableCarriers > 0 && OrdersToTreat() && freightOut.road.roadLock.IsFree(Orientation.SOUTH))//TODO orientation (opposée à celle du bâtiment) { ResourceOrder toTreat = NextOrder(); FreightAreaIn destinationIn = toTreat.deliveryPlace.freightAreaData.freightAreaIn; if (RoadsPathfinding.RouteStar(destinationIn.road, freightOut.road, 10, Orientation.SOUTH) != null)//TODO orientation { MarkAsTreated(toTreat); freightAreaData.SendCarrier(toTreat.deliveryPlace, Orientation.SOUTH, toTreat.shipment);//TODO orientation (opposée à celle du bâtiment) return(true); } else //Si la destination n'est pas raccordée au réseau routier, on annule sa commande et on l'en notifie { toTreat.deliveryPlace.freightAreaData.parentStock.GetComponent <ResourceConsumer>().CancelResourceOrder(toTreat.shipment); } } return(false); }
private IEnumerator <MoveDescriptor> FollowMoveKeyPoints(Stack <RoadData> moveKeyPoints, RoadData finalDestination, RetryData retryData) { RoadData lastKeyPoint = occupiedRoad; foreach (RoadData keyPoint in moveKeyPoints) { if (keyPoint != null && keyPoint.Neighbors().Contains(occupiedRoad)) { AnticipateNextOrientation(occupiedRoad, keyPoint, _moveManager.orientation); } RoadData lastRoad = occupiedRoad; while (occupiedRoad != keyPoint) { string currentOrientation = _moveManager.orientation; Stack <RoadData> roadPath = RoadsPathfinding.RoadPathStar(keyPoint, occupiedRoad, _keyPointsModulo, currentOrientation, finalDestination); if (roadPath == null)//On annule cet énumérateur et on relance la recherche { if (retryData == null) { retryData = new RetryData(lastKeyPoint, finalDestination); } retryData.IncrementCounter(); _moveManager.CancelMove(); if (retryData.triesCounter == MAX_MOVE_TRIES) { onStuckEvent.Invoke(retryData); } else { MoveTo(finalDestination, UnityEngine.Random.Range(0.25f, 1.5f), retryData); //Ce qui relance donc une coroutine qui va s'occuper de ça! } yield break; } else if (roadPath.Count == 0) { break; //Si le roadPath est vide, c'est qu'on est déjà à destination, ou suffisamment proche de celle-ci pour passer au prochain keyPoint } RoadData nextRoad = roadPath.Pop(); if (nextRoad != lastRoad || UnityEngine.Random.Range(1, 100) == 1) { foreach (RoadData toReserve in roadPath) { toReserve.roadLock.reservationsNber++; } MoveDescriptor toApply = new MoveDescriptor(nextRoad.transform.position, occupiedRoad.transform.position, speed, 0.1f); string orientationToUnlock = currentOrientation; if (MovementWithoutUnshift(occupiedRoad.roadLock, currentOrientation, toApply))//Partage la case avec un autre dans une orientation contraire { _moveManager.UseShiftForMovement(toApply); } else if (occupiedRoad.roadLock.split != RoadLock.DIAGONAL_SPLIT)//!NoNeedToRelock()) { occupiedRoad.roadLock.UnlockFor(currentOrientation, gameObject); occupiedRoad.roadLock.LockFor(toApply.orientation, gameObject); orientationToUnlock = toApply.orientation; } nextRoad.roadLock.LockFor(toApply.orientation, gameObject); //+++++> C'est la ligne ci-dessus qui est chiante: avec un diag split, on risque de relocker dans la mme direction que le contenu principal...>TROUVER UNE SOLUTION //>>>> cas de double split à gérer via une condition spéciale (offre spéciale, SUPER PROMO) dans le RoadLock (avec qques var globales :/) yield return(toApply); lastRoad = occupiedRoad; occupiedRoad.roadLock.UnlockFor(orientationToUnlock, gameObject); occupiedRoad = nextRoad; foreach (RoadData toFree in roadPath) { toFree.roadLock.reservationsNber--; } if (roadPath.Count != 0)//Donc, s'il y a encore une case sur laquelle bouger par la suite jusqu'au prochain keypoint { RoadData secondNextRoad = roadPath.Pop(); AnticipateNextOrientation(nextRoad, secondNextRoad, toApply.orientation); } } else { yield return(null); } } lastKeyPoint = keyPoint; } yield return(null); }
/** * Coroutine se chargeant d'envoyer tout ce qui a été produit par le bâtiment * et se trouve contenu dans son stock à l'entrepôt le plus proche capable * de l'accueillir ou à un bâtiment l'ayant commandé. Tout cela se voit bien * entendu diviser entre les transporteurs * disponibles pour le bâtiment. **/ private IEnumerator ManageProductionStorage() { FreightAreaData freightData = orderManager.freightAreaData; while (true) { if (!orderManager.SendOrderIfPossible() && !orderManager.OrdersToTreat() && _warehouseOrders.Count > 0)//L'appel à la méthode envoie une commande si elle retourne true, d'où l'aspect un peu étrange de la condition { ResourceOrder warehouseOrder = _warehouseOrders.Dequeue(); ResourceShipment orderShipment = warehouseOrder.shipment; FreightAreaIn destinationIn = warehouseOrder.deliveryPlace.freightAreaData.freightAreaIn; if (orderManager.stock.StockFor(orderShipment.resourceName) >= orderShipment.amount && RoadsPathfinding.RouteStar(destinationIn.road, freightData.freightAreaOut.road, 10, Orientation.SOUTH) != null)//TODO orientation { _currentStock.RemoveFromStock(orderShipment.resourceName, orderShipment.amount); freightData.SendCarrier(warehouseOrder.deliveryPlace, Orientation.SOUTH, orderShipment);//TODO orientation } } yield return(new WaitForSeconds(0.5f)); } }