/// <summary> /// попытаться поставить avader'а в очередь, с возвратом его позиции /// </summary> /// <param name="avaider">кандидат в очередь</param> /// <param name="position">позиция в очереди</param> /// <returns>true - если добавлен в очередь, иначе false</returns> /// <exception cref="ArgumentNullException"></exception> public bool Register(IAvaidObject avaider, out Vector3?position) { if (avaider == null) { throw new ArgumentNullException("avaider"); } //определяем адиус виража, как расстояни до впереди идущего float maneurRadius = AngularMath.CircleRadius(avaider.MaxLinearSpeed, avaider.MaxAngularSpeed); LinkedListNode <IAvaidObject> search; //ищем позицию кандидата в очереди, или добавляем в конец bool result = !_distanceCache.TryGetValue(avaider, out search); if (result) { search = _order.AddLast(avaider); _distanceCache[avaider] = search; } //если нет никого впереди if (search.Previous == null || search.Previous.Value == null) { position = null; } //если впереди идущий есть, отсчитываем позицию до него else { IAvaidObject previous = search.Previous.Value; position = previous.Position - previous.Direction * maneurRadius; } return(result); }
/// <summary> /// возвращает позицию в очереди на посадку, или null, если можно садиться /// </summary> /// <exception cref="ArgumentNullException"></exception> public Vector3?this[IAvaidObject avaider] { get { if (avaider == null) { throw new ArgumentNullException("avaider"); } //если в череди никого нет, можно садиться if (_order.Count == 0) { return(null); } LinkedListNode <IAvaidObject> search; //если avaider есть в очереди, то будем считать его позицию от предыдущего, //в противном случае - от последнего, если таковой есть if (!_distanceCache.TryGetValue(avaider, out search)) { search = _order.Last; } if (search == null || search.Previous == null || search.Previous.Value == null) { return(null); } //определяем радиус моневра avaider'а, будет использовано, как расстояние до впереди идущего float maneurRadius = AngularMath.CircleRadius(avaider.MaxLinearSpeed, avaider.MaxAngularSpeed); IAvaidObject previous = search.Previous.Value; return(previous.Position - previous.Direction * maneurRadius); } }
private void Update() { //фиксация желания запуска самолета if (Input.GetButtonUp("scout")) { TryTakeoffPendingPlane(); } //самолет возможно запустить, есть есть самолеты на запуск и при этом не производится посадка if (_landingQueue.Count == 0 && _takeoffPending > 0) { //интервал между запусками/посадкой равен времени полного виража самолета, //чтобы при истекании времени полета не создавать скученности самолетов на посадке float fullSpinTime = 360 / _planePrefab.MaxAngularSpeed; if (Time.time - _takeoffTime > fullSpinTime) { StartPlane(); } } //логика определения угрозы столкновения и расчет точек избегания foreach (IAvaidObject avaider in _onTrack) { //если объект не способен уклоняться, расчитывать нечего if (!avaider.CanAvaid) { continue; } Vector3 avaiderVelocity = avaider.Direction * avaider.LinearSpeed; //итоговый вектор направления избегания столкновения Vector3 avaidVector = Vector3.zero; //проходимся по каждому из "соперников" foreach (IAvaidObject toAvaid in _onTrack) { if (toAvaid == avaider) { continue; } Vector3 toAvaidVelocity = toAvaid.Direction * toAvaid.LinearSpeed; //длина проекции скорости и направления движения соперника //на скорость и направление движения рассматриваемого объекта float projectionLength = Vector3.Dot(avaiderVelocity, toAvaidVelocity) / avaiderVelocity.magnitude; //скорректированная скорость рассматриваемого объекта float correctedAvaiderLinearSpeed = avaiderVelocity.magnitude - projectionLength; //дистанция, на которой стоит предпринимать меы по уклонению начинается с безопасной дистанции полета float distanceToReact = _flightParams.saveDistance; Vector3 avaiderToAvaid = toAvaid.Position - avaider.Position; float maneurRadius; //если скорректированная скорость рассматриваемого объекта не положительна, //а соперник вне безопасной зоны, можно не учитывать радиус маневрирования if (correctedAvaiderLinearSpeed <= 0 && distanceToReact < avaiderToAvaid.magnitude) { maneurRadius = 0; } //иначе определяем максимальный радиус маневрирования для рассматриваемого объекта else { maneurRadius = AngularMath.CircleRadius(correctedAvaiderLinearSpeed, avaider.MaxAngularSpeed); } //итоговая дистанция, на которой нужно начинать уворачиваться distanceToReact += maneurRadius; float fromReactDistanceToAvaid = avaiderToAvaid.magnitude - distanceToReact; //если соперник находится за пределами дистанции реагирования, можно не беспокоиться if (fromReactDistanceToAvaid > 0) { continue; } //иначе добавить к вектору уклонения противоположное направление в той мере, //в какой соперник приблизился к рассматриваемому объекту else { if (_debug) { Debug.DrawLine(avaider.Position, toAvaid.Position, Color.blue); } avaidVector += avaiderToAvaid.normalized * fromReactDistanceToAvaid; } } //если вектор уворота есть - нужно увернуться if (avaidVector != Vector3.zero) { float maneurRadius = AngularMath.CircleRadius(avaider.AvgLinearSpeed, avaider.MaxAngularSpeed); avaider.Target = avaider.Position + avaidVector.normalized * maneurRadius; if (_debug) { Debug.DrawRay(avaider.Target.Value, Vector3.up, Color.magenta); } } else { avaider.Target = null; } } }