Главная » Программирование игр под Android » НЕМНОГО 3D -ФИЗИКИ – РАЗРАБОТКА ИГР ДЛЯ ОС ANDROID

0

 

В этом разделе мы используем очень простую и достаточно ограниченную физику. Разработчики игр идут на все, чтобы избавиться от сложных вычислений. Поведение объектов в игре не должно быть на 100 % физически точным, оно просто должно быть достаточно реалистичным, чтобы выглядеть правдоподобно. Иногда мы даже не хотим, чтобы поведение было полностью физически достоверным (например, один набор объектов должен падать вниз, а другой такой же – вверх).

Даже в классической игре Супер Марио используется несколько простых принципов ньютоновской физики. Эти принципы действительно достаточно просты и легки в применении. Мы говорим только об абсолютном минимуме, необходимом для реализации очень простой физической модели для наших игровых объектов.

Ньютон и Эйлер – друзья навек

Нас в основном интересует физика перемещения, которая касается изменения положения, скорости, ускорения объекта во времени. Мы работаем с точечной массой, то есть приравниваем все объекты к крайне малым точкам, имеющим ассоциированную с ними массу. Нас, например, не интересует крутящий момент – вращательная скорость объекта, движущегося вокруг центра массы, – поскольку это достаточно сложная проблема, о которой была написана не одна толстая а.

Рассмотрим свойства нашего объекта.

Позиция объекта – это просто вектор в пространстве, в нашем случае в 20-пространстве. Мы представляем его в качестве вектора. Обычно местоположение дается в метрах.

Скорость объекта – это изменение местоположения в секунду. Скорость дается как двухмерный вектор скорости, который представляет собой сочетание вектора направления (длиной в единицу) в сторону, в которую направляется объект, и скорости в метрах в секунду, с которой движется объект. Обратите внимание, что скорость управляет только длиной вектора скорости. Если мы нормализуем вектор скорости, мы получим вектор направления длиной в единицу.

Ускорение объектов – это изменение их скорости в секунду. Мы можем выражать это или в скалярах, которые просто отображают скорость (длину вектора скорости) или как 20-вектор, чтобы у нас было разное ускорение по осям х и у. В данном случае мы выберем второй вариант, поскольку он упрощает работу с такими вещами, как баллистика. Фактически ускорение измеряется в метрах в секунду за секунду (м/с2). Нет, это не опечатка, мы действительно изменяем скорость на какое-то значение, измеряющееся в метрах в секунду каждую секунду.

Когда у нас есть эти свойства объекта для заданного момента времени, мы можем совместить их, чтобы эмулировать путь объекта в мире с течением времени. Может показаться, что звучит это страшновато, но мы уже поступали так с Мистером Номом и нашем BobTest. В тех случаях мы просто не использовали ускорение, мы устанавливали скорость с фиксированным вектором. Вот как можно интегрировать ускорение, скорость и местоположение объекта:

Этот феномен также называется Эйлерова числовая интеграция, и это наиболее понятный метод интеграции из всех используемых в играх. Мы начинаем с точки (0; 0), при этом скорость равна (0; 0), а ускорение – (0; -10). Это означает, что скорость будет возрастать на 1 метр в секунду по оси у. По оси х движения не будет. Перед тем как начнется цикл интеграции, объекты будут находиться в состоянии покоя. Внутри цикла сначала обновляем скорость, основанную на ускорении, умноженном на дельту времени. Вот и все, что скрывается за таким умным словом интеграция.

ПРИМЕЧАНИЕ

Как правило, это даже не полдела. Эйлерова интеграция – это нестабильный метод, которого по возможности следует избегать. Обычно используется другой вариант интеграции, лишь немного более сложный, – интеграция Верле. Однако для наших целей упрощенной интеграции Эйлера вполне достаточно.

Сила и масса

Может возникнуть вопрос: а откуда берется ускорение? Вопрос хороший, и ответов на него много. Ускорение машины берется из двигателя. Двигатель применяет силу к машине, что вызывает ее ускорение. Однако это еще не все. Машина также ускоряется в сторону центра Земли из-за гравитации. Единственная вещь, которая препятствует тому, чтобы она провалилась к центру Земли, это то, что она не может пройти сквозь толщу земной коры. Земля нивелирует силу гравитации. Общая идея выглядит так:

Сила = масса  х ускорение

Мы можем превратить это в следующее уравнение:

Ускорение = сила х масса

Сила выражается в единицах СИ – ньютонах (догадайтесь, кто их придумал). Если мы определяем ускорение как вектор, мы также должны определять силу как вектор. Таким образом, сила может иметь направление. Например, сила гравитации направлена вниз (0; -1). Ускорение также зависит от массы объекта. Чем больше масса объекта, тем большую силу нам нужно применить, чтобы она ускорялась так же быстро, как и объект меньшего веса. Это прямое следствие предыдущего равенства.

Однако для простых игр мы можем игнорировать массу и силу и работать непосредственно со скоростью и ускорением. В предыдущем псевдокоде мы задали ускорение на (0; -10) метров в секунду за секунду (опять повторяю, это не опечатка), что примерно равно ускорению, которое испытает объект, падая на землю вне зависимости от его массы (мы не принимаем во внимание сопротивление воздуха). Это действительно так, Галилей подтвердит.

Манипулируем теоретически

Используем наш предыдущий пример, чтобы поэкспериментировать с объектом, падающим вниз на Землю. Предположим, что мы прошли цикл 10 раз и что getDeltaTimeO всегда будет равно 0,1 секунды. Мы получим следующие местоположения и скорости для каждого раза:

За 1 секунду объект падает на 5,5 м и имеет скорость (0; -10) м/с, направляясь прямо к коре Земли (конечно, до тех пор, как он ударится о землю).

Объект постоянно увеличивает скорость падения, поскольку мы не принимаем во внимание фактор сопротивления воздуха. (Как я говорил раньше, в нашей системе нам никто не запрещает немного жульничать). Мы можем легко узнать текущую максимальную скорость, посмотрев на длину вектора скорости.

Всезнающая Википедия сообщает нам, что человек в свободном падении может достичь максимальной скорости 125 миль в час. Если перевести это в метры в секунду (125 х 1,6 х 1000 / 3600), мы получим 55,5 м/с. Чтобы сделать нашу эмуляцию более реалистичной, мы можем изменить цикл следующим образом:

Поскольку скорость нашего объекта (длина вектора скорости) меньше, чем 55,5 м/с, мы увеличим скорость с помощью ускорения. Когда мы достигли максимальной скорости, мы просто больше не увеличиваем ее ускорением. Такое простое лимитирование скоростей используется во множестве игр.

Мы могли бы добавить ветер в уравнение, указав еще одно ускорение по оси х, допустим (-1; 0) м/с2. Для этого нам нужно просто добавить гравитационное ускорение и ускорение ветра перед тем, как мы прибавим все это к скорости:

Мы можем также не учитывать вообще никакого ускорения и задать объектам фиксированную скорость. Именно так мы раньше поступали в BobTest. Мы изменяли скорость каждого Боба, если он касался края.

Манипулируем на практике

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

Когда пользователь водит пальцем по экрану, пушка следует за ним. Так мы определяем угол, под которым выстрелим ядром.

Когда мы получаем событие касания, выстреливаем ядром в сторону, куда направлена пушка. Начальная скорость ядра будет следовать из направления пушки и изначальной скорости ядра. Скорость равна расстоянию между пушкой и точкой касания. Чем дальше мы касаемся, тем быстрее ядро будет лететь.

Ядро будет лететь до тех пор, пока не появится новое событие касания.

Мы вдвое увеличим размер конуса отображения от (0; 0) до (9,6; 6,4), чтобы мы могли видеть большую часть мира. Кроме того, разместим пушку в (0; 0).

Обратите внимание, что все единицы в игровом мире даются в метрах.

Мы визуализируем ядро как красный квадрат размером 0,2 х 0,2 м, что равно 20 х 20 см. Как мне кажется, достаточно похоже на настоящее ядро. Конечно, если среди вас есть канониры, вы можете выбрать размер пореалистичнее.

Изначально местоположение ядра будет (0; 0) – таким же, как у пушки. Скорость также будет равна (0; 0). Поскольку мы применяем гравитацию в каждом обновлении, ядро просто будет падать прямо и вниз.

Когда получено событие завершения касания (touch up), мы снова задаем положение ядра в точку (0; 0), а его скорость – в значение (Math. cos (cannonAngl е); Math. sin(cannonAngle)). Это гарантирует, что ядро будет лететь в сторону, в которую направлена пушка. Мы также задаем скорость, просто умножим скорость на расстояние между точкой касания и пушкой. Чем ближе точка касания к пушке, тем медленнее будет лететь ядро.

Звучит достаточно просто, реализуем это. Я скопировал код из CannonTest в новый файл CannonGravi tyTest .java. Переименовал классы, которые находятся в этом файле в CannonGravityTest и CannonGravityScreen.

В листинге 8.3 показан CannonGravityScreen.

Листинг 8.3. Фрагмент из CannonGravityTest

Не так уж много изменилось. Мы просто вдвое увеличили размер конуса отображения и отразили это, установив FRUSTUM WIDTH и FRUSTUMHEIGHT на 9,6 и 6,2 соответственно. Это значит, что мы можем видеть в нашем игровом мире прямоугольник размером 9,2 х 6,2. Поскольку мы также хотим нарисовать ядро, я добавил еще один экземпляр класса Vertices, называющийся bail Vertices, который уйдет содержать 4 вершины и 6 индексов прямоугольника ядра. Новые элементы bal 1 Pos-и bal1 Velocity содержат положение и скорость ядра, а элемент gravity – это гравитационное ускорение, которое является константой (0; -10) м/с2.

В конструкторе просто создаем дополнительный экземпляр класса Verti ces для прямоугольника пушки. Снова определяем его в пространстве модели с вершинами (-0,1; -0,1), (0,1; -0,1), (0,1; 0,1) и (-0,1; 0,1). Рисуем с применением индексов, так что в этом случае также определяем шесть вершин.

Метод update также изменился незначительно. Подсчеты точки касания в координатах мира и угла пушки остаются прежними. Первое дополнение заключается в конструкции if внутри цикла обработки событий. Еесли мы получаем событие завершения касания, то готовим наше ядро к выстрелу. Сначала преобразуем угол пушки в радианы, для этого используем FloatMath.cos и FloatMath.sin. Затем подсчитываем расстояние между пушкой и точкой касания. Это будет скорость ядра. После этого устанавливаем местоположение ядра в положение пушки. Наконец, подсчитываем начальную скорость ядра. Применяем синусы и косинусы, как мы обсудили это выше, чтобы создать направление вектора из угла пушки. Умножаем этот вектор на скорость ядра, чтобы получить конечную скорость ядра. Это интересно, поскольку ядро с самого первого момента будет иметь скорость. В реальном мире ядро, конечно, ускорялось бы от 0 м/с до той скорости, которую могло бы достичь с учетом сопротивления воздуха и силы, примененной к нему со стороны пушки. Мы можем здесь схитрить, поскольку это ускорение будет происходить за очень краткий промежуток времени (пара тысячных секунды). Последняя деталь, которая выполняется в методе update , – обновление скорости ядра и в зависимости от этого изменение местоположения.

В методе present  мы просто добавляем визуализацию прямоугольника ядра. Мы делаем это после визуализации треугольника пушки, что означает, что нам нужно очистить модельно-видовую матрицу до того, как мы визуализируем прямоугольник. Делаем это с помощью glLoadIdentity, а затем используем glTranslatefO, чтобы преобразовать прямоугольник ядра из пространства модели в пространство мира, чтобы ядро попало в ту точку, в которой должно находиться.

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

Рис. 8.7. Треугольная пушка, которая стреляет красными прямоугольниками. Впечатляюще.

Этой физики вполне хватит для наших целей. С данной простой моделью мы можем создать гораздо больше ядер. Например, Супер Марио был в основном создан так же. Если вы когда-то играли в Супер Марио, вы заметили, что Марио требуется немного времени, чтобы достичь максимальной скорости при беге. Это реализуется с помощью очень большого ускорения и лимитрирования скорости, как и в предыдущем псевдокоде. Прыжки можно реализовать таким же способом, каким мы запускаем ядра. Текущая скорость Марио будет скорректирована начальным увеличением скорости по оси у (не забывайте, что мы можем складывать скорости так же, как и любые другие векторы). Если бы под его ногами не было земли, мы бы применили гравитационное ускорение, чтобы он снова упал на землю. Скорость по оси х не зависит от скорости по оси у. Мы также можем нажимать кнопки Влево и Вправо, чтобы изменить скорость по оси х. Красота этой простой модели состоит в том, что она позволяет нам реализовать сложное поведение с помощью очень малого количества кода. Мы будем использовать тот же тип физики при написании нашей следующей игры.

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

Источник: Mario Zechner / Марио Цехнер, «Программирование игр под Android», пер. Егор Сидорович, Евгений Зазноба, Издательство «Питер»

По теме:

  • Комментарии