В данной статье разберёмся как устроена физика в 2D играх. Реализуем реалистичное движение персонажа, ускорение, падение, а так же прыжок. Разберёмся с понятиями позиция, скорость, ускорение, сопротивление среды, и гравитация, хитбокс объекта,
Полная инструкция по разработке 1 части игры Марио будет показана в видео прикреплённом ниже.
Физика 2D - это просто!
Что бы объект в игре обладал достаточной механикой для моделирования физических явлений, нужно сделать следующее:
1. Добавить объекту ряд переменных:
- Хитбок который будет отвечать за размер объекта, а так же отслеживать столкновения с другими объектами - playerRect (это специальный объект модуля Pygame);
- Переменные позиции отдельно по оси X и оси Y - px, py (position x, y);
- Переменные скорости по осям - sx, sy (speed x, y);
- Переменные ускорения по осям - ax, ay (acceleration x, y);
- Переменная текущего ускорения, которая будет использоваться для управления движением влево и вправо - accel (acceleration);
- Переменную признака нахождения на поверхности на которой можно прыгать - isStand;
- Переменную отвечающую за сопротивление среды - resist (resistion);
- Переменную отвечающую за силу гравитации;
playerRect = pygame.Rect(0, 0, 50, 50)
px, py = WIDTH // 2, HEIGHT // 2
sx, sy = 0, 0
ax, ay = 0, 0
accel = 0.3
isStand = False
resist = 0.97
gravity = 1
# WIDTH и HEIGHT хранят размер окна игры
Переменная playerRect нужна только для отслеживания столкновения объекта, поэтому при её создании имеет значение только размер прямоугольника (50х50), позиция левого верхнего угла получают значения 0, 0.
2. В игровом цикле, написать вычисления для переменных:
isStand = False
px += sx
sx = (sx + ax) * resist
if fabs(sx) < accel * 0.9: sx = 0
playerRect.centerx = px
py += sy
sy = (sy + ay + gravity) * resist
playerRect.bottom = py
Как всё происходит:
- В начале вычислений isStand принимает значение Ложь, что означает, что объект находиться в воздухе;
- Далее вычисления разделены на две части сначала для оси X, а потом для Y;
- Позиция постоянно увеличивается на значение скорости, что собственно следует из определения скорости (скорость это то расстояние, которое объект проходит за единицу времени. Расстояние в программе измеряются в пикселях, а единица времени это одна игровая итерация);
- Скорость получает новое значение, которое получается путём увеличения старого значения на значение ускорения и умножения получившегося результат на сопротивление среды. Так как resist имеет значение 0.97, то при вычислениях новое значение скорости будет стремиться к нулю.
- Так как умножение на resist никогда не приведёт скорость к полному нулю, необходимо отслеживать значение sx и если оно стало слишком маленьким, то просто приравнивать его к нулю. fabs - это функция стандартного модуля math;
- При вычислении скорости объекта по оси Y добавляется гравитация, благодаря чему скорость будет постоянно стремиться увеличиться, а соответственно позиция по оси Y будет постоянно смещаться вниз до столкновения с каким-либо препятствием;
- После вычислений позиций и скоростей по каждой оси, корректируется положение хитбокса, таким образом, что бы нижняя середина хитбокса располагалась в позиции px, py.
Важно отметить, переменная хитбокса playerRect не способна хранить вещественные значения внеобходимой точности, поэтому используются обычные вещественные переменные px и py, что даёт возможность совершать плавные движения.
3. Ограничить падение объекта нижней границей экрана:
if playerRect.bottom >= HEIGHT:
playerRect.bottom, sy, isStand = HEIGHT, 0, True
py = playerRect.bottom
Данный код не только возвращает объект в нужное место, но и регистрирует тот факт, что объект встал на поверхность (isStand получает значение True).
Так же после любой корректировки позиции хитбокса, нужно обновлять переменные позиции (py = playerRect.bottom).
4. Вывести на экран изображение объекта:
window.fill('black')
pygame.draw.rect(window, 'white', playerRect)
5. Добавить управление объектом:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] or keys[pygame.K_a]: ax = -accel
elif keys[pygame.K_RIGHT] or keys[pygame.K_d]: ax = accel
else: ax = 0
if keys[pygame.K_SPACE] and isStand: sy = -22
Сначала опрашиваем клавиатуру, после чего реагируем на нужные клавиши.
Движение влево и вправо зависит от переменной ax, которая в вычислениях будет влиять на скорость sx, а та в свою очередь на позицию px. Обязательно нужно обнулять ax, если клавиши не нажаты, иначе объект не будет останавливаться.
Для совершения прыжка нужно не только нажать соответствующую клавишу, но ещё необходимо, что бы объект стоял на поверхности. Так же, для уверенного прыжка, изменять нужно не ускорение ay, а сразу скорость sy.
Готовая физика
Давайте посмотрим на весь код целиком:
import pygame
from math import fabs
pygame.init()
WIDTH, HEIGHT = 500, 500
FPS = 60
window = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
playerRect = pygame.Rect(0, 0, 50, 50)
px, py = WIDTH // 2, HEIGHT // 2
sx, sy = 0, 0
ax, ay = 0, 0
accel = 0.3
isStand = False
resist = 0.97
gravity = 1
play = True
while play:
for event in pygame.event.get():
if event.type == pygame.QUIT:
play = False
# Обработка управления
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] or keys[pygame.K_a]: ax = -accel
elif keys[pygame.K_RIGHT] or keys[pygame.K_d]: ax = accel
else: ax = 0
if keys[pygame.K_SPACE] and isStand: sy = -22
# Вычисления
isStand = False
px += sx
sx = (sx + ax) * resist
if fabs(sx) < accel * 0.9: sx = 0
playerRect.centerx = px
py += sy
sy = (sy + ay + gravity) * resist
playerRect.bottom = py
if playerRect.bottom >= HEIGHT:
playerRect.bottom, sy, isStand = HEIGHT, 0, True
py = playerRect.bottom
if isStand: accel, resist = 0.8, 0.9
else: accel, resist = 0.3, 0.97
# Вывод на экран
window.fill('black')
pygame.draw.rect(window, 'white', playerRect)
pygame.display.update()
clock.tick(FPS)
pygame.quit()
Не так уж и много!
Следует обратить внимание, на то, что код игрового цикла, как бы разделён на три части: управление, вычисления, вывод. Всегда следует придерживаться такого подхода и не смешивать в программе код из разных разделов, в дальнейшем это будет иметь очень важное значение!
Ссылка сообщество VK с файлами проекта:
Видео по разработке полноценной физики для игры Super Mario Bros: