//определение, может ли самолет повернуть в указанную точку без дополнительных виражей 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 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); }