El cambio de dirección debería ralentizar el objeto y acelerar en un nuevo rumbo (basado en cuadrícula 2D)

8

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:

  1. Estamos trabajando con una cuadrícula 2D
  2. El barco tiene un solo motor donde puede establecer la potencia de 0 a 1 para indicar la potencia máxima.
  3. El motor tiene una velocidad máxima.
  4. 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

ingrese la descripción de la imagen aquí

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

}
Thraka
fuente
¿Estás hablando de agregar arrastre?
Daniel Holst
No lo sé. Reescribí el título y parte de la descripción para centrarme más en "lo que quiero". :)
Thraka
1
Todavía no está 100% claro qué comportamiento desea que tenga su nave espacial. Tal vez lea algunas preguntas similares y vea si le dan lo que necesita o lo ayudan a aislar qué comportamiento específico desea que sea diferente al suyo. Diagramar una jugada por jugada de lo que quieres que haga la nave en cada parte del turno puede ser de gran ayuda.
DMGregory
Esa pregunta puede ayudarme, pero parece que puede estar tratando de hacer más de lo que quiero. ¡Gracias por los consejos sobre la diagramación! Lo haré hoy después del trabajo.
Thraka
1
Mira en física básica 2d. Parece que todo lo que necesita hacer es aplicar la aceleración a su vector de velocidad.
ClassicThunder

Respuestas:

6

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:

Position:   Velocity:
(0,0)       (12,0)
(12,0)      (12,0)
(24,0)      (12,0)
(36,0)      (12,0)

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):

Position:   Velocity:   Acceleration:
(0,0)       (12,0)      (0,0)     # starts in 0,0 going right
(12,0)      (12,0)      (0,0)
(24,0)      (12,0)      (-4,0)
(36,0)      (8,0)       (-4,0)    # starts to slow down
(44,0)      (4,0)       (-4,0)
(48,0)      (0,0)       (-4,0)    # stops
(48,0)      (-4,0)      (-4,0)    # changes direction
(44,0)      (-8,0)      (-4,0)    # starts to go left
(36,0)      (-12,0)     (0,0)     # goes left at steady speed
(24,0)      (-12,0)     (0,0)
(12,0)      (-12,0)     (0,0)
(0,0)       (-12,0)     (0,0)     # passes 0,0 starting point
(-12,0)     (-12,0)     (0,0)     # keeps going left with the same speed
(-24,0)     (-12,0)     (0,0)

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ámelo Dragy 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):

class Vector {
    x = 0;
    y = 0;

    add(Vector v) {
        this.x += v.x;
        this.y += v.y;
    }
}

class Ship {
    position = new Vector;
    velocity = new Vector;
    maxSpeed = 12;

    accelerate(Vector acceleration) {
        this.velocity.add(acceleration);
        if (this.velocity.x > this.maxSpeed)
            this.velocity.x = this.maxSpeed);
        if (this.velocity.x < -1*this.maxSpeed)
            this.velocity.x = -1*this.maxSpeed); // do the same for y
    }
}

switch (pressedKey) {
    case 'right': Ship.accelerate(new Vector(4,0)); break;
    case 'left': Ship.accelerate(new Vector(-4,0)); break;
}

Ship.position.add(Ship.velocity); // world updates the ship's position
cprn
fuente
1
Gracias por la respuesta detallada, la leeré y te responderé. Agradezco la ayuda !!
Thraka
1
También puede usar la función de arrastre para limitar la velocidad de las naves y ralentizar la nave si reducen la potencia. Esto tendría el beneficio de reducir suavemente la aceleración a medida que la velocidad se acerca a la velocidad máxima (sé que esto está más allá del alcance de su pregunta, pero pensé que podría ser una buena adición si sigue este enfoque)
Malrig
1
Quiero arrastre, en realidad ese es mi punto # 4 en mi pregunta. :)
Thraka
3

Para hacer esto, necesitas simular la inercia. Así es como recomendaría hacerlo:

class Ship
{
    public Vector2 Pos; //Current ship position
    public Vector2 Vel; //Store current velocity as a vector
    public float Rot; //What direction the ship is facing in radians

    public float Accel; //Maximum acceleration
    public float MaxSpeed; //Maximum velocity 

    public void Update(float elapsedTime)
    {
        this.Pos += this.Vel * elapsedTime; //Update our position based on our current velocity
        this.Rot = MathHelper.WrapAngle(this.Rot); //Wrap our heading angle to always be between -Pi and Pi
        if (this.Vel.LengthSquared() > this.MaxSpeed * MaxSpeed) //Keep the velocity vector's length shorter than our max speed
        {
            this.Vel.Normalize();
            this.Vel *= this.MaxSpeed;
        }
    }

    public void ThrustForward(float elapsedTime) //Apply our acceleration to our current velocity
    {
        this.Vel += Vector2.Transform(-Vector2.UnitY * this.Accel * elapsedTime, Matrix.CreateRotationZ(this.Rot));
    }
}
Ramon J Denham
fuente
Gracias por venir a ver esto. Esto parece una toma interesante. Estoy tratando de implementarlo, pero no funciona como creo que debería. ¿¿Es esto correcto?? if (this.Vel.LengthSquared() > this.MaxSpeed * MaxSpeed)tiene MaxSpeed ​​allí dos veces ... Además, ThrustForwardusa this.Accelpero su comentario dice que esta es la aceleración máxima, ¿ es eso correcto también?
Thraka
Sí, estos son correctos, lo copié directamente de un juego en el que estoy trabajando que todavía está en las primeras etapas. Siéntase libre de usar este código como base y modificarlo según lo necesite. this.MaxSpeed¿Hay dos veces para optimizar el código? Vector2.Length()tarda más en calcular de Vector2.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)
Ramon J Denham
0

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.

Yosh Synergi
fuente
Tal vez estás golpeando algo que me falta. Una demora debe calcularse en función de la nueva trayectoria en comparación con la anterior y la demora de giro ... Sin embargo, no estoy seguro de cómo modelar eso.
Thraka