Example #1
0
    /// <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);
    }
Example #2
0
    /// <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);
        }
    }
Example #3
0
    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);
    }
Example #4
0
    //определение, может ли самолет повернуть в указанную точку без дополнительных виражей
    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);
    }
Example #5
0
    //прогноз позиции корабя на время, которое нужно, чтобы до него долететь
    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);
    }
Example #6
0
    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;
            }
        }
    }
Example #7
0
    //находит точку захода посадку
    //чем более "правильное" положение занимает самолет относительно корабля, тем точка ближе к месту посадки,
    //"доводя" таким образом самолет до нужного направления захода на палубу
    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);
    }