Eine Richtungsänderung sollte das Objekt verlangsamen und in einer neuen Richtung beschleunigen (2D-Gitter basierend).


8

Ich versuche, in meinem 2D-Spiel eine Art Faux-Space-Physik zu implementieren. Ich habe eine Ansicht von oben nach unten von meinem Raumschiff. Sie können die Richtung ändern und eine Geschwindigkeit bis zu einem Maximum einstellen, die das Schiff dann entsprechend dem Motorbeschleunigungsbetrag des Schiffes in diese Richtung beschleunigt.

Ich habe einen Code, der gut funktioniert, damit sich das Schiff langsam in diese Richtung bewegt und die Geschwindigkeit erhöht, bis die Höchstgeschwindigkeit erreicht ist.

Aktualisieren

Die Antworten waren zwar etwas hilfreich, bringen mich aber nicht zu meiner endgültigen Lösung. Ich kann die Theorien nicht in Arbeitscode umwandeln. Hier sind einige weitere Parameter:

  1. Wir arbeiten mit einem 2D-Raster
  2. Das Schiff verfügt über einen einzelnen Motor, in dem Sie die Leistung von 0 bis 1 einstellen können, um die volle Leistung anzuzeigen.
  3. Der Motor hat eine Höchstdrehzahl
  4. Es gibt eine gefälschte Raumreibung, bei der das Schiff irgendwann aufhört, wenn Sie es nicht mehr mit Strom versorgen.

Problem

Das Problem, das ich habe, ist, wenn ich die Richtung ändere. Wenn ich mit 300 Geschwindigkeit in einer Richtung fahre und dann die Richtung in die entgegengesetzte Richtung ändere, fahre ich jetzt sofort mit der eingestellten Geschwindigkeit, anstatt langsamer zu werden, und komme in dieser Richtung wieder auf diese Geschwindigkeit.

Wunschzustand

Geben Sie hier die Bildbeschreibung ein

Aktueller Code

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

}

Sprechen Sie über das Hinzufügen von Drag?
Daniel Holst

Ich weiß es nicht. Ich habe den Titel und einen Teil der Beschreibung umformuliert, um mich mehr auf das zu konzentrieren, was ich will. :)
Thraka

1
Es ist immer noch nicht 100% klar, welches Verhalten Ihr Raumschiff haben soll. Lesen Sie vielleicht einige ähnliche Fragen und sehen Sie, ob diese Ihnen das geben, was Sie brauchen, oder helfen Sie Ihnen dabei, herauszufinden, welches spezifische Verhalten Sie möchten, das sich von ihrem unterscheidet. Es kann sehr hilfreich sein, ein Spiel für Spiel darzustellen, was das Schiff in jedem Teil der Runde tun soll.
DMGregory

Diese Frage mag mir helfen, aber es scheint, als würde sie versuchen, mehr zu tun, als ich will. Danke für die Tipps zum Diagramm! Ich mache das heute nach der Arbeit.
Thraka

1
Schauen Sie sich die grundlegende 2D-Physik an. Klingt so, als müssten Sie nur eine Beschleunigung auf Ihren Geschwindigkeitsvektor anwenden.
ClassicThunder

Antworten:


6

Ich bin nicht vertraut mit xna... aber ich kenne Mathe. Und Physik zu implementieren, ohne die Mathematik dahinter zu verstehen, ist wie in die Politik zu gehen, ohne zu wissen, wie man lügt. Also lasst uns anfangen!

Erstens ist Ihre Art, das Schiff zu bewegen, nicht wirklich physikbasiert. Sie möchten nicht, dass der Spieler die Position des Schiffes direkt ändert. Was Sie tun möchten, ist, den Spieler auf das Schiff beschleunigen zu lassen, dann die Physik die Schiffsgeschwindigkeit berechnen zu lassen und dann die Welt die Schiffsposition um diese neu berechnete Geschwindigkeit ändern zu lassen . Die Geschwindigkeit ist der zeitliche Unterschied in der Position des Schiffes. Wenn es 5 Einheiten nach rechts und 1 Einheit nach oben bewegte, bewegte es sich mit einer Geschwindigkeit von (5,-1). Die Beschleunigung ist der Geschwindigkeitsunterschied des Schiffes - sie beeinflusst die Position des Schiffes nur durch Änderung seiner Geschwindigkeit. Wenn Ihr Schiff 2 Einheiten nach links und 1 Einheit nach unten fuhr, was die Geschwindigkeit von bedeutet(2,1)und der Spieler beschleunigt es in die entgegengesetzte Richtung (dh (-2,-1)), es stoppt mit der nächsten Zeiteinheit (sei es Frame oder Tick oder was auch immer). Mit anderen Worten, Sie müssen dem Geschwindigkeitsvektor einen Beschleunigungsvektor hinzufügen und dann berechnen, wo das Schiff als nächstes sein wird.

Vektoren

Stellen Sie sich einen Pfeil vor, der irgendwo beginnt (Ursprung), irgendwo zeigt (Richtung) und eine bestimmte Länge (Größe) hat. Beschreiben Sie es nun mit zwei Werten - wie viel X und wie viel Y ist sein Ende von Anfang an. Zur Vereinfachung werde ich nur über die X-Achse sprechen, was bedeutet, dass Ihre Vektoren auf etwas zeigen, das "so viel X" rechts (positiv) oder links (negativ) ist.

Geschwindigkeit

Wie sollte sich nun die Position des Schiffes zwischen den Frames ändern? Mit Geschwindigkeitsvektor. Nehmen wir an, Ihr Schiff startet am Ort (0,0) mit der Geschwindigkeit (12,0). Dies bedeutet, dass die Position wie folgt geändert wird:

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

Beschleunigung

Wie ändern wir die Richtung? Sie möchten nicht nur die Geschwindigkeit auf ändern (-12,0). Das würde bedeuten, dass das Schiff von 100 Parsec nach rechts auf 100 Parsec nach links in einem "Frame" fährt. Ich würde nicht auf diesem Schiff sein wollen, wenn es passiert. Wiederum wird die "Länge" des Vektors "Größe" genannt und im Falle einer Geschwindigkeit ist es zufällig Geschwindigkeit. Sie möchten also, dass die Größe der Geschwindigkeit (Schiffsgeschwindigkeit) langsam auf 0 abnimmt und dann auf negativ 12 beschleunigt (was bedeutet, dass sie sich in die entgegengesetzte Richtung bewegt). Sie können dies tun, indem Sie der Geschwindigkeit eine Beschleunigung hinzufügen, z. B. die Beschleunigung von (-4,0). Das Schiff bewegt sich nun wie folgt (der Spieler drückte auf einem dritten "Rahmen" nach links und ließ es dann auf einem neunten los):

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)

Sie möchten also eine Beschleunigung von anwenden (4,0), damit das Schiff allmählich in einer positiven X-Richtung an Geschwindigkeit gewinnt, wenn der Spieler den rechten Pfeil drückt, und eine Beschleunigung anwenden, (-4,0)wenn der linke Pfeil gedrückt wird. Wenn keine Tasten gedrückt werden, wenden Sie offensichtlich keine Beschleunigung an, was bedeutet, dass das Schiff seine Geschwindigkeit beibehält (sich mit konstanter Geschwindigkeit in eine bestimmte Richtung bewegt). Wenn Sie möchten, dass es sich allmählich verlangsamt, wenn keine Taste gedrückt wird, fügen Sie einen weiteren Vektor hinzu, rufen Sie ihn auf Dragund geben Sie ihm die Richtung, die immer der Geschwindigkeit entgegengesetzt ist (dh zur Rückseite des Schiffes), bis die Geschwindigkeit 0 erreicht. Hoffentlich haben Sie die Idee .

Code

Was ich tun würde (Pseudocode, Sie müssen ihn reparieren, Kapselung hinzufügen usw., außerdem werden einige Aspekte ignoriert, z. B. ist die Diagonale etwas schneller als die Gerade nach links, rechts, oben oder unten):

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
Vielen Dank für die ausführliche Antwort. Ich werde sie durchlesen und mich bei Ihnen melden. Ich schätze die Hilfe !!
Thraka

1
Sie können auch das Ziehen verwenden, um die Schiffsgeschwindigkeit zu begrenzen und das Schiff zu verlangsamen, wenn sie die Leistung verringern. Dies hätte den Vorteil, dass die Beschleunigung sanft verringert wird, wenn sich die Geschwindigkeit der Maximalgeschwindigkeit nähert (ich weiß, dass dies den Rahmen Ihrer Frage
sprengt

1
Ich möchte ziehen, eigentlich ist das mein # 4 Punkt in meiner Frage. :)
Thraka

3

Dazu müssen Sie die Trägheit simulieren. So würde ich es empfehlen:

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

Vielen Dank, dass Sie gekommen sind, um sich das anzusehen. Das sieht nach einer interessanten Einstellung aus. Ich versuche es zu implementieren, aber es funktioniert nicht so, wie ich es mir vorstelle. Ist das richtig?? if (this.Vel.LengthSquared() > this.MaxSpeed * MaxSpeed)Sie haben MaxSpeed ​​dort zweimal .. Auch ThrustForwardverwendet, this.Accelaber Ihr Kommentar sagt, dies ist Max Beschleunigung ist das auch richtig?
Thraka

Ja, diese sind korrekt. Ich habe sie direkt aus einem Spiel kopiert, an dem ich gerade arbeite und das sich noch in einem sehr frühen Stadium befindet. Sie können diesen Code auch als Basis verwenden und nach Bedarf ändern. this.MaxSpeedgibt es zweimal, um den Code zu optimieren. Die Vector2.Length()Berechnung dauert länger als Vector2.LengthSquared()die folgende if-Anweisung macht dasselbe, ist jedoch nicht optimiert und leichter zu verstehen:if (this.Vel.Length() > this.MaxSpeed)
Ramon J Denham

0

Ok, es ist eigentlich ganz einfach zu erreichen. Wie bereits erwähnt, beschreibt Ihre Motorrichtung zunächst den Bewegungspfad. Dies macht es angenehm, damit zu arbeiten.

Speichern Sie zunächst immer einen Vektor der Bewegungsrichtung.

Als nächstes müssen Sie einen Vektor des Aussehens Ihres Motors haben.

Wenn Sie sich also in Bewegung setzen, sagen wir mal rechts, zeigen sowohl die Richtung als auch das Aussehen des Motorvektors nach rechts. Wenn Sie jetzt drehen wollen, sagen wir nach oben (90 Grad), dann drehen Sie einfach den Lookat-Engine-Vektor.

Jetzt kommt der lustige Teil. Bestimmen Sie mit jeder Funktion, wie stark die Richtungsänderung und der Bruch beeinflusst werden sollen.

zuerst die Richtungsänderung.

Abhängig von Ihrer Geschwindigkeit und Änderung des Winkels kann u den Richtungsvektor verlangsamen und drehen.

Wenn Sie eine vollständige Richtungsänderung (180 Grad) wünschen, dann ist es einfache Mathematik. In deinem Update ändere einfach deine Geschwindigkeit langsam. Wenn die Geschwindigkeit auf Null geht, drehen Sie den Richtungsvektor um 180 Grad und fügen Sie die Geschwindigkeit erneut hinzu.

Bei einer 90-Grad-Drehung wird es etwas komplizierter. Sie müssen eine Funktion definieren, um zu berechnen, wie viel das Schiff je nach Geschwindigkeit drehen darf und ob es langsamer wird. Aber du kannst mit den Werten spielen, bis sie passen, was du willst.


Vielleicht treffen Sie etwas, das mir fehlt. Eine Verzögerung muss basierend auf der neuen Flugbahn im Vergleich zur alten und der Abbiegeverzögerung berechnet werden. Ich bin mir jedoch nicht sicher, wie ich das modellieren soll.
Thraka
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.