Какой лучший способ перемещать спрайт быстрее, чем частота обновления в простой 2d игре?
На данный момент у меня есть спрайт, который я произвольно установил для перемещения на 1 пиксель в секунду. Код в основном такой (код вообще не оптимизирован, я мог бы сделать это намного лучше, но это принцип, который я пытаюсь решить сначала:):
private const long MOVEMENT_SPEED = 10000000; // Ticks in 1 second
private long movementTimeSpan = MOVEMENT_SPEED;
protected void PerformMovement(GameTime gameTime)
{
movementTimeSpan -= gameTime.ElapsedGameTime.Ticks;
if (movementTimeSpan <= 0)
{
// Do the movement of 1 pixel in here, and set movementTimeSpan back to MOVEMENT_SPEED
}
}
Выполнение движения вызывается в цикле, как и следовало ожидать, и это равносильно обновлению около 10 раз в секунду. Поэтому, если я уменьшу MOVEMENT_SPEED, мой спрайт ускорится, но он никогда не будет работать быстрее 10 пикселей в секунду. Для снарядов и прочего я хочу, чтобы он обновлялся намного быстрее, чем этот.
Если я изменю движение на 2 пикселя или более, это создаст проблемы с вычислением столкновений и тому подобное, но это может быть возможно преодолеть.
Другая альтернатива - хранить x и y как число с плавающей точкой, а не как int, и увеличивать значения как часть количества прошедших тиков. Я не уверен, будет ли это создавать плавное движение или нет, так как все еще необходимо некоторое округление.
Так что мой вопрос, кто-нибудь знает стандартный способ?
Должен ли я увеличить значение более чем на 1 пиксель и обновить обнаружение столкновений, чтобы оно стало рекурсивным, следует ли сохранять значения X,Y как плавающие и перемещать как% от прошедшего времени, или есть третий лучший способ сделать это?
2 ответа
Стандартным способом является не обратный отсчет времени для перемещения, а наоборот:
private const float MOVEMENT_SPEED = 10.0f; //pixels per second
private float time;
protected void PerformMovement(GameTime gameTime)
{
time = (float)gameTime.ElapsedGameTime.TotalSeconds;
character.X += MOVEMENT_SPEED * time;
}
Сделайте движение на основе прошедшего времени. Причина, по которой обычно используются поплавки, состоит в том, чтобы получить дробное значение движения. Фиксированная точка - еще одно распространенное дробное представление, но использует int
вместо.
Что касается столкновения, столкновение может быть очень сложным, но в целом вам не обязательно делать это один раз на пиксель движения (как вы предложили с рекурсией); это излишне и приведет к ужасной производительности в кратчайшие сроки. Если у вас возникли проблемы с движением в 2 пикселя, я бы оценил ваши столкновения. В общем, это становится проблематичным, когда вы очень быстро движетесь к точке пропуска тонких стен или даже переходите на "изнаночную сторону" стены, в зависимости от того, как настроено ваше столкновение. Это известно как "туннелирование". Есть много способов решения этой проблемы. Посмотрите здесь и прокрутите вниз до "Предотвращение туннелирования". Как говорится в статье, многие люди ограничивают свою скорость безопасным значением. Но другим распространенным методом является "пошаговое выполнение" вашего алгоритма с меньшими временными шагами, чем в настоящее время. Например, если текущее истекшее время равно 0,1, вы можете перейти на 0,01 в цикле и проверять каждый маленький шаг.
Способ сделать то, что вы просите, хотя и не очень рекомендуется, это увеличить частоту обновления вашей игры до более высокого значения, чем обычные 30 или 60 кадров в секунду, но выводить на экран только каждые N кадров. Вы можете сделать это, просто заставив свой графический движок игнорировать вызовы Draw, пока счетчик или таймер не достигнут желаемого значения.
Конечно, этого решения следует избегать, если только оно не является особенно желательным, поскольку производительность может снижаться довольно быстро по мере увеличения количества обновляемых элементов.
Например, Proun (не игра XNA) использует этот прием именно по вашим причинам.
По умолчанию IsFixedTimeStep = true
XNA ведет себя аналогичным образом, пропуская вызовы Draw, если обновление занимает слишком много времени.