/// <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() { //получаем вектор управления Vector2 moveControl = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")); //определение ускорения и целевой линейной скорости float acceleration, goalValue = 0; if (moveControl.y == 0) { acceleration = _sleepDeceleration * Time.deltaTime; } else { acceleration = _acceleration * Time.deltaTime; goalValue = moveControl.y * _maxSpeed; } //корректировка линейной скорости CurrSpeed = Mathf.MoveTowards(CurrSpeed, goalValue, acceleration); //определение ускорения и угловой скорости acceleration = _rotateSpeedAcceleration * Time.deltaTime; if (moveControl.x == 0) { goalValue = 0; } else { goalValue = _angularSpeed * moveControl.x; } //корректировка угловой скорости, не зависящей от линейной скорости _currRotateSpeed = Mathf.MoveTowards(_currRotateSpeed, goalValue, acceleration); //определение степени влияния лиейной скорости на конечную угловую скорость float rotateRate = Mathf.Clamp01(Mathf.Abs(CurrSpeed) / _angularSpeed); CurrRotateSpeed = rotateRate * _currRotateSpeed; if (_debug) { for (float forecastTime = Time.deltaTime; forecastTime < 5; forecastTime += Time.deltaTime) { Quaternion forecastRotation; Vector3 forecastPosition = AngularMath.Forecast( transform.position, transform.forward * CurrSpeed, CurrRotateSpeed, forecastTime, out forecastRotation ); Debug.DrawRay(forecastPosition, Vector3.up, Color.green, 5); } } //применение линейной и угловой скоростей transform.position += transform.forward * CurrSpeed * Time.deltaTime; transform.Rotate(Vector3.up, CurrRotateSpeed * Time.deltaTime); }
//определение, может ли самолет повернуть в указанную точку без дополнительных виражей private bool CanBeReached(Vector3 position, ref float speed) { Vector3 toPosition = position - Position; Vector3 toPositionLocal = transform.InverseTransformDirection(toPosition); //если точка находится почти на прямой перед нами - можно смело лететь if (toPositionLocal.z >= 0 && Mathf.Abs(toPositionLocal.x) < 0.0001f) { speed = CorrectSpeed(speed, _maxSpeed); return(true); } //определение по относительному положению точки угловой скорости со знаком, чтобы довернуть до нее float angSpeed = _angularSpeed; if (toPositionLocal.x < 0) { angSpeed = -_angularSpeed; } float maneurRadius; //определяем центр радиуса виража, и сам радиус при минимальной скорости Vector3 maneurCenter = AngularMath.CircleCenter( Position, Direction * _minSpeed, angSpeed, out maneurRadius ); if (_debug) { Debug.DrawRay(maneurCenter, (Position - maneurCenter).normalized * maneurRadius, Color.cyan); } float fromCenterToPosition = Vector3.Distance(position, maneurCenter); //если расстояние от центра виража до самолема меньше радиуса, //то точка находится внутри виража, и достичь ее не можем if (fromCenterToPosition < maneurRadius) { speed = CorrectSpeed(speed, _minSpeed); return(false); } //точка находится где-то за пределами виража, //нужно расчитать скорость, на которой самолет сможет в нее попасть float radiusRate = fromCenterToPosition / maneurRadius; float moneurMaxSpeed = radiusRate * _minSpeed; speed = CorrectSpeed(speed, moneurMaxSpeed); return(true); }
//прогноз позиции корабя на время, которое нужно, чтобы до него долететь private Vector3 ForecastCarrierPosition(out float forecastTimeSec) { CarrierDispatch carrier = flight.carrier; //время, чтобы прилететь к кораблю forecastTimeSec = ToCarrierTime(); Quaternion forecastRotation; //находим саму прогнозируемую позицию Vector3 carrierForecastPosition = AngularMath.Forecast( carrier.Position, carrier.Direction * carrier.LinearSpeed, carrier.AngularSpeed, forecastTimeSec, out forecastRotation ); return(carrierForecastPosition); }
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; } } }
//находит точку захода посадку //чем более "правильное" положение занимает самолет относительно корабля, тем точка ближе к месту посадки, //"доводя" таким образом самолет до нужного направления захода на палубу private Vector3 GetStartLandingPoint() { CarrierDispatch carrier = flight.carrier; //сначала рассчитываем время прогноза позиции корабля float toCarrierDistance = Vector3.Distance(Position, carrier.Position); float toCarrierTime = toCarrierDistance / _minSpeed; float toCarrierDirectionAngle = Vector3.Angle(carrier.Direction, Direction); float toCarrierDirectionTime = toCarrierDirectionAngle / _angularSpeed; float forecastTime = toCarrierTime + toCarrierDirectionTime; //на основе времени прогноза расчитываем направление и позуцию корабля Quaternion forecastRotation; Vector3 forecastCarrierPosition = AngularMath.Forecast( carrier.Position, carrier.Direction * -Mathf.Abs(carrier.LinearSpeed), carrier.AngularSpeed, forecastTime, out forecastRotation ); //расчитываем величину доворота курса до прогнозируемой позиции корабля до прогнозируемого направления корабля, Vector3 forecastCarrierDirection = forecastRotation * carrier.Direction; Vector3 toForecastCarrierPosition = forecastCarrierPosition - Position; //это даст понимание расположения самолета относительно корабля в его прогнозируемой позиции Vector3 fromToCarrierToForecastDirectionEuler = Quaternion.FromToRotation(toForecastCarrierPosition, forecastCarrierDirection).eulerAngles - new Vector3(0, 180, 0); //на основе предыдущего расчета, определяем, в какую сторону должен будет поворачивать самолет float signedAngSpeed = _angularSpeed; if (fromToCarrierToForecastDirectionEuler.y > 0) { signedAngSpeed = -signedAngSpeed; } //коррекция угловой скорости самолета на угловую скорость корабля if (carrier.LinearSpeed > 0) { signedAngSpeed += Mathf.Max(carrier.AngularSpeed, 0); } else if (carrier.LinearSpeed < 0) { signedAngSpeed -= Mathf.Min(carrier.AngularSpeed, 0); } float maneurRadius; //расчет центра маневра от прогнозирумых позиции и нарпавления корабля Vector3 maneurCenter = AngularMath.CircleCenter( forecastCarrierPosition, forecastCarrierDirection * LinearSpeed, signedAngSpeed, out maneurRadius ); if (_debug) { Vector3 radius = Vector3.forward * maneurRadius; Quaternion oneGeg = Quaternion.AngleAxis(1, Vector3.up); for (int i = 0; i < 360; i++) { Vector3 debugPoint = maneurCenter + radius; Debug.DrawRay(debugPoint, Vector3.up, Color.white); radius = oneGeg * radius; } } //строим гопотенузу прямого треугольника для расчета касательной Vector3 toManeurCenter = maneurCenter - Position; //расчет длины касатльной от позиции самолета к траектории виража на посадку float distanceToTangentPoint = Mathf.Pow(toManeurCenter.magnitude, 2) - Mathf.Pow(maneurRadius, 2); //если длина касательной не положительна, то мы находимся внутри траектории виража, //надо отлетать от корабля, но можно поропбовать зайти на посадку if (distanceToTangentPoint <= 0) { return(carrier.Position); } distanceToTangentPoint = Mathf.Pow(distanceToTangentPoint, 0.5f); //строим основу для касательной от гипотенузы (центр виража - позиция самолета) Vector3 toManeurTangent = toManeurCenter.normalized * distanceToTangentPoint; //определяем угол между гипотенузой и касательной float toManeurTangentRotateSin = maneurRadius / toManeurCenter.magnitude; float toManeurTangetnRotateAngle = Mathf.Asin(toManeurTangentRotateSin) * 180 / Mathf.PI; if (signedAngSpeed > 0) { toManeurTangetnRotateAngle = -toManeurTangetnRotateAngle; } //для поворота заготовки касательной Quaternion rotateToManeurTangent = Quaternion.AngleAxis(toManeurTangetnRotateAngle, Vector3.up); //построение касательной от позиции самолета к траектории входа на посадку toManeurTangent = rotateToManeurTangent * toManeurTangent; //определние точки входа в траекторию посадки return(Position + toManeurTangent); }