Il cambio di direzione dovrebbe rallentare l'oggetto e accelerare in una nuova direzione (basata su griglia 2D)


8

Sto cercando di implementare una sorta di fisica spaziale falsa nel mio gioco 2D. Ho una vista dall'alto verso il basso della mia nave spaziale. È possibile cambiare direzione e impostare una velocità fino a un massimo che accelera la nave in quella direzione in base alla quantità di accelerazione del motore della nave.

Ho un codice che funziona bene facendo in modo che la nave inizi lentamente a muoversi in quella direzione e aumenti la velocità fino a quando non viene raggiunta la velocità massima.

Aggiornare

Mentre le risposte sono state leggermente utili, non mi sta portando alla mia soluzione finale. Non riesco a trasformare le teorie in codice funzionante. Ecco alcuni altri parametri:

  1. Stiamo lavorando con una griglia 2D
  2. La nave ha un singolo motore in cui è possibile impostare la potenza da 0 a 1 per indicare la piena potenza.
  3. Il motore ha una velocità massima
  4. C'è un falso attrito spaziale in cui se non si applica più energia alla nave, alla fine si fermerà.

Problema

Il problema che sto avendo è quando cambio direzione. Se sto viaggiando in una direzione a 300 velocità, quindi cambio la direzione in direzione opposta, ora sto viaggiando istantaneamente alla velocità impostata invece di rallentare e tornare a quella velocità in quella direzione.

Stato desiderato

inserisci qui la descrizione dell'immagine

Codice attuale

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

}

Codice ShipEngine

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

}

Stai parlando di aggiungere trascinamento?
Daniel Holst,

Non lo so. Ho riformulato il titolo e parte della descrizione per focalizzarmi maggiormente su "quello che voglio". :)
Thraka,

1
Non è ancora chiaro al 100% quale comportamento desideri avere la tua astronave. Magari leggi alcune domande simili e vedi se quelle ti danno ciò di cui hai bisogno o ti aiutano a isolare quale comportamento specifico desideri che sia diverso dal loro. Diagramma di un play-by-play di ciò che vuoi che la nave faccia in ogni parte del turno potrebbe essere di grande aiuto.
DMGregory

Questa domanda può darmi una mano, ma sembra che stia cercando di fare più di quanto io voglia. Grazie per i suggerimenti sulla creazione di diagrammi! Lo farò dopo il lavoro oggi.
Thraka,

1
Guarda la fisica di base 2d. Sembra che tutto ciò che devi fare è applicare l'accelerazione al tuo vettore di velocità.
ClassicThunder

Risposte:


6

Non ho familiarità con xna... ma conosco la matematica. E implementare la fisica senza capire la matematica che sta dietro è come entrare in politica senza sapere come mentire. Quindi iniziamo!

Prima di tutto, il tuo modo di muovere la nave non è in realtà basato sulla fisica. Non vuoi che il giocatore cambi direttamente la posizione della nave. Quello che vuoi fare è lasciare che il giocatore applichi l'accelerazione alla nave, quindi consenti alla fisica di calcolare la velocità della nave , quindi lascia che il mondo cambi la posizione della nave con quella velocità appena calcolata. La velocità è la differenza nella posizione della nave nel tempo. Se si spostava di 5 unità a destra e 1 unità in alto, si spostava per velocità di (5,-1). L'accelerazione è la differenza di velocità della nave - influenza solo la posizione della nave cambiando la sua velocità. Se la tua nave stava andando a 2 unità a sinistra e 1 unità in basso, vale a dire la velocità di(2,1)e il giocatore lo accelera nella direzione opposta, nel senso (-2,-1)), si fermerà in posizione con la successiva unità di tempo (che sia frame o tick o altro). In altre parole, è necessario aggiungere il vettore di accelerazione al vettore di velocità e quindi calcolare dove sarà la nave successiva.

Vettori

Immagina una freccia che inizia da qualche parte (origine), punta da qualche parte (direzione) e ha una certa lunghezza (magnitudine). Descrivilo ora con due valori: quanta X e quanta Y è la sua fine dall'inizio. Per semplificazione parlerò solo dell'asse X, il che significa che i tuoi vettori puntano verso qualcosa che è "tanta X" a destra (positivo) o a sinistra (negativo).

Velocità

Ora, come dovrebbe cambiare la posizione della nave tra i frame? Con il vettore di velocità. Supponiamo che la tua nave inizi nella posizione (0,0) con velocità (12,0). Significa che cambierà la sua posizione come segue:

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

Accelerazione

Come cambiamo direzione? Non vuoi semplicemente cambiare la velocità in (-12,0). Ciò significherebbe che la nave passa da 100 parsec a destra a 100 parsec a sinistra in un "frame". Non vorrei essere su quella nave quando succede. Ancora una volta, la "lunghezza" del vettore si chiama "magnitudo" e in caso di velocità sembra essere la velocità. Quindi vuoi che la grandezza della velocità (velocità della nave) diminuisca lentamente a 0 e poi acceleri a 12 negativo (il che significa che si muove nella direzione opposta). Puoi farlo aggiungendo l'accelerazione alla velocità, ad esempio l'accelerazione di (-4,0), quindi ora la nave si muove come segue (il giocatore ha premuto a sinistra su un terzo "frame" e poi l'ha rilasciato su un 9):

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)

Quindi si desidera applicare un'accelerazione (4,0)per fare in modo che la nave ottenga gradualmente velocità in una direzione X positiva quando il giocatore preme la freccia destra e applica un'accelerazione di (-4,0)quando viene premuta la freccia sinistra. Ovviamente quando non viene premuto alcun tasto non si applica alcuna accelerazione, il che significa che la nave mantiene la sua velocità (muovendosi a velocità costante in una data direzione). Se vuoi che rallenti gradualmente quando non viene premuto alcun tasto, aggiungi un altro vettore, chiamalo Drage dagli la direzione sempre opposta alla velocità (cioè verso la parte posteriore della nave) fino a quando la grandezza della velocità non raggiunge 0. Speriamo che tu abbia l'idea .

Codice

Cosa farei (pseudo codice, dovresti risolverlo, aggiungere incapsulamento, ecc., Inoltre ignora alcuni aspetti, ad esempio andare in diagonale è leggermente più veloce di dritto a sinistra, a destra, su o giù):

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

1
Grazie per la risposta dettagliata, la leggerò e ti ricontatterò. Apprezzo l'aiuto !!
Thraka,

1
Puoi anche usare la resistenza per limitare la velocità della nave e rallentare la nave se riducono la potenza. Ciò avrebbe il vantaggio di abbassare agevolmente l'accelerazione mentre la velocità si avvicina alla velocità massima (so che questo va oltre lo scopo della tua domanda, ma ho pensato che potrebbe essere una buona aggiunta se
segui

1
Voglio trascinare, in realtà questo è il mio punto 4 nella mia domanda. :)
Thraka,

3

Per fare ciò è necessario simulare l'inerzia. Ecco come consiglierei di farlo:

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

Grazie per essere venuto a vedere questo. Questo sembra un risultato interessante. Sto cercando di implementarlo, ma non funziona come penso che dovrebbe. È corretto?? if (this.Vel.LengthSquared() > this.MaxSpeed * MaxSpeed)hai MaxSpeed ​​lì due volte .. Inoltre, ThrustForwardusa this.Accelma il tuo commento dice che questa è l' accelerazione massima è corretta?
Thraka,

Sì, sono corretti, l'ho copiato direttamente da un gioco a cui sto lavorando che è ancora nelle fasi iniziali. Sentiti libero di usare questo codice come base e modificarlo di cui hai bisogno. this.MaxSpeedc'è due volte per ottimizzare il codice. Vector2.Length()richiede più tempo per il calcolo di quanto Vector2.LengthSquared() segue se l'istruzione fa la stessa cosa ma non è ottimizzata e più facile da capire:if (this.Vel.Length() > this.MaxSpeed)
Ramon J Denham

0

Ok, in realtà è davvero semplice da raggiungere. Prima di tutto, come detto, la direzione del motore descrive il percorso del movimento. Questo lo rende comodo da lavorare.

Prima di tutto, memorizza sempre un vettore della direzione in movimento.

Quindi devi avere un vettore dell'aspetto del tuo motore.

Quindi per ora quando inizi a muoverti, diciamo giusto, sia la direzione che l'aspetto del vettore del motore stanno puntando a destra. Quando ora vuoi girare diciamo in alto (90 gradi), allora semplicemente giri il motore del motore lookat.

Ora arriva la parte divertente. Determina con qualsiasi funzione la forza di effettuare il cambio di direzione e la rottura.

primo del cambio di direzione.

A seconda della velocità e del cambio di angolo, è possibile rallentare e ruotare il vettore di direzione.

Se vuoi un cambio di direzione completo (180 gradi), allora è semplice matematica. Nell'aggiornamento è sufficiente modificare lentamente la velocità. Quando la velocità diventa zero, capovolgere il vettore di direzione per 180 gradi e ricominciare ad aggiungere velocità.

Su una curva di 90 gradi, diventa un po 'più complicato. devi definire una funzione per calcolare quanto la nave può girare in base alla velocità e se rallenterà di quanto. Ma puoi giocare con i valori finché non si adatta a quello che vuoi.


Forse stai colpendo qualcosa che mi manca. Un ritardo deve essere calcolato in base alla nuova traiettoria rispetto al vecchio e al ritardo di virata ... Non sono sicuro di come modellarlo.
Thraka,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.