Введение
В предыдущем посте мы сделали небольшую часть нашей игры flappy. Мы могли летать на нашей птичке и рисовать землю, bg и т.д. Теперь мы продолжаем разработку, создавая трубы в качестве препятствий, и нам нужно создать событие завершения игры и все остальное, чтобы закончить игру. Итак, давайте начнем.
Прочитайте первую часть, если вы еще не читали: Часть-0
Трубы
Давайте создадим трубу, которая будет генерироваться случайным образом из конца экрана и прокручиваться в сторону птицы. Нам нужно управлять птицей, чтобы она не врезалась в трубы. Поэтому сначала создадим класс Pipe
.
# pipe class
class Pipe(pygame.sprite.Sprite):
def __init__(self, x, y, position):
pass
def update(self):
pass
Как мы создали класс птицы, класс трубы имеет метод __init__
, который является конструктором, и метод update
. Мы получаем x, y
и position
, чтобы определить, находится ли труба на возвышении или на земле. Нам нужно объявить переменную pipe_gap
в верхней части кода и установить ее в значение 100.
pipe_gap = 100
Теперь давайте завершим метод __init__
.
def __init__(self, x, y, position):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('img/pipe.png')
self.image = pygame.transform.scale(self.image, (35, 250))
self.rect = self.image.get_rect()
# position 1 is from the top and -1 is from bottom
if position == 1:
self.image = pygame.transform.flip(self.image, False, True)
self.rect.bottomleft = [x, y - int(pipe_gap / 2)]
elif position == -1:
self.rect.topleft = [x, y + int(pipe_gap / 2)]
Сначала мы загружаем изображение трубы, а затем позиционируем ее в соответствии с аргументом position
. Если position = 1
, труба должна быть перевернута, как будто она выходит из неба. А если -1
, то труба должна быть расположена на земле. Убедитесь, что мы учитываем pipe_gap
, когда задаем y
положение труб.
Теперь перейдем к методу обновления.
def update(self):
self.rect.x -= scroll_speed
if self.rect.right < 0:
self.kill()
Здесь мы вычли scroll_speed
из положения x
прямоугольника изображения, чтобы переместить трубу влево. Если труба выйдет за пределы экрана в левую сторону, мы ее уничтожим. Таким образом, мы можем сэкономить немного места. Теперь класс трубы завершен.
Нам нужно создать pipe_group
, как мы создали bird_group
.
pipe_group = pygame.sprite.Group()
Перейдите в цикл и нарисуйте pipe_group
на экране. убедитесь, что она находится прямо над кодом для рисования земли.
# draw pipes
pipe_group.draw(screen)
Теперь мы рисуем pipe_group
на экране, но в pipe_group
нет трубы. Она пуста. Нам нужно добавить ее. Для этого объявите переменную в верхней части кода с именем pipe_frequency
. Установите ее на 1500. Также объявите переменную last_pipe
.
pipe_frequency = 1500
last_pipe = pygame.time.get_ticks() - pipe_frequency
Мы делаем это для того, чтобы каждый раз генерировать трубу после определенной частоты кадров.
Внутри цикла перейдите к позиции, где мы установили код для обновления ground_scroll, внутри условия, проверяющего game_over == False и flying == True
. Прямо внутри условия добавьте следующее.
time_now = pygame.time.get_ticks()
if time_now - last_pipe > pipe_frequency:
pipe_height = random.randint(-50, 50)
btm_pipe = Pipe(screen_width, int(screen_height/2) + pipe_height, -1)
top_pipe = Pipe(screen_width, int(screen_height/2) + pipe_height, 1)
pipe_group.add(btm_pipe)
pipe_group.add(top_pipe)
last_pipe = time_now
pipe_group.update()
Мы проверяем, пришло ли время генерировать новую трубу в соответствии с параметром pipe_frequency. Когда мы увеличиваем pipe_frequency
, труба будет генерироваться на большем расстоянии.
Мы генерируем случайную высоту для трубы и генерируем нижнюю и верхнюю трубы. Затем добавим их в pipe_group
. Установите last_pipe
в time_now
, чтобы сгенерировать другую трубу в соответствии с этим. В заключение, нам нужно генерировать две трубы (одну сверху, а другую снизу) после каждого pipe_frequency
раза кадров. Итак, мы должны проверить, что time_now
— last_pipe's
время больше, чем pipe_frequency
. Вот и все.
Наконец, мы вызываем функцию обновления pipe_group
вне условия.
Попробуйте запустить игру сейчас, и вы увидите, что трубы генерируются с правой стороны, когда птица летит.
Столкновение и конец игры
Теперь у нас нет обнаружения столкновений между птицей и трубами. Нам нужно проверить это, чтобы определить, закончилась игра или нет.
Вот простой код для проверки столкновения. Вставьте его прямо внутрь цикла.
# look for collision
if pygame.sprite.groupcollide(bird_group, pipe_group, False, False) or flappy.rect.top < 0:
game_over = True
В Pygame уже есть метод groupcollide
для проверки столкновения двух групп спрайтов. Мы проверяем условие и устанавливаем game_over
в true.
Нам нужно остановить игру, если птица тоже ударится о землю. Это просто.
# check if bird hit the ground
if flappy.rect.bottom >= 420:
game_over = True
flying = False
Добавьте этот код в цикл, в котором мы проверяем, что спрайт flappy поднимается выше 420
, где находится земля, и установите game_over = true
и flying = false
.
Запустите игру, и вы увидите, что птица падает вниз, когда мы сталкиваемся с трубами. Это потому, что мы уже закодировали коды падения птицы в ее классе.
Оценка
Далее мы можем создать функцию оценки. Сначала объявите переменную в верхней части. Также булевую переменную с именем pass_pipe
.
score = 0
pass_pipe = False
Мы должны проверить, прошла ли птица каждый набор труб, и увеличить переменную на единицу. Вот код, который нужно поместить внутрь цикла.
# check the score
if len(pipe_group) > 0:
if bird_group.sprites()[0].rect.left > pipe_group.sprites()[0].rect.left
and bird_group.sprites()[0].rect.left < pipe_group.sprites()[0].rect.right
and pass_pipe == False:
pass_pipe = True
if pass_pipe == True:
if bird_group.sprites()[0].rect.left > pipe_group.sprites()[0].rect.right:
score += 1
pass_pipe = False
Мы проверяем координаты pipe_group и bird_group и увеличиваем счет в соответствии с этим.
Попробуйте вывести счет в консоль, вы увидите, что он меняется, когда птица пролетает мимо трубы.
print(score);
Текст
Нам не нужно, чтобы счет выводился на консоль или терминал. Нам нужно, чтобы он был внутри игры, внутри графики. Здесь нам нужен рендеринг текста. Давайте создадим функцию draw_text
в верхней части кода.
def draw_text(text, font, text_col, x, y):
img = font.render(text, True, text_col)
screen.blit(img, (x, y))
Мы получаем текст, шрифт, цвет текста и положение x, y в функцию и рендерим шрифт в изображение. Затем рисуем его на экране.
Теперь нам нужно вызвать эту функцию из цикла.
# draw score
draw_text(str(score), font, white, int(screen_width / 2), 20)
В качестве текста здесь используется переменная score, преобразованная в sting. Позиция должна быть в центре сверху. Поэтому мы разделили screen_width
на 2 и установили y равным 20. Мы вызываем переменные font
и white
, которые нужно было объявить раньше, вне цикла.
# define font
font = pygame.font.SysFont('Bauhaus 93', 50)
# define colours
white = (255, 255, 255)
Мы используем шрифт Bauhaus для рендеринга текста. Запустите игру, и вы увидите счет вверху.
Перезапуск
Нам нужно отобразить кнопку перезапуска, если игра закончилась и при нажатии на нее игра должна быть перезапущена. Поэтому объявите класс Button с методом __init__
и update
.
class Button():
def __init__(self, x, y, image):
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = (x, y)
def draw(self):
action = False
pos = pygame.mouse.get_pos()
if self.rect.collidepoint(pos):
if pygame.mouse.get_pressed()[0] == 1:
action = True
screen.blit(self.image, (self.rect.x, self.rect.y))
return action
Нужно получить image
и x, y
в качестве аргументов и установить изображение в соответствии с x, y
.
В методе update мы возвращаем, если мышь щелкнула по кнопке. А также рисуем кнопку на экране.
Теперь инициализируем кнопку,
button = Button(screen_width // 2 - 50, screen_height // 2 - 50, button_img)
Убедитесь, что изображение загружено в переменную button_img
вверху.
button_img = pygame.image.load('img/restart.png')
Теперь нарисуйте кнопку внутри цикла после проверки, что это game_over
.
# check for game over and reset
if game_over:
if button.draw():
game_over = False
score = reset_game()
Здесь мы вызываем функцию reset_game
, которая возвращает начальный счет (0). Объявите эту функцию в самом верху.
def reset_game():
pipe_group.empty()
flappy.rect.x = 100
flappy.rect.y = int(screen_height / 2)
score = 0
return score
Попробуйте поиграть в игру, намеренно нажимайте на трубы, и вы увидите кнопку перезапуска в центре экрана. Нажмите на нее, и игра перезапустится.
Заключение
Надеюсь, вам понравилось разрабатывать Flappy bird. Скоро я вернусь с другим учебником. Оставляйте свои комментарии.
Вот репозиторий Github: Flappy