Часто я хочу использовать значение скорости, например, 2,5, для перемещения моего персонажа в пиксельной игре. Обнаружение столкновений, как правило, будет более трудным, если я это сделаю. В итоге я делаю что-то вроде этого:
moveX(2);
if (ticks % 2 == 0) { // or if (moveTime % 2 == 0)
moveX(1);
}
Каждый раз, когда мне приходится писать, я съеживаюсь изнутри, есть ли более чистый способ перемещения персонажа с нецелыми значениями скорости, или я застряну, делая это навсегда?
c++
2d
movement
floating-point
аккумуляторный
источник
источник
Ответы:
Bresenham
В старые времена, когда люди все еще писали свои собственные базовые видео-процедуры для рисования линий и кругов, было весьма неслыханно использовать для этого алгоритм линий Брезенхэма.
Bresenham решает эту проблему: вы хотите нарисовать линию на экране, которая перемещает
dx
пиксели в горизонтальном направлении и в то же время охватываетdy
пиксели в вертикальном направлении. В строках есть свой "плавный" характер; даже если у вас целочисленные пиксели, вы получите рациональные наклонности.Хотя алгоритм должен быть быстрым, это означает, что он может использовать только целочисленную арифметику; и это также уходит без умножения или деления, только сложение и вычитание.
Вы можете адаптировать это для вашего случая:
«x / y» - это не местоположение на экране, а значение одного из ваших измерений во времени. Очевидно, что если ваш спрайт движется в произвольном направлении по экрану, у вас будет несколько брезенхамов, работающих отдельно, 2 для 2D, 3 для 3D.
пример
Допустим, вы хотите переместить своего персонажа простым движением от 0 до 25 вдоль одной из ваших осей. Поскольку он движется со скоростью 2,5, он будет расти там на 10 кадре.
Это то же самое, что «рисование линии» от (0,0) до (10,25). Возьмите алгоритм Брезенхема и дайте ему поработать. Если вы сделаете это правильно (и когда вы изучите это, очень быстро станет ясно, как вы это делаете правильно), то это сгенерирует для вас 11 «очков» (0,0), (1,2), (2, 5), (3,7), (4,10) ... (10,25).
Советы по адаптации
Если вы заглянете в этот алгоритм и найдете какой-то код (на Википедию заключен довольно большой договор), есть несколько вещей, на которые нужно обратить внимание:
dx
иdy
. Вы заинтересованы в одном конкретном случае, хотя (то есть, вы никогда не будете иметьdx=0
).dx
иdy
являются положительными, отрицательными, а также тогоabs(dx)>abs(dy)
или нет. Вы, конечно, также выберите то, что вам нужно здесь. Вы должны быть особенно уверены, что направление, которое увеличивается с1
каждым тактом, всегда является вашим «часовым» направлением.Если вы примените эти упрощения, результат будет действительно очень простым и полностью избавит вас от любых реалов.
источник
Есть отличный способ сделать именно то, что вы хотите.
В дополнение к
float
скорости вам понадобится втораяfloat
переменная, которая будет содержать и накапливать разницу между реальной скоростью и округленной скоростью. Эта разница затем объединяется с самой скоростью.Выход:
источник
Используйте плавающие значения для движения и целочисленные значения для столкновения и рендеринга.
Вот пример:
Когда вы двигаетесь, вы используете,
move()
что накапливает дробные позиции. Но столкновение и рендеринг могут иметь дело с интегральными позициями с помощьюgetPosition()
функции.источник