Le changement de direction devrait ralentir l'objet et accélérer dans un nouveau cap (basé sur une grille 2D)

8

J'essaie d'implémenter une sorte de fausse physique spatiale dans mon jeu 2D. J'ai une vue de haut en bas de mon vaisseau spatial. Vous pouvez changer de direction et définir une vitesse maximale qui accélère ensuite le navire dans cette direction en fonction de l'accélération du moteur du navire.

J'ai du code qui fonctionne bien pour que le navire commence lentement à se déplacer dans cette direction et augmente la vitesse jusqu'à ce que la vitesse maximale soit atteinte.

Mise à jour

Bien que les réponses aient été légèrement utiles, cela ne m'amène pas à ma solution finale. Je n'arrive pas à transformer les théories en code de travail. Voici quelques paramètres supplémentaires:

  1. Nous travaillons avec une grille 2D
  2. Le navire a un seul moteur où vous pouvez régler la puissance de 0 à 1 pour indiquer la pleine puissance.
  3. Le moteur a une vitesse maximale
  4. Il y a un faux frottement spatial où si vous n'appliquez plus de puissance au navire, il s'arrêtera finalement.

Problème

Le problème que j'ai, c'est quand je change de direction. Si je voyage dans un cap à 300 vitesses, puis changez de cap dans le sens contraire, je voyage maintenant instantanément à la vitesse définie au lieu de ralentir et de revenir à cette vitesse dans cette direction.

État désiré

entrez la description de l'image ici

Code actuel

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
la source
Parlez-vous d'ajouter de la traînée?
Daniel Holst
Je ne sais pas. J'ai reformulé le titre et une partie de la description pour être plus "ce que je veux". :)
Thraka
1
On ne sait toujours pas à 100% quel comportement vous souhaitez que votre vaisseau spatial ait. Peut-être lisez quelques questions similaires et voyez si celles-ci vous donnent ce dont vous avez besoin, ou vous aident à isoler quel comportement spécifique que vous voulez qui est différent du leur. La représentation graphique de ce que vous voulez que le vaisseau fasse à chaque partie du tour peut aider beaucoup.
DMGregory
Cette question peut m'aider, mais il semble qu'elle essaie de faire plus que ce que je veux. Merci pour les conseils sur la création de diagrammes! Je ferai ça après le travail aujourd'hui.
Thraka
1
Regardez dans la physique 2D de base. On dirait que tout ce que vous devez faire est d'appliquer une accélération à votre vecteur de vitesse.
ClassicThunder

Réponses:

6

Je ne connais pas xna... mais je connais les mathématiques. Et mettre en œuvre la physique sans comprendre les mathématiques derrière, c'est comme entrer en politique sans savoir mentir. Alors, commençons!

Tout d'abord, votre façon de déplacer le navire n'est pas vraiment basée sur la physique. Vous ne voulez pas que le joueur change directement la position du navire. Ce que vous voulez faire, c'est laisser le joueur appliquer une accélération au navire, puis laisser la physique calculer la vitesse du navire , puis laisser le monde changer la position du navire selon cette vitesse nouvellement calculée. La vitesse est la différence de position du navire dans le temps. S'il s'est déplacé de 5 unités vers la droite et de 1 unité vers le haut, il s'est déplacé de la vitesse de (5,-1). L'accélération est la différence de vitesse du navire - elle n'influence la position du navire qu'en modifiant sa vitesse. Si votre navire se déplaçait de 2 unités vers la gauche et 1 unité vers le bas, ce qui signifie la vitesse de(2,1), et le joueur l'accélère dans la direction opposée, ce qui signifie (-2,-1)), il s'arrêtera en place avec l'unité de temps suivante (que ce soit une image ou une coche ou autre). En d'autres termes, vous devez ajouter un vecteur d'accélération au vecteur de vitesse, puis calculer l'emplacement du navire suivant.

Vecteurs

Imaginez une flèche qui commence quelque part (origine), pointe vers quelque part (direction) et a une certaine longueur (magnitude). Décrivez-le maintenant avec deux valeurs - combien X et combien Y est sa fin depuis son début. Pour simplifier, je ne parlerai que de l'axe X, ce qui signifie que vos vecteurs pointent vers quelque chose qui est "autant de X" à droite (positif) ou à gauche (négatif).

Rapidité

Maintenant, comment la position du navire devrait-elle changer entre les images? Avec vecteur vitesse. Supposons que votre vaisseau commence à l'emplacement (0,0) avec la vitesse (12,0). Cela signifie qu'il va changer sa position comme suit:

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

Accélération

Comment changeons-nous de direction? Vous ne voulez pas simplement changer la vitesse en (-12,0). Cela signifierait que le vaisseau passe de 100 parsecs à droite à 100 parsecs à gauche dans un "cadre". Je ne voudrais pas être sur ce navire quand ça arrivera. Encore une fois, la "longueur" du vecteur est appelée "amplitude" et en cas de vitesse, il s'agit de la vitesse. Vous voulez donc que l'amplitude de la vitesse (vitesse du navire) diminue lentement jusqu'à 0, puis accélère jusqu'à un niveau négatif de 12 (ce qui signifie qu'elle se déplace dans la direction opposée). Vous pouvez le faire en ajoutant une accélération à la vitesse, par exemple l'accélération de (-4,0), alors maintenant le vaisseau se déplace comme suit (le joueur a appuyé à gauche sur un 3ème "cadre" puis l'a relâché le 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)

Vous voulez donc appliquer une accélération de (4,0)pour que le vaisseau gagne progressivement de la vitesse dans une direction X positive lorsque le joueur appuie sur la flèche droite et applique une accélération (-4,0)lorsque la flèche gauche est enfoncée. Évidemment, lorsqu'aucune touche n'est enfoncée, vous n'appliquez aucune accélération, ce qui signifie que le navire maintient sa vitesse (se déplaçant à vitesse constante dans une direction donnée). Si vous voulez qu'il ralentisse progressivement lorsqu'aucune touche n'est enfoncée, ajoutez un autre vecteur, appelez-le Draget donnez-lui une direction toujours opposée à la vitesse (c'est-à-dire vers l'arrière du navire) jusqu'à ce que l'amplitude de la vitesse atteigne 0. J'espère que vous avez l'idée .

Code

Ce que je ferais (pseudo-code, vous devrez le corriger, ajouter l'encapsulation, etc., il ignore également certains aspects, par exemple, aller en diagonale est légèrement plus rapide que tout droit à gauche, à droite, en haut ou en bas):

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
la source
1
Merci pour la réponse détaillée, je vais la lire et vous contacter. J'apprécie l'aide !!
Thraka
1
Vous pouvez également utiliser la traînée pour limiter la vitesse des navires et ralentir le navire s'ils réduisent la puissance. Cela aurait l'avantage d'abaisser progressivement l'accélération lorsque la vitesse approche de la vitesse maximale (je sais que cela dépasse le cadre de votre question, mais j'ai pensé que cela pourrait être un bon ajout si vous
optez
1
Je veux faire glisser, en fait c'est mon point n ° 4 dans ma question. :)
Thraka
3

Pour ce faire, vous devez simuler l'inertie. Voici comment je recommanderais de le faire:

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
la source
Merci d'être venu voir ça. Cela ressemble à une prise intéressante. J'essaie de le mettre en œuvre mais cela ne fonctionne pas comme je le pense. Est-ce correct?? if (this.Vel.LengthSquared() > this.MaxSpeed * MaxSpeed)vous avez MaxSpeed ​​là-dedans deux fois aussi. ThrustForwardUtilise this.Accelmais votre commentaire dit que c'est l' accélération Max est-ce correct aussi?
Thraka
Oui, elles sont correctes, je les ai copiées directement à partir d'un jeu sur lequel je travaille et qui en est encore à ses débuts. N'hésitez pas à utiliser ce code comme base et à le modifier selon vos besoins. this.MaxSpeedest là deux fois pour optimiser le code. Vector2.Length()prend plus de temps à calculer que l' Vector2.LengthSquared() instruction if suivante fait la même chose mais n'est pas optimisée et plus facile à comprendre:if (this.Vel.Length() > this.MaxSpeed)
Ramon J Denham
0

Ok, c'est vraiment très simple à réaliser. Tout d'abord, comme vous l'avez mentionné, la direction de votre moteur décrit la trajectoire du mouvement. Cela le rend confortable pour travailler avec.

Tout d'abord, stockez toujours un vecteur de la direction dans laquelle vous vous déplacez.

Ensuite, vous devez avoir un vecteur du look de votre moteur.

Donc pour l'instant, lorsque vous commencez à bouger, disons à droite, la direction et le lookat du vecteur moteur pointent vers la droite. Lorsque vous voulez maintenant tourner, disons en haut (90 degrés), il vous suffit de tourner le vecteur moteur lookat.

Maintenant vient la partie amusante. Déterminez avec n'importe quelle fonction la force pour effectuer le changement de direction et la rupture.

premier changement de direction.

Selon votre vitesse et le changement d'angle, u pourrait ralentir et tourner le vecteur de direction.

Si vous voulez un changement de direction complet (180 degrés), alors c'est simple. Dans votre mise à jour, changez simplement votre vitesse lentement. Lorsque la vitesse devient nulle, retournez le vecteur de direction à 180 degrés et recommencez à ajouter de la vitesse.

Dans un virage à 90 degrés, c'est un peu plus compliqué. vous devez définir une fonction pour calculer combien le navire est autorisé à tourner en fonction de la vitesse et s'il va ralentir combien. Mais vous pouvez jouer avec les valeurs jusqu'à ce qu'elles correspondent à ce que vous voulez.

Yosh Synergi
la source
Peut-être que vous frappez quelque chose qui me manque. Un retard doit être calculé en fonction de la nouvelle trajectoire par rapport à l'ancienne et au retard de virage ... Je ne sais pas comment modéliser cela.
Thraka