Изменение направления должно замедлять объект и ускоряться в новом направлении (на основе 2D-сетки)

8

Я пытаюсь реализовать какую-то физику искусственного пространства в моей 2D игре. У меня есть вид сверху на мой космический корабль. Вы можете изменить направление и установить максимальную скорость, которая затем ускоряет корабль в этом направлении в зависимости от величины ускорения двигателя.

У меня есть код, который работает нормально, заставляя корабль медленно начинать движение в этом направлении и увеличивать скорость до достижения максимальной скорости.

Обновить

Хотя ответы были немного полезны, я не могу прийти к окончательному решению. Я не могу преобразовать теории в рабочий код. Вот еще несколько параметров:

  1. Мы работаем с 2D сеткой
  2. Корабль имеет один двигатель, где вы можете установить мощность от 0 до 1, чтобы указать полную мощность.
  3. Двигатель имеет максимальную скорость
  4. Существует фальшивое космическое трение, где, если вы больше не применяете силу к кораблю, он в конечном итоге остановится.

проблема

У меня проблема, когда я меняю направление. Если я еду в одном направлении на скорости 300, а затем меняю направление на противоположное, я теперь сразу же двигаюсь на заданной скорости вместо того, чтобы замедляться и возвращаюсь к этой скорости в этом направлении.

Желаемое состояние

введите описание изображения здесь

Текущий код

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

}
Thraka
источник
Вы говорите о добавлении перетаскивания?
Даниэль Холст
Я не знаю. Я переписал название и некоторые описания, чтобы они были более сфокусированы на том, что я хочу. :)
Трака
1
До сих пор не ясно на 100%, какое поведение вы хотите, чтобы ваш космический корабль имел. Возможно, прочитайте несколько похожих вопросов и посмотрите, дадут ли они вам то, что вам нужно, или помогут вам определить, какое конкретное поведение вы хотите, чтобы оно отличалось от их поведения. Диаграмма игры за игрой того, что вы хотите, чтобы корабль делал в каждой части хода, может сильно помочь.
DMGregory
Этот вопрос может помочь мне, но, похоже, он пытается сделать больше, чем я хочу. Спасибо за советы по диаграммам! Я сделаю это после работы сегодня.
Трака
1
Посмотрите на основную физику 2D. Похоже, все, что вам нужно сделать, это применить ускорение к вашему вектору скорости.
ClassicThunder

Ответы:

6

Я не знаком с xna... но я знаю математику. А реализация физики без понимания математики, стоящей за ней, это все равно, что идти в политику, не зная, как лгать. Итак, начнем!

Прежде всего, ваш способ перемещения корабля не основан на физике. Вы не хотите, чтобы игрок изменил положение корабля напрямую. То, что вы хотите сделать, это позволить игроку применить ускорение к кораблю, затем позволить физике рассчитать скорость корабля , а затем позволить миру изменить положение корабля на эту вновь рассчитанную скорость. Скорость - это разница в положении корабля во времени. Если он двигался на 5 единиц вправо и на 1 единицу вверх, он двигался со скоростью (5,-1). Ускорение - это разница в скорости корабля - она ​​влияет только на положение корабля, изменяя его скорость. Если ваш корабль шел 2 единицы влево и 1 единицу вниз, то есть скорость(2,1)и игрок ускоряет его в противоположном направлении, то есть (-2,-1)), он остановится на месте в следующую единицу времени (будь то кадр, тик или что-то еще). Другими словами, вам нужно добавить вектор ускорения к вектору скорости и затем вычислить, где корабль будет следующим.

векторы

Представьте себе стрелку, которая начинается где-то (начало координат), указывает куда-то (направление) и имеет определенную длину (величину). Теперь опишите его двумя значениями - сколько 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). Это означало бы, что корабль проходит от 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
CpRn
источник
1
Спасибо за подробный ответ, я прочитаю его и вернусь к вам. Я ценю помощь !!
Thraka
1
Вы также можете использовать перетаскивание, чтобы ограничить скорость корабля и замедлить его, если они уменьшают мощность. Это позволило бы плавно снизить ускорение при приближении скорости к максимальной скорости (я знаю, что это выходит за рамки вашего вопроса, но я подумал, что это может быть хорошим дополнением, если вы
воспользуетесь
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));
    }
}
Ramon J Denham
источник
Спасибо, что пришли посмотреть на это. Это выглядит как интересный дубль. Я пытаюсь реализовать это, но это не работает так, как мне кажется. Это правильно?? if (this.Vel.LengthSquared() > this.MaxSpeed * MaxSpeed)у вас есть MaxSpeed ​​там дважды. Кроме того, ThrustForwardиспользует, this.Accelно ваш комментарий говорит, что это Макс ускорение это тоже правильно?
Трака
Да, это правильно, я скопировал это прямо из игры, над которой я работаю, и которая все еще находится на ранней стадии. Не стесняйтесь использовать этот код в качестве основы и изменять его по мере необходимости. this.MaxSpeedесть ли дважды для оптимизации кода. Vector2.Length()вычисление занимает больше времени, чем Vector2.LengthSquared() следующее, если оператор делает то же самое, но не оптимизирован и его легче понять:if (this.Vel.Length() > this.MaxSpeed)
Рамон Денхэм
0

Хорошо, это действительно очень просто. Прежде всего, как вы упомянули, направление движения вашего двигателя описывает траекторию движения. Это делает его удобным для работы.

Прежде всего, всегда сохраняйте вектор направления движения.

Далее у вас должен быть вектор вида вашего двигателя.

Итак, сейчас, когда вы начинаете движение, скажем, вправо, и направление, и направление вектора двигателя указывают вправо. Когда вы теперь хотите повернуть, скажем, на верх (скажем, на 90 градусов), вы просто поворачиваете вектор двигателя lookat.

Теперь самое интересное. С помощью любой функции определите, насколько сильным может быть изменение направления и обрыва.

сначала изменение направления.

В зависимости от скорости и изменения угла вы можете замедлить ход и повернуть вектор направления.

Если вы хотите полное изменение направления (180 градусов), то это простая математика. В вашем обновлении просто меняйте скорость медленно. Когда скорость обернется до нуля, переверните вектор направления на 180 градусов и начните снова добавлять скорость.

На повороте на 90 градусов все становится немного сложнее. Вам нужно определить функцию, чтобы вычислить, сколько кораблю разрешено поворачивать в зависимости от скорости, и насколько оно замедлится. Но вы можете играть со значениями, пока они не соответствуют тому, что вы хотите.

Йош Синерги
источник
Может быть, вы попали в то, что я скучаю. Задержка должна быть рассчитана на основе новой траектории по сравнению со старой и задержкой разворота ... Хотя не знаю, как это смоделировать.
Трака,