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 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 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); }