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:
- Nous travaillons avec une grille 2D
- Le navire a un seul moteur où vous pouvez régler la puissance de 0 à 1 pour indiquer la pleine puissance.
- Le moteur a une vitesse maximale
- 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é
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);
}
}
Réponses:
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:
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):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-leDrag
et 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):
la source
Pour ce faire, vous devez simuler l'inertie. Voici comment je recommanderais de le faire:
la source
if (this.Vel.LengthSquared() > this.MaxSpeed * MaxSpeed)
vous avez MaxSpeed là-dedans deux fois aussi.ThrustForward
Utilisethis.Accel
mais votre commentaire dit que c'est l' accélération Max est-ce correct aussi?this.MaxSpeed
est 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)
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.
la source