Я использую Unity3D и пишу на C#.
Все значения являются реальными значениями / 100, так как я уменьшаю масштаб. (например, гравитационная постоянная была 6,67408e-11/11 = 6,67408e-15.
Вот код...
void Gravity(Gravity body) {
float dSquared = (body.transform.position - transform.position).sqrMagnitude;
float force = (body.G * shipMass * body.planetMass) / dSquared;
Vector3 forceDirection = (body.transform.position - transform.position).normalized;
Vector3 forceVector = (forceDirection * force);
shipVelocity += forceVector * Time.deltaTime;
transform.position += shipVelocity * Time.deltaTime;
}
Чтобы сделать эту работу, мне нужно установить гравитационную постоянную на: 1,59508e-21.
Вот изображение орбиты, розовая линия — пройденный, а не будущий путь (показанные расчетные элементы орбиты, скорее всего, неверны):
Вот изображение орбиты после ускорения времени:
Редактировать 1:
Используя эту формулу для ускорения и первую часть ответа Рассела Борогова, я смог выйти на круговую орбиту, используя эти значения:
и этот код:
void Gravity(Gravity body) {
float dSquared = (body.transform.position - transform.position).sqrMagnitude;
float M = body.planetMass;
Vector3 forceDirection = (body.transform.position - transform.position).normalized;
float g = G * M / dSquared;
Vector3 forceVector = (forceDirection * g);
shipVelocity += forceVector * Time.deltaTime;
}
Редактировать 2: добавлены расчеты гравитации с использованием скрипта-аккумулятора Рассела Борогова ниже...
void Update() {
Gravity(orbitee);
ApplyForce();
}
void ApplyForce() {
transform.position += shipVelocity * Time.deltaTime;
transform.Rotate(transform.forward * shipTorque * Time.deltaTime);
}
void Gravity(Gravity body) {
accumulator += Time.deltaTime;
while (accumulator > 0.0f) {
float dSquared = (body.transform.position - transform.position).sqrMagnitude;
float M = body.planetMass;
Vector3 forceDirection = (body.transform.position - transform.position).normalized;
float g = G * M / dSquared;
Vector3 forceVector = (forceDirection * g);
shipVelocity += forceVector * simtime;
accumulator -= simtime;
}
}
Редактировать 3: Обновлен скрипт орбитальных элементов (вероятно, не на 100% правильно)
//Mass of satellite
float m1 = shipMass;
//Mass of planet
float m2 = orbiting.planetMass;
float M = m2;
//Relative Position Vector
Vector3 r = FindRelativePosition(orbitee.transform.position, transform);
//Relative Velocity Vector
Vector3 v = shipCurVel;
//#!# add relative to planet velocity
//Specific Angular Momentum
Vector3 h = Vector3.Cross(r, v);
//Standard Gravitational Parameter
float µ = G * (m1 + m2);
//float µ = G * M;
//Eccentricity Vector
Vector3 evec = (Vector3.Cross(v, h) / µ) - (r / Vector3.Magnitude(r));
//Eccentricity
float e = Vector3.Magnitude(evec);
//Vector to Ascending Node #!#
Vector3 n = Vector3.Cross(new Vector3(0, 0, 1), h);
//True Anomaly
float t = Mathf.Acos((Vector3.Dot(evec, r)) / (e * Vector3.Magnitude(r)));
if (Vector3.Dot(r, v) < 0)
t = (2 * Mathf.PI) - t;
//Eccentric Anomaly
float E = 2 * Mathf.Atan(Mathf.Tan(t / 2) / Mathf.Sqrt((1 + e) / (1 - e)));
//Longitude of Ascending Node (2D)
float Ω = 0;
//Inclination (2D)
float i = 0;
//Argument of Periapsis
float ω = Mathf.Atan2(evec.y, evec.x);
float ωdegrees = ω * (180 / Mathf.PI);
//float ω = Mathf.Acos((Vector3.Dot(n, evec)) / (e * Vector3.Magnitude(n)));
//Mean Anomaly
float MA = E - (e * Mathf.Sin(E));
//Semi-Major Axis
float a = 1 / ((2 / Vector3.Magnitude(r)) - (Mathf.Pow(Vector3.Magnitude(v), 2) / µ));
//Apoapsis
float ap = a * (1 + e);
//Periapsis
float pe = a * (1 - e);
//Orbital Period
float T = (2 * Mathf.PI) * Mathf.Sqrt(Mathf.Pow(a, 3) / µ)/60;
Последнее редактирование: исправлена проблема, связанная с неправильным эксцентриситетом — неправильное вычисление относительного положения.
Большая G равна 6,67408e-11 м 3 кг -1 сек - 2 .
Таким образом, если вы равномерно используете 100-километровые единицы вместо m единиц, это будет разница в 5 величин, поэтому G должна измениться на 15 величин, потому что линейный размер возводится в куб.
Единицы массы - 100 кг, G использует -1 из них, поэтому G идет в другую сторону на 2 величины.
Я понятия не имею, что ты делаешь с секундами. Если секунды — это секунды (а для совершения оборота по орбите требуется 90 минут), то G не меняется. Если вы используете какой-то другой коэффициент преобразования, удвойте количество нулей, на которые вы сдвигаете секунды, чтобы изменить G.
Что касается нестабильности, то она неизбежна при моделировании непрерывных функций с временным шагом. Вы можете разделить временной интервал обновления Unity на небольшой фиксированный квант и запускать функцию симуляции несколько раз для каждого игрового кадра:
float accumulator = 0.0f;
float simtime = 0.001f;
void Update( void )
{
accumulator += Time.deltaTime;
while (accumulator > 0.0f)
{
SimulateGravityForTime( simtime );
accumulator -= simtime;
}
}
Это запускает ваш сим один раз за каждую миллисекунду времени, прошедшего в кадре. Это должно сохранять стабильность. Аккумулятор предназначен для хранения долей миллисекунд от кадра к кадру. Вы потратите больше процессорного времени на повторяющиеся вычисления, но современный компьютер должен легко себе это позволить.
Что касается единиц измерения, то на вашем месте я бы отделил представление (на основе преобразований Unity) от модели (симуляции физики). Это позволит вам запустить симуляцию полностью в стандартных единицах, устраняя большую путаницу и уменьшая вероятность ошибок. Внутри физической модели вы всегда должны использовать единицы измерения м/кг/с и отслеживать позиции в векторах, которые не являются частью преобразований Unity. Один раз за кадр вы тщательно конвертировали в единицы Unity и устанавливали позиции преобразований.
Mathf.Atan2(Mathf.Tan(t / 2), Mathf.Sqrt((1 + e) / (1 - e)))
или2 * Mathf.Atan(Mathf.Tan(t / 2) / Mathf.Sqrt((1 + e) / (1 - e)))
v = v / 100
или v /= 100
после нахождения скорости v , AP выходит правильным, так что где-то все еще что-то не так на порядок?2
in в
библиотеках Atan2
компьютерной математики относится к двум аргументам, а не к умножению на 2. Один аргумент Atan
не может определить разницу между входными данными ( + / + ) и ( - / - ), что важно в некоторых триггерных приложениях. не знаю, является ли это одним из них. Второй из ваших вариантов выглядит правильно, для первого нужен файл 2 *
. Если ваши результаты являются нормальными для половины орбиты и безумными для другой половины, вам нужно Atan2
.
Рассел Борогов
Вафли
Рассел Борогов
Вафли
Рассел Борогов