Физика в Game Maker

В этом уроке я планирую объяснить с нуля как использовать физику в GMS (Game maker studio). Для этого используется встроенный движок Box2D который очень хорошо подходит для различных 2d игр.

Итак, для начала посмотрим как включать физику. Для этого создаём объект (например o_hero), нарисуем для него спрайт и поставим галочку "Uses physics". Появилось окно с настройкой физических свойств.

Потом мы его разберём, а сейчас настроим маску для столкновений (физическую). Вверху можно выбрать одну из трёх форм: круг, прямоугольник и выпуклый многоугольник. Для нашего героя больше всего подходит форма прямоугольника.

Ставим галку напротив "Box" и смело кликаем на "Modify Collision Shape":
Фото
Открылось окно с настройкой физической формы объекта. Подгоняем вершины прямоугольника под нашего героя:
Фото
Отлично, форма объекта настроена. Закрываем окно и разберём физические свойства объекта:

-Density - плотность. Чем больше - тем тяжелее. Если плотность равна нулю то объект будет статичным (не будет падать, например пол в платформере)
-Restitution - упругость. Отвечает за "прыгучесть" объекта и его способность объекта отталкивать другие объекты. Пример: если у героя Restitution=0,1, а у пола Restitution=2 то герой будет отскакивать от пола, но не отскакивать от других объектов.
-Collision Group - группа столкновений. Тут немного посложнее:
Если у обоих объектов Collision Group отрицательные (меньше нуля:-1,-5 и т.д) то эти объекты никогда не будут сталкиваться (они будут пролетать друг через друга)
Если у обоих объектов Collision Group положительные (больше нуля: 2, 9 и т.д.) то они будут всегда сталкиваться друг с другом.
По умолчанию группа столкновений равна нулю что значит что программист хочет сам определить столкновения. Для этого у любого из объектов (например у героя) надо добавить стандартное событие столкновения (Add event -> Collision)с другим объектом и добавить туда любое действие. Например ничего не делающий код вроде

//null

Если этого не сделать то объекты будут пролетать друг через друга.
-Linear Damping - замедляет объект с течением времени. Не заменяет трения (о нём ниже), а работает совместно с ним.
-Angular Damping - замедляет вращение объекта с течением времени. 0 - не замедлять объект (однажды получив вращение он будет вращаться вечность). Аналог физического сопротивления среды.
-Friction - трение о поверхность. 0 - трение отсутствует.
СИЛЫ И ДВИЖЕНИЕ
Всё, с настройками объекта разобрались. Теперь можно закрепить результат создание платформера. Создайте ещё один объект пола с настройками плотности (Density) равными нулю и пропишите игроку (объекту o_hero) в событие столкновения код

 //111

или любой другой ничего не делающий. Дальше создайте комнату и в настройках комнаты откройте вкладку physics. Поставьте галочку на "Room is Physics World" (включить физику в комнате) Ниже можно настроить силу и направление гравитации. По умолчанию Gravity X: 0 Y: 10,0 (чем больше значение тем сильнее гравитация). Настройка по умолчанию низкие лучше увеличить Y раза в 4 до 40-50, размещаем в комнате платформы и игрока.
Фото
Тестируем уровень. Если герой падает по всем правилам гравитации то идём дальше, если нет то:
-Попробуйте поднять его над блоками пола. Может он просто находится в равновесии и не собирается падать.
-Проверьте прописано ли у героя событие столкновения с полом.
-Включена ли физика у пола.

Теперь можно добавить игроку движения. С событии шага пишем такой код:

if keyboard_check(vk_left)//если нажата клавиша влево 
{physics_apply_impulse(x,y,-10,0)}//добавить импульс направленный  от текущих координат влево
if keyboard_check(vk_right)//если нажата клавиша вправо
{physics_apply_impulse(x,y,10,0)}//добавить импульс направленный  от текущих координат вправо
 if keyboard_check(vk_up)&& !place_free(x,y+4)//если нажата клавиша вверх  и под ногами есть пол
{physics_apply_impulse(x,y,0,-80)}//добавить импульс направленный  от текущих координат вверх

Я думаю стандартные функции вы знаете, разберём только новые физические.
Функция physics_apply_impulse(xpos, ypos, ximpulse, yimpulse) добавляет импульс (кратковременный "толчок ") герою по направлению к точке ximpulse, yimpulse (относительно точки xpos, ypos) Т.е если взять xpos=x, ypos=y то ximpulse=2, yimpulse=-20 добавится импульс направленный вверх и немного вправо: (не забывайте что ось Y в Game Maker перевёрнута, так что -20 - это вверх, а 20 - вниз)
Фото
Функция physics_apply_force(xpos, ypos, xforce, yforce) используется так же, но в отличии от импульса добавляет постоянную силу действующую по данному вектору.
То есть если physics_apply_impulse означает "пнуть объект к данной точке" то physics_apply_force значит "тянуть объект к точке".
Есть аналогичные функции: physics_apply_local_impulse(xpos, ypos, ximpulse, yimpulse) и physics_apply_local_force(xlocal, ylocal, xforce, yforce). Но они действуют локально, в зависимости от поворота объекта в пространстве. То есть если сделать physics_apply_local_force направленный от координат объекта вверх, то объект будет двигатся туда, куда направлен его перед. Вот отличная картинка из справки по действию локальной силы/импульса:
Фото
Теперь для интереса можете создать динамичный объект "ящик" и прописать игроку событие столкновения с ящиком. Раскидайте их по комнате. Теперь протестируйте. Можно побегать, попрыгать и толкать ящики героем. Поэкспериментируйте с функциями, добавьте блоки льда (скользкий, нет трения), гравия (много трения), резины (большое значение Restitution, упругости).

СВЯЗИ, ВРАЩЕНИЕ И МОТОРЫ
Это было просто. Теперь рассмотрим связи между объектами и вращение объектов. Как ни странно связь нужна для того, что бы связать два физических объекта между собой. Чаще всего будут использоваться только два типа связей: в одной точке и в двух точках.
-При соединении в одной точке объекты будут "прибиты" друг к другу одним гвоздём. То есть они могут вращаться раздельно друг от друга (вокруг своего центра), но если использовать physics_apply_force на одном объекте то он потащим за собой другой объект.
- Соединение в двух точках скрепляет объекты между собой своеобразным невидимым жестким канатом (имеющим постоянную длину). Они могут вращаться друг относительно друга, но не могут приблизится или отдалится друг от друга. Так же они могут вращаться относительно своих центров.
Кстати, задать вращение объекта о котором я постоянно говорю может простая функция:
physics_apply_torque(torque)
-torque - скорость вращения, отрицательное значение - вращение в другую сторону.
Теперь добавим в наш платформер машинку. Нарисуйте прямоугольник (корпус) и круг (колесо). Потом создайте три объекта: корпуса и два колеса. Настройте им стандартную физику, колёсам добавьте столкновение с полом. Теперь в событие Create корпуса напишите код присоединения колёс:

l=physics_joint_revolute_create(o_korpus,o_coleso,o_coleso.x,o_coleso.y,0,0,0,90,10,true,false)
r=physics_joint_revolute_create(o_korpus,o_coleso2,o_coleso2.x,o_coleso2.y,0,0,0,90,10,true,false)

Рассмотрим эту функцию. Она присоединяет объекты o_korpus и o_coleso в одной точке.

Синтаксис:
physics_joint_revolute_create(inst1, inst2, w_anchor_x, w_anchor_y, ang_min_limt, ang_max_limit, ang_limit, max_motor_torque, motor_speed, motor, col)
inst1, inst2 - названия подсоединяемых объектов.
w_anchor_x, w_anchor_y - точка соединения объектов
ang_min_limt, ang_max_limit - максимальный и минимальный углы вращения
ang_limit - активированы ли лимиты вращения (0-нет, 1 -да)
max_motor_torque - максимальный вращающий момент мотора
motor_speed - скорость мотора
col - проверять ли столкновения между объектами
l= и r= это занесение связи в переменную для дальнейшего обращения к этой связи.
Моторы отвечают за вращение объекта. Что бы заставить машину двигаться добавьте следующий код в событие шага:

if keyboard_check(ord('D'))//если нажата клавиша D
{physics_joint_set_value(l, phy_joint_motor_speed, 20);physics_joint_set_value(r, phy_joint_motor_speed, 20)}//включить левый  и правый мотор  на скорость 20
if keyboard_check(ord('A')) //если нажата клавиша A
{physics_joint_set_value(l, phy_joint_motor_speed, -20);physics_joint_set_value(r, phy_joint_motor_speed, -20)}//реверс левого  и правого мотора  на скорость 20
if keyboard_check(vk_nokey) //если  не нажата клавиша
{physics_joint_set_value(l, phy_joint_motor_speed, 0);physics_joint_set_value(r, phy_joint_motor_speed, 0)}//остановить моторы

Функция physics_joint_set_value(joint, field, value) устанавливает значение константы (в данном случае phy_joint_motor_speed - скорость мотора).
joint - индекс связи
field - переменная для изменения
value - новое значение
Теперь расставьте в комнате корпус и колёса и играйте).
На этом урок закончен, надеюсь вам было интересно.