Estoy tratando de implementar algún tipo de física de espacio falso en mi juego 2D. Tengo una vista de arriba hacia abajo de mi nave espacial. Puede cambiar de dirección y establecer una velocidad hasta un máximo que luego acelere el barco en esa dirección de acuerdo con la cantidad de aceleración del motor del barco.
Tengo un código que funciona bien al hacer que la nave comience a moverse lentamente en esa dirección y aumente la velocidad hasta alcanzar la velocidad máxima.
Actualizar
Si bien las respuestas han sido un poco útiles, no me está llevando a mi solución final. Parece que no puedo transformar las teorías en código de trabajo. Aquí hay algunos parámetros más:
- Estamos trabajando con una cuadrícula 2D
- El barco tiene un solo motor donde puede establecer la potencia de 0 a 1 para indicar la potencia máxima.
- El motor tiene una velocidad máxima.
- Hay una falsa fricción espacial en la que si ya no aplicas potencia a la nave, finalmente se detendrá.
Problema
El problema que tengo es cuando cambio de dirección. Si viajo en un rumbo a una velocidad de 300, luego cambie el rumbo al opuesto, ahora estoy viajando instantáneamente a la velocidad establecida en lugar de reducir la velocidad y volver a esa velocidad en esa dirección.
Estado deseado
Código actual
public void Update(Consoles.Space space)
{
var GameTimeElapsedUpdate = (float)SadConsole.Engine.GameTimeElapsedUpdate;
Graphic.PositionOffset = viewPortMaster.Position;
// Update the engine
ShipDetails.Engine.Update();
// Degrade the current velocity with friction??
if (velocity.Length() < 0f)
{
var accelerationFrame = ShipDetails.Engine.GetAccelerationFrame();
if (velocity.X > 0)
velocity.X -= accelerationFrame;
else if (velocity.X < 0)
velocity.X += accelerationFrame;
if (velocity.Y > 0)
velocity.Y -= accelerationFrame;
else if (velocity.Y < 0)
velocity.Y += accelerationFrame;
}
// Handle any new course adjustments
if (IsTurnRightOn)
SetHeading(heading + (ShipDetails.TurningSpeedRight * GameTimeElapsedUpdate));
if (IsTurnLeftOn)
SetHeading(heading - (ShipDetails.TurningSpeedLeft * GameTimeElapsedUpdate));
// Handle any power changes
if (IsPowerIncreasing)
{
SetPower(ShipDetails.Engine.DesiredPower + (GameTimeElapsedUpdate * ((ShipDetails.Engine.MaxSpeed / Settings.SecondsForFullPowerAdjustment) / ShipDetails.Engine.MaxSpeed)));
if (ShipDetails.Engine.DesiredPower > 1.0d)
ShipDetails.Engine.DesiredPower = 1.0d;
}
if (IsPowerDecreasing)
{
SetPower(ShipDetails.Engine.DesiredPower - (GameTimeElapsedUpdate * ((ShipDetails.Engine.MaxSpeed / Settings.SecondsForFullPowerAdjustment) / ShipDetails.Engine.MaxSpeed)));
if (ShipDetails.Engine.DesiredPower < 0.0d)
ShipDetails.Engine.DesiredPower = 0.0d;
}
// Calculate new velocity based on heading and engine
// Are we changing direction?
if (vectorDirectionDesired != vectorDirection)
{
// I think this is wrong, I don't think this is how I'm supposed to do this. I don't really want to
// animate the heading change, which is what I think this is actually doing..
if (vectorDirectionDesired.X < vectorDirection.X)
vectorDirection.X = Math.Min(vectorDirection.X + (vectorDirectionDesired.X * Settings.SpeedSquareSecond * GameTimeElapsedUpdate), vectorDirectionDesired.X);
else if (vectorDirectionDesired.X > vectorDirection.X)
vectorDirection.X = Math.Max(vectorDirection.X + (vectorDirectionDesired.X * Settings.SpeedSquareSecond * GameTimeElapsedUpdate), vectorDirectionDesired.X);
if (vectorDirectionDesired.Y < vectorDirection.Y)
vectorDirection.Y = Math.Min(vectorDirection.Y + (vectorDirectionDesired.Y * Settings.SpeedSquareSecond * GameTimeElapsedUpdate), vectorDirectionDesired.Y);
else if (vectorDirectionDesired.Y > vectorDirection.Y)
vectorDirection.Y = Math.Max(vectorDirection.Y + (vectorDirectionDesired.Y * Settings.SpeedSquareSecond * GameTimeElapsedUpdate), vectorDirectionDesired.Y);
}
vectorDirection = vectorDirectionDesired;
if (ShipDetails.Engine.Power != 0)
{
var force = new Vector2(vectorDirection.X * (float)ShipDetails.Engine.Speed, vectorDirection.Y * (float)ShipDetails.Engine.Speed);
var acceleration = new Vector2(force.X / ShipDetails.Engine.Acceleration, force.Y / ShipDetails.Engine.Acceleration) * GameTimeElapsedUpdate;
velocity = new Vector2(velocity.X + acceleration.X, velocity.Y + acceleration.Y);
Point endingLocation;
endingLocation.X = (int)velocity.X;
endingLocation.Y = (int)velocity.Y;
velocity.X -= endingLocation.X;
velocity.Y -= endingLocation.Y;
MapPosition += endingLocation;
}
if (this == Settings.GameWorld.CurrentShip)
{
var debug = space.GetDebugLayer();
debug.Clear();
debug.Print(0 + space.ViewArea.X, 0 + space.ViewArea.Y, $"Ship: {MapPosition}");
debug.Print(0 + space.ViewArea.X, 1 + space.ViewArea.Y, $"Speed: {ShipDetails.Engine.Speed} Desired: {ShipDetails.Engine.DesiredPower}");
debug.Print(0 + space.ViewArea.X, 2 + space.ViewArea.Y, $"Heading: {heading} Adjusted: {adjustedHeading}");
debug.Print(0 + space.ViewArea.X, 3 + space.ViewArea.Y, $"Dir: {vectorDirection.X.ToString("0.00")}, {vectorDirection.Y.ToString("0.00")} DirDes: {vectorDirectionDesired.X.ToString("0.00")}, {vectorDirectionDesired.Y.ToString("0.00")}");
}
}
ShipEngine Code
class ShipEngine
{
public int Acceleration;
public int AccelerationBonus;
public int MaxSpeed;
public int MaxAfterburner;
public int Speed { get { return (int)(Power * MaxSpeed); } }
// This is a 0-1 no power to full power rating where MaxSpeed is full power
public double DesiredPower { get { return desiredPower; } set { desiredPower = value; if (value != Power) isDesiredTriggered = true; } }
public double Power;
public bool IsAdjusting { get { return Speed != 0; } }
private double desiredPower;
private bool isDesiredTriggered;
public void Update()
{
if (DesiredPower != Power)
{
var GameTimeElapsedUpdate = (float)SadConsole.Engine.GameTimeElapsedUpdate;
var accelerationFrame = (((float)(Acceleration + AccelerationBonus) / Settings.SpeedSquareSecond) * GameTimeElapsedUpdate);
if (DesiredPower > Power)
{
Power += accelerationFrame;
if (Power > DesiredPower)
Power = DesiredPower;
}
else if (DesiredPower < Power)
{
Power -= accelerationFrame;
if (Power < DesiredPower)
Power = DesiredPower;
}
}
}
public float GetAccelerationFrame()
{
return (((float)Acceleration / Settings.SpeedSquareSecond) * (float)SadConsole.Engine.GameTimeElapsedUpdate);
}
}
Respuestas:
No estoy familiarizado con
xna
... pero sé matemáticas. E implementar física sin comprender las matemáticas detrás de esto es como entrar en política sin saber cómo mentir. ¡Entonces empecemos!En primer lugar, tu forma de mover la nave no está basada en la física. No quieres que el jugador cambie la posición de la nave directamente. Lo que quieres hacer es dejar que el jugador aplique la aceleración a la nave, luego que la física calcule la velocidad de la nave , luego deja que el mundo cambie la posición de la nave por esa velocidad recién calculada. La velocidad es la diferencia en la posición del barco en el tiempo. Si se movió 5 unidades hacia la derecha y 1 unidad hacia arriba, se movió por la velocidad de
(5,-1)
. La aceleración es la diferencia en la velocidad del barco: solo influye en la posición del barco al cambiar su velocidad. Si su nave iba 2 unidades hacia la izquierda y 1 unidad hacia abajo, lo que significa la velocidad de(2,1)
, y el jugador lo acelera en la dirección opuesta, es decir(-2,-1)
), se detendrá en su lugar con la siguiente unidad de tiempo (ya sea marco o tic o lo que sea). En otras palabras, debes agregar el vector de aceleración al vector de velocidad y luego calcular dónde estará la nave.Vectores
Imagine una flecha que comienza en algún lugar (origen), apunta a algún lugar (dirección) y tiene una cierta longitud (magnitud). Ahora descríbalo con dos valores: cuánto X y cuánto Y es su final desde el principio. Para simplificar, solo hablaré sobre el eje X, lo que significa que sus vectores apuntan a algo que es "tanto X" a la derecha (positivo) o a la izquierda (negativo).
Velocidad
Ahora, ¿cómo debería cambiar la posición del barco entre cuadros? Con vector de velocidad. Supongamos que su nave comienza en la ubicación (0,0) con velocidad (12,0). Significa que cambiará su posición de la siguiente manera:
Aceleración
¿Cómo cambiamos la dirección? No solo quieres cambiar la velocidad a
(-12,0)
. Eso significa que la nave va de 100 parsecs a la derecha a 100 parsecs a la izquierda en un "marco". No me gustaría estar en ese barco cuando suceda. Nuevamente, la "longitud" del vector se llama "magnitud" y, en caso de velocidad, resulta ser velocidad. Por lo tanto, desea que la magnitud de la velocidad (velocidad del barco) disminuya lentamente a 0 y luego acelere a 12 negativo (lo que significa que se mueve en la dirección opuesta). Puede hacerlo agregando aceleración a la velocidad, por ejemplo, la aceleración de(-4,0)
, por lo que ahora el barco se mueve de la siguiente manera (el jugador presiona a la izquierda en un tercer "cuadro" y luego lo suelta en un noveno):Por lo tanto, desea aplicar una aceleración de
(4,0)
para que el barco gane velocidad gradualmente en una dirección X positiva cuando el jugador presiona la flecha derecha y aplique una aceleración de(-4,0)
cuando se presiona la flecha izquierda. Obviamente, cuando no se presionan teclas, no se aplica ninguna aceleración, lo que significa que la nave mantiene su velocidad (se mueve a una velocidad constante en la dirección dada). Si desea que disminuya la velocidad gradualmente cuando no se presiona ninguna tecla, agregue otro vector, llámeloDrag
y déle una dirección siempre opuesta a la velocidad (es decir, hacia la parte posterior de la nave) hasta que la magnitud de la velocidad llegue a 0. Espero que tenga la idea .Código
Lo que haría (pseudocódigo, tendrá que arreglarlo, agregar encapsulación, etc., también ignora algunos aspectos, por ejemplo, ir en diagonal es un poco más rápido que recto a la izquierda, derecha, arriba o abajo):
fuente
Para hacer esto, necesitas simular la inercia. Así es como recomendaría hacerlo:
fuente
if (this.Vel.LengthSquared() > this.MaxSpeed * MaxSpeed)
tiene MaxSpeed allí dos veces ... Además,ThrustForward
usathis.Accel
pero su comentario dice que esta es la aceleración máxima, ¿ es eso correcto también?this.MaxSpeed
¿Hay dos veces para optimizar el código?Vector2.Length()
tarda más en calcular deVector2.LengthSquared()
la siguiente instrucción if hace lo mismo, pero es un-optimizado y más fácil de entender:if (this.Vel.Length() > this.MaxSpeed)
Ok, en realidad es muy fácil de lograr. En primer lugar, como mencionó, la dirección de su motor describe el camino del movimiento. Esto hace que sea cómodo trabajar con él.
En primer lugar, almacene siempre un vector de la dirección en la que se mueve.
A continuación, debe tener un vector del aspecto de su motor.
Entonces, por ahora, cuando comienzas a moverte, digamos bien, tanto la dirección como el aspecto del vector del motor apuntan hacia la derecha. Cuando ahora quiere girar digamos hacia arriba (90 grados), simplemente gira el vector del motor de búsqueda.
Ahora viene la parte divertida. Determine con cualquier función qué tan fuerte es efectuar el cambio de dirección y la ruptura.
Primero del cambio de dirección.
Dependiendo de su velocidad y cambio de ángulo, podría reducir la velocidad y girar el vector de dirección.
Si desea un cambio completo de dirección (180 grados), entonces es matemática simple. En su actualización, simplemente cambie su velocidad lentamente. Cuando la velocidad llegue a cero, voltee el vector de dirección 180 grados y comience a agregar velocidad nuevamente.
En un giro de 90 grados, se vuelve un poco más complicado. Necesitas definir una función para calcular cuánto puede girar el barco de acuerdo con la velocidad y si va a disminuir la velocidad. Pero puedes jugar con los valores hasta que se ajuste a lo que quieres.
fuente