A mudança de direção deve desacelerar o objeto e acelerar no novo cabeçalho (baseado em grade 2D)

8

Estou tentando implementar algum tipo de física espacial falsa no meu jogo 2D. Eu tenho uma visão de cima para baixo da minha nave espacial. Você pode mudar de direção e definir uma velocidade máxima, o que acelera o navio nessa direção de acordo com a quantidade de aceleração do motor do navio.

Eu tenho um código funcionando bem para fazer com que o navio comece lentamente a se mover nessa direção e aumente a velocidade até que a velocidade máxima seja atingida.

Atualizar

Embora as respostas tenham sido um pouco úteis, isso não está me levando à minha solução final. Não consigo transformar as teorias em código funcional. Aqui estão mais alguns parâmetros:

  1. Estamos trabalhando com uma grade 2D
  2. O navio possui um único mecanismo onde você pode definir a potência de 0 a 1 para indicar potência total.
  3. O motor tem uma velocidade máxima
  4. Há uma fricção espacial falsa onde, se você não aplicar mais energia à nave, ela acabará parando.

Problema

O problema que estou tendo é quando mudo de direção. Se eu estiver viajando em uma direção a 300 velocidades, mude a direção para o oposto, agora estou viajando instantaneamente na velocidade definida, em vez de diminuir a velocidade e voltar à velocidade nessa direção.

Estado desejado

insira a descrição da imagem aqui

Código atual

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

}

Código 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
fonte
Você está falando sobre adicionar drag?
Daniel Holst
Eu não sei. Eu reformulei o título e parte da descrição para ser mais "o que eu quero" focado. :)
Thraka
11
Ainda não está 100% claro qual comportamento você deseja que sua nave espacial tenha. Talvez leia algumas perguntas semelhantes e veja se elas lhe dão o que você precisa ou o ajudam a isolar o comportamento específico que você deseja que seja diferente do deles. Diagramar uma jogada por jogada do que você quer que a nave faça em cada parte do turno pode ajudar bastante.
DMGregory
Essa pergunta pode me ajudar, mas parece que ela está tentando fazer mais do que eu quero. Obrigado pelas dicas na diagramação! Hoje farei isso depois do trabalho.
Thraka
11
Olhe para a 2d física básica. Parece que tudo que você precisa fazer é aplicar aceleração ao seu vetor de velocidade.
precisa

Respostas:

6

Não conheço xna... mas sei matemática. E implementar a física sem entender a matemática por trás disso é como entrar na política sem saber mentir. Então vamos começar!

Primeiro de tudo, sua maneira de mover o navio não é realmente baseada na física. Você não quer que o jogador mude a posição do navio diretamente. O que você quer fazer é deixar o jogador aplicar aceleração ao navio, depois deixar a física calcular a velocidade do navio e deixar o mundo mudar a posição do navio por essa velocidade recém-calculada. Velocidade é a diferença na posição do navio no tempo. Se movia 5 unidades para a direita e 1 unidade para cima, movia-se pela velocidade de (5,-1). Aceleração é a diferença de velocidade do navio - apenas influencia a posição do navio, alterando sua velocidade. Se sua nave estava indo 2 unidades para a esquerda e 1 unidade para baixo, significando a velocidade de(2,1), e o jogador o acelera na direção oposta, ou seja (-2,-1)), ele parará no local com a próxima unidade de tempo (seja frame ou tick ou qualquer outra coisa). Em outras palavras, você precisa adicionar o vetor de aceleração ao vetor de velocidade e depois calcular onde o navio será o próximo.

Vetores

Imagine uma flecha que comece em algum lugar (origem), aponte para algum lugar (direção) e tenha um certo comprimento (magnitude). Agora descreva-o com dois valores - quanto X e quanto Y é o seu fim desde o início. Para simplificar, falarei apenas do eixo X, o que significa que seus vetores apontam para algo que é "tanto X" para a direita (positiva) ou para a esquerda (negativa).

Velocidade

Agora, como a posição do navio deve mudar entre os quadros? Com vetor de velocidade. Vamos supor que sua nave comece no local (0,0) com velocidade (12,0). Isso significa que ele mudará de posição da seguinte maneira:

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

Aceleração

Como mudamos a direção? Você não quer apenas mudar a velocidade para (-12,0). Isso significa que o navio passa de 100 parsecs à direita para 100 parsecs restantes em um "quadro". Eu não gostaria de estar naquele navio quando isso acontecer. Novamente, o "comprimento" do vetor é chamado de "magnitude" e, no caso de velocidade, passa a ser velocidade. Portanto, você deseja que a magnitude da velocidade (velocidade da nave) diminua lentamente para 0 e depois acelere para 12 negativo (o que significa que ela se move na direção oposta). Você pode fazer isso adicionando aceleração à velocidade, por exemplo, a aceleração de (-4,0), agora o navio se move da seguinte maneira (jogador pressionado à esquerda no terceiro "quadro" e depois no 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)

Então você deseja aplicar uma aceleração de (4,0)para fazer o navio gradualmente ganhar velocidade na direção X positiva quando o jogador pressiona a seta direita e aplicar uma aceleração de (-4,0)quando a seta esquerda é pressionada. Obviamente, quando nenhuma tecla é pressionada, você não aplica nenhuma aceleração, o que significa que a nave está mantendo sua velocidade (se movendo a uma velocidade constante em uma determinada direção). Se você quiser que ele diminua gradualmente quando nenhuma tecla for pressionada, adicione outro vetor, chame-o Drage dê-lhe a direção sempre oposta à velocidade (ou seja, na direção da parte traseira do navio) até que a magnitude da velocidade atinja 0. Esperamos que você entenda a idéia .

Código

O que eu faria (pseudo-código, você terá que corrigi-lo, adicionar encapsulamento, etc., também ignora alguns aspectos, por exemplo, a diagonal é um pouco mais rápida que a esquerda, direita, para cima ou para baixo):

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
fonte
11
Obrigado pela resposta detalhada, vou ler e voltar para você. Agradeço a ajuda !!
Thraka 9/12/2015
11
Você também pode usar o arrasto para limitar a velocidade da nave e diminuir a velocidade da nave se eles reduzirem a potência. Isso teria o benefício de diminuir suavemente a aceleração à medida que a velocidade se aproxima da velocidade máxima (eu sei que isso está além do escopo da sua pergunta, mas achei que poderia ser uma boa adição se você seguir essa abordagem)
Malrig
11
Eu quero arrastar, na verdade esse é o meu ponto # 4 na minha pergunta. :)
Thraka
3

Para fazer isso, você precisa simular inércia. É assim que eu recomendaria:

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
fonte
Obrigado por vir para ver isso. Parece uma tomada interessante. Estou tentando implementá-lo, mas não está funcionando como acho que deveria. Isso está correto? if (this.Vel.LengthSquared() > this.MaxSpeed * MaxSpeed)você tem o MaxSpeed ​​lá duas vezes. Além disso, ThrustForwardusa , this.Accelmas o seu comentário diz que isso é aceleração máxima, isso também está correto?
Thraka
Sim, estas estão corretas, copiei diretamente de um jogo no qual estou trabalhando que ainda está nos estágios iniciais. Sinta-se livre para usar esse código como base e modificá-lo conforme necessário. this.MaxSpeedexiste duas vezes para otimizar o código. Vector2.Length()leva mais tempo para calcular do queVector2.LengthSquared() a seguir se instrução faz a mesma coisa, mas é un otimizado e fácil de entender:if (this.Vel.Length() > this.MaxSpeed)
Ramon J Denham
0

Ok, é realmente muito simples de conseguir. Antes de tudo, como você mencionou, a direção do motor descreve o caminho do movimento. Isso facilita o trabalho.

Antes de tudo, armazene sempre um vetor da direção em que você está se movendo.

Em seguida, você deve ter um vetor da aparência de seu mecanismo.

Então, por enquanto, quando você começa a se mover, digamos certo, a direção e a aparência do vetor do mecanismo estão apontando para a direita. Quando você agora quer virar digamos para o topo (90 graus), então você simplesmente gira o vetor do mecanismo lookat.

Agora vem a parte divertida. Determine, com qualquer função, quão forte é o efeito da mudança de direção e quebra.

primeiro da mudança de direção.

Dependendo de sua velocidade e mudança de ângulo, você pode diminuir a velocidade e transformar o vetor de direção.

Se você quer uma mudança completa de direção (180 graus), é simples matemática. Em sua atualização, basta alterar sua velocidade lentamente. Quando a velocidade chegar a zero, gire o vetor de direção em 180 graus e comece a adicionar velocidade novamente.

Em uma curva de 90 graus, fica um pouco mais complicado. Você precisa definir uma função para calcular quanto o navio pode girar de acordo com a velocidade e se vai diminuir a velocidade. Mas você pode brincar com os valores até que caiba no que você quer.

Yosh Synergi
fonte
Talvez você esteja acertando algo que estou perdendo. Um atraso precisa ser calculado com base na nova trajetória em relação à antiga e ao atraso da curva ... Não sei ao certo como modelar isso.
Thraka 07/07/2015