SFML C++ Я неправильно использую дельта-время, как правильно

Я пытаюсь сделать клон астероидов, и пока мой корабль летает. Однако его скорость также зависит от FPS. Итак, чтобы смягчить то, что я читал, мне пришлось бы умножать мои управляющие переменные на deltaTime (время между кадрами, если я правильно понял). Однако, когда я попытался реализовать это, корабль отказался двигаться. Я думал, что это связано с возможным неявным округлением до 0 (преобразование в int?), но никаких предупреждений не выдается. Что я делаю не так?

Вот как выглядит код:

sf::Vector2f newPosition(0,0);
sf::Vector2f velocity(0,0);
float acceleration = 3.0f;
float angle = 0;
float angularVelocity = 5;
float velDecay = 0.99f;


sf::Clock deltaClock;

window.setFramerateLimit(60);
while (window.isOpen())
{
    sf::Time deltaTime = deltaClock.restart();
    sf::Event event;
    while (window.pollEvent(event))
    {
        if (event.type == sf::Event::Closed || ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape)))
            window.close();
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
    {
        if(velocity.x < 10)velocity.x += acceleration * deltaTime.asSeconds();
        if(velocity.y < 10)velocity.y += acceleration * deltaTime.asSeconds();
        angle = player.getRotation() - 90;
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
    {
        if(velocity.x > 0)velocity.x -= acceleration * deltaTime.asSeconds();
        else velocity.x = 0;
        if(velocity.y > 0)velocity.y -= acceleration * deltaTime.asSeconds();
        else velocity.y = 0;
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
    {
        player.rotate(-angularVelocity);
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
    {
        player.rotate(angularVelocity);
    }

    newPosition.x = player.getPosition().x + (velocity.x * cos(angle * (M_PI / 180.0))) * deltaTime.asSeconds();
    newPosition.y = player.getPosition().y + (velocity.y * sin(angle * (M_PI / 180.0))) * deltaTime.asSeconds();    
    player.setPosition(newPosition);

    velocity.x *= velDecay;
    velocity.y *= velDecay;

    window.clear();
    window.draw(background);
    window.draw(player);
    window.draw(debugText);
    window.display();
}

person MrPlow    schedule 15.06.2013    source источник
comment
может быть потому, что вы устанавливаете позицию на скорость? Вместо скорости к скорости? Это может быть просто семантика, но я решил, что все равно спрошу. Я смотрю на код, пытаясь понять, что может быть не так. Я предполагаю, что где-то что-то обнуляется, если вы вообще не двигаетесь.   -  person Dean Knight    schedule 15.06.2013
comment
Убедитесь, что вы не застряли здесь в бесконечном цикле: while (window.pollEvent(event))   -  person Dean Knight    schedule 15.06.2013
comment
Да, разве вы не должны устанавливать для позиции что-то вроде: newPosition.x = curPosition.x + (VelocityPerSec / dtSeconds)? То же самое для направления Y. Кроме того: ваши новые скорости не должны рассчитываться с использованием положения ... По крайней мере, если я правильно понимаю простую физику. Используйте это уравнение для расчета новой скорости в каждом кадре: vf = vi + at Может быть, исправление этой математики предотвратит странное поведение вашей симуляции. Дайте мне знать, как это происходит, и я могу составить ответ, чтобы людям было легче найти и прочитать.   -  person Dean Knight    schedule 15.06.2013
comment
@DeanKnight Действительно, семантически имена неверны, и моя математика, вероятно, плохая, поскольку я уже давно не занимался физикой. Я немного изменил код, переименовал несколько вещей. Снижение скорости не работает, как я думал, и я не знаю, как заставить его работать (так как сейчас оно выполняется постоянно и сразу же убивает скорость, заставляя корабль оставаться на месте). while (window.pollEvent(event)) не вызывает бесконечный цикл.   -  person MrPlow    schedule 15.06.2013
comment
@DeanKnight while(window.pollEvent(event)) - это способ обработки событий в SFML, это не может быть бесконечный цикл.   -  person teh internets is made of catz    schedule 15.06.2013
comment
Вы совершенно правы. Я узнал это после поиска в нем. Никогда нельзя быть слишком тщательным, если перед вами нет кода, который можно запустить через отладчик. :) Похоже, мы разобрались с этим вопросом. Спасибо за комментарий.   -  person Dean Knight    schedule 15.06.2013


Ответы (1)


Я не могу запустить ваш код, поэтому я не могу быть уверен на 100%, но следующее выглядит неправильно:

Расчет скорости

velocity.x = (player.getPosition().x + (acceleration * cos(angle * (M_PI / 180.0)) * deltaTime.asSeconds()));
velocity.y = (player.getPosition().y + (acceleration * sin(angle * (M_PI / 180.0)) * deltaTime.asSeconds()));   

Попробуйте изменить его на что-то вроде этого:

velocity.x = (player.getVelocity().x + (acceleration * cos(angle * (M_PI / 180.0)) * deltaTime.asSeconds()));
velocity.y = (player.getVelocity().y + (acceleration * sin(angle * (M_PI / 180.0)) * deltaTime.asSeconds()));   

Это использует простое физическое уравнение vf = vi + a*t, но в виде компонентов x,y. Я считаю, что использование Position.x и Position.y полностью отбросило бы это уравнение.

Примечание: с точки зрения синтаксиса код, который я вам дал, может не работать. Поместите любой код в player.getVelocity().x, который даст вам текущую скорость игрока в направлении X. Сделайте то же самое для направления Y.

Установка новой позиции

Я не могу быть уверен, правильно ли player.setPosition(velocity);. Если функция setPosition позаботится о следующем:

newPosition.x = oldPosition.x + (velocity.x/dt)

как в направлении x, так и в направлении y, тогда это должно работать.

Но если он просто делает:

newPosition.x = velocity.x

Тогда я считаю, что это будет неправильно и приведет к неправильному моделированию.

Общий

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

Редактировать1:

Ваш код выглядит намного лучше. Отсюда я бы добавил код, который изменяет ваше ускорение. Скажем, нажатие клавиши w включит «двигатели», которые дадут вам ускорение, равное 2. Ускорение ухудшится (вернется к нулю) в течение ВРЕМЕНИ, когда никакая клавиша не будет нажата. Не за кадр. Раньше вы умножали на 0,99 за кадр. Это означает, что ускорение может быть равно нулю за полсекунды, если вы получаете 120 кадров в секунду (вполне возможно в такой простой игре). Вам нужно, чтобы он деградировал на основе вашей переменной dt. Однако, как только она достигнет нуля, у вас все равно будет положительная скорость. Обычно эта скорость со временем уменьшается из-за гравитации, но в космосе гравитация будет очень маленькой по сравнению с тем, что вы найдете на Земле (-9,8 м/с или -32 фута/с). Так что, возможно, вы могли бы реализовать падение силы тяжести на вашей скорости, которая также рассчитывается во времени.

OR

вы можете игнорировать гравитацию и позволить им нажать клавишу S и применить отрицательное ускорение (-2), а затем применить это к вашей скорости, как вы сделали. Это позволит отрицательным значениям ухудшить вашу скорость и может рассматриваться как ваши корабли, включающие двигатели в противоположном направлении.

Конечно, как разработчик игры, вы можете обманывать и не допускать, чтобы ваша скорость когда-либо опускалась ниже нуля (если вы хотите, чтобы игрок двигался только вперед). И когда вы обнаружите, что скорость является отрицательной, установите скорость на 0 и ускорение на 0.

Примечание: ускорение «ухудшается» как для положительного, так и для отрицательного, и оно «ухудшается» до нуля. Вам придется повозиться с этими значениями и протестировать игру, чтобы увидеть, что кажется правильным. Должно ли ускорение ухудшаться на 1 в секунду? Стоит ли вообще использовать значения 2 и -2 в качестве значений ускорения, о которых я упоминал ранее? 2 и -2 могут работать, но, может быть, 3 и -3 лучше? На все эти вопросы вы сможете ответить сами, протестировав его.

Я надеюсь, что это даст вам больше идей и поможет полностью решить ваш вопрос! Дайте мне знать, как это происходит.

person Dean Knight    schedule 15.06.2013
comment
Я немного изменил код, и теперь он больше похож на ваши примеры. setPosition просто устанавливает значения x и y в соответствии с тем, что я там установил. Однако я обнаружил, что в SFML есть метод move, который добавляет текущую позицию. Я хотел бы иметь какое-то трение, которое замедляло бы корабль, пока он плывет в космосе, и это то, что должен делать распад, но он не работает так, как я предполагал. Я пока не уверен, как это исправить. - person MrPlow; 15.06.2013
comment
Спасибо за идеи. У меня уже была мысль о снижении скорости с течением времени вместо использования кадров, как я делал до сих пор. На данный момент этого будет достаточно, так как мне еще нужно многому научиться, прежде чем я смогу возиться с гравитацией, силами и прочим. Спасибо за вашу помощь - person MrPlow; 15.06.2013
comment
Не проблема. Рад, что смог помочь. - person Dean Knight; 15.06.2013