方向を変更すると、オブジェクトが遅くなり、新しい見出しで加速するはずです(2Dグリッドベース)。


8

私は2Dゲームにある種の偽の宇宙物理学を実装しようとしています。宇宙船を真上から見下ろしています。方向を変えて速度を最大に設定すると、船のエンジン加速量に応じてその方向に船が加速します。

船がゆっくりとその方向に動き始め、最高速度に達するまで速度を上げていくコードをうまく機能させています。

更新

答えは少し役に立ちましたが、それでも私の最終的な解決策にたどり着けません。理論を実用的なコードに変換することはできません。さらにいくつかのパラメータがあります:

  1. 2Dグリッドで作業しています
  2. 船のエンジンは1つで、出力を0から1に設定して、全出力を示すことができます。
  3. エンジンに最高速度があります
  4. 偽の宇宙摩擦があり、船に電力を供給しなくなった場合、最終的に停止します。

問題

私が抱えている問題は、方向を変えるときです。300の速度で1つのヘディングで移動している場合、ヘディングを反対に変更すると、減速してその方向にその速度に戻るのではなく、設定された速度で瞬時に移動します。

望ましい状態

ここに画像の説明を入力してください

現在のコード

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コード

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

}

ドラッグの追加について話していますか?
Daniel Holst

知りません。タイトルと説明の一部をより「私が欲しいもの」に焦点を合わせるように書き換えました。:)
トラカ

1
宇宙船にどのような振る舞いをさせたいかはまだ100%明確ではありません。たぶん、同様の質問をいくつか読んで、それらがあなたに必要なものを提供しているかどうかを確認するか、またはあなたが彼らのそれとは異なるあなたが望む特定の行動を分離するのを助ける。ターンの各部分で船に何をしてほしいかを実況で図解することは、非常に役立ちます。
DMGregory

その質問は私を助けるかもしれませんが、私が望んでいる以上のことをしようとしているようです。作図のヒントをありがとう!今日は仕事帰りにやります。
Thraka 2015

1
基本的な2D物理を調べます。あなたがする必要があるすべてのような音はあなたの速度ベクトルに加速を適用することです。
ClassicThunder 2015

回答:


6

私はあまり詳しくありませんがxna...数学は知っています。そして、その背後にある数学を理解せずに物理学を実装することは、うそをつく方法を知らずに政治に入るようなものです。それでは始めましょう!

まず第一に、あなたの船を動かす方法は、実際には物理学に基づいていません。プレイヤーが船の位置を直接変更したくない場合。あなたがやりたいことは、プレイヤーに船に加速適用させ、次に物理学に船の速度を計算させ、次に新しく計算された速度によって世界に船の位置を変化させることです。速度は、時間内の船の位置の違いです。右に5単位、上に1単位移動すると、の速度で移動しました(5,-1)。加速度は船の速度の違いです。速度は船の位置にのみ影響します。船が左に2ユニット、1ユニット下がっていた場合、(2,1)、そしてプレーヤーがそれを反対方向に加速する、つまり(-2,-1))、次の時間単位(フレームまたはティックなど)で停止します。つまり、速度ベクトルに加速度ベクトルを追加して、次に船がどこに行くかを計算する必要があります。

ベクタ

どこか(起点)から始まり、どこか(方向)を指し、一定の長さ(大きさ)の矢印を想像してみてください。次に、2つの値でそれを記述します-最初から最後までのXとYの量。簡単にするために、X軸についてのみ説明します。これは、ベクトルが右(正)または左(負)の「Xだけ」大きいものを指すことを意味します。

速度

では、船の位置はフレーム間でどのように変化するのでしょうか?速度ベクトル付き。船が位置(0,0)から速度(12,0)で出発するとします。これは、次のように位置を変更することを意味します。

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

加速度

どのように方向を変えるのですか?速度を単にに変更する必要はありません(-12,0)。つまり、船は1つの "フレーム"内に100パーセクから100パーセク残っています。それが起こったとき、私はその船に乗りたくありません。繰り返しになりますが、ベクトルの「長さ」は「大きさ」と呼ばれ、速度の場合は偶然に速度になります。したがって、速度の大きさ(船の速度)をゆっくりと0に減少させてから、負の12に加速させる(つまり、反対方向に移動する)必要があります。これを行うには、速度に加速度を追加(-4,0)します(例:の加速度)。これで、船は次のように移動します(プレーヤーが3番目の「フレーム」でを押してから、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)

したがって(4,0)、プレイヤーが右矢印を押したときに船が徐々に正のX方向に速度を上げ、(-4,0)左矢印を押したときに加速度を適用するために、の加速度を適用する必要があります。明らかに、キーが押されていないときは、加速を適用しないことを意味します。これは、船が速度を維持していることを意味します(特定の方向に一定の速度で移動します)。キーが押されていないときに徐々に速度を落とす場合は、別のベクトルを追加して呼び出しDrag、速度の大きさが0になるまで常に速度とは反対の方向(つまり、船の後方)に方向を付けます。 。

コード

私が何をするか(疑似コード、あなたはそれを修正し、カプセル化を追加する必要があります、またそれはいくつかの側面を無視します、例えば斜めに行くことはまっすぐな左、右、上または下よりわずかに速いです):

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
詳細な回答に感謝しますので、一通り読んでご連絡いたします。私は助けに感謝します!!
Thraka

1
また、ドラッグを使用して船の速度を制限し、パワーが低下した場合に船の速度を落とすこともできます。これは、速度が最大速度に近づくにつれて加速をスムーズに下げるという利点があります(これはあなたの質問の範囲を超えていることはわかっていますが、このアプローチを行えば、これは良い追加になると思います)
Malrig

1
ドラッグが必要です。実際、これが私の質問の4番目のポイントです。:)
Thraka

3

これを行うには、慣性をシミュレートする必要があります。これは私がそれを行うことをお勧めする方法です:

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

これを見に来てくれてありがとう。これは興味深いテイクのように見えます。私はそれを実装しようとしていますが、私が思うように機能していません。これは正しいです??if (this.Vel.LengthSquared() > this.MaxSpeed * MaxSpeed)あなたはまた、...二回そこにMaxSpeedを持っているThrustForwardの用途this.Accelが、あなたのコメントはこれがあると言う最大加速度が正しいというすぎますか?
Thraka

はい、これらは正しいです。私が取り組んでいるゲームからそれを直接コピーしましたが、まだ非常に初期の段階です。このコードをベースとして自由に使用し、必要に応じて変更してください。this.MaxSpeedコードを最適化するために2回あります。Vector2.Length()計算に時間がかかりVector2.LengthSquared() 文が同じことを行いますが、最適化されていないと簡単には理解している場合は、以下:if (this.Vel.Length() > this.MaxSpeed)
ラモンJデンハム

0

OK、実際に達成するのは本当に簡単です。まず第一に、あなたが述べたように、あなたのエンジンの方向は動きの経路を表します。これにより、作業が快適になります。

まず、常に移動する方向のベクトルを保存します。

次のuには、urエンジンのlookatのベクトルが必要です。

したがって、今のところuが移動を開始したときに、右側としましょう。エンジンベクトルの方向とルックアットの両方が右側を指しています。今度はlet.sを上(90度)に向けたい場合、ルックアットエンジンベクトルを単に回転させます。

ここからが楽しい部分です。方向の変更とブレークにどの程度の影響を与えるかを関数で決定します。

最初の方向転換。

urの速度と角度の変化に応じて、uは減速して方向ベクトルを回転させることができます。

完全な方向変更(180度)が必要な場合は、簡単な計算です。ur updateでは、ur速度をゆっくりと変更します。速度がゼロになったら、方向ベクトルを180度反転し、速度の追加を再開します。

90度回転すると、少し複雑になります。u船が速度に応じてどれだけ旋回できるか、そしてどれだけ減速するかを計算する関数を定義する必要があります。しかし、あなたはそれがあなたが望むものに合うまで値で遊ぶことができます。


たぶん、あなたは私が行方不明のものにぶつかっています。新しい軌道と古い軌道、および旋回遅延に基づいて遅延を計算する必要があります...しかし、それをモデル化する方法がわかりません。
Thraka
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.