Создать игру Aliens с использованием Pygame

PythonPythonBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом проекте мы проведем вас через процесс создания простой игры "Aliens" с использованием библиотеки Pygame. В игре нужно защищаться от пришельцев, стреляя их вниз. Мы разобьем процесс разработки на несколько шагов, начиная от настройки файлов проекта и заканчивая запуском готовой игры.

Pygame - популярная библиотека для создания 2D игр на Python. Она предоставляет функции для обработки графики, звука и ввода пользователя, что делает ее отличным выбором для начинающих, интересующихся разработкой игр.

👀 Предварительный просмотр

Игра Aliens

Эта игра была доработана на основе примеров Pygame.

🎯 Задачи

В этом проекте вы научитесь:

  • настраивать начальную структуру проекта и загружать необходимые ресурсы, такие как изображения и звуки;
  • определять классы для игрока и пришельцев;
  • создавать дополнительные классы для обработки взрывов, выстрелов игрока, бомб пришельцев и игрового счета;
  • инициализировать игру, загружать ресурсы и настраивать игровое окно;
  • реализовывать главный игровой цикл, обрабатывать ввод пользователя, обновлять игровые сущности, обрабатывать столкновения и рисовать игровую сцену.

🏆 Достижения

После завершения этого проекта вы сможете:

  • использовать библиотеку Pygame для разработки 2D игры;
  • загружать и отображать изображения в Pygame;
  • обрабатывать ввод пользователя и управлять движением игрока;
  • создавать и обновлять игровые сущности с использованием классов спрайтов;
  • обрабатывать столкновения между игровыми сущностями;
  • рисовать игровую сцену и обновлять экран;
  • воспроизводить звуковые эффекты и музыку в игре;
  • реализовать главный игровой цикл для управления игровой логикой.

Создать файлы проекта

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

Создайте новый файл под названием aliens.py и добавьте следующий код:

cd ~/project
touch aliens.py
sudo pip install pygame

Затем откройте файл в своем любимом текстовом редакторе и добавьте следующий код:

import os
import random
from typing import List

## import basic pygame modules
import pygame as pg

## see if we can load more than standard BMP
if not pg.image.get_extended():
    raise SystemExit("Sorry, extended image module required")


## game constants
MAX_SHOTS = 2  ## most player bullets onscreen
ALIEN_ODDS = 22  ## chances a new alien appears
BOMB_ODDS = 60  ## chances a new bomb will drop
ALIEN_RELOAD = 12  ## frames between new aliens
SCREENRECT = pg.Rect(0, 0, 640, 480)
SCORE = 0

main_dir = os.path.split(os.path.abspath(__file__))[0]


def load_image(file):
    """loads an image, prepares it for play"""
    file = os.path.join(main_dir, "data", file)
    try:
        surface = pg.image.load(file)
    except pg.error:
        raise SystemExit(f'Could not load image "{file}" {pg.get_error()}')
    return surface.convert()


def load_sound(file):
    """because pygame can be compiled without mixer."""
    if not pg.mixer:
        return None
    file = os.path.join(main_dir, "data", file)
    try:
        sound = pg.mixer.Sound(file)
        return sound
    except pg.error:
        print(f"Warning, unable to load, {file}")
    return None

В этом шаге мы импортируем необходимые модули, определяем игровые константы и создаем функции для загрузки изображений и звуковых эффектов. Мы также настраиваем главную директорию проекта.

В def load_image и def load_sound мы используем функцию os.path.join для объединения главной директории проекта с директорией data, где мы будем хранить наши игровые ресурсы. Мы создадим эту директорию на следующем шаге.

✨ Проверить решение и практиковаться

Определить классы игрока и пришельцев

Теперь мы определим классы для персонажей нашей игры: Player и Alien. Класс Player представляет собой персонажа игрока, в то время как класс Alien представляет пришельцев.

#... (код из шага 1)

## Определить класс Player
class Player(pg.sprite.Sprite):
    """Представляет игрока в виде автомобиля-лунохода."""

    speed = 10
    bounce = 24
    gun_offset = -11
    images: List[pg.Surface] = []

    def __init__(self, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect(midbottom=SCREENRECT.midbottom)
        self.reloading = 0
        self.origtop = self.rect.top
        self.facing = -1

    def move(self, direction):
        if direction:
            self.facing = direction
        self.rect.move_ip(direction * self.speed, 0)
        self.rect = self.rect.clamp(SCREENRECT)
        if direction < 0:
            self.image = self.images[0]
        elif direction > 0:
            self.image = self.images[1]
        self.rect.top = self.origtop - (self.rect.left // self.bounce % 2)

    def gunpos(self):
        pos = self.facing * self.gun_offset + self.rect.centerx
        return pos, self.rect.top

Здесь мы определили класс Player. Он включает атрибуты, такие как скорость, отскок и смещение пушки. Метод move обрабатывает движение игрока. Метод gunpos возвращает позицию пушки игрока. Класс Player наследуется от класса pg.sprite.Sprite, который является базовым классом для видимых игровых объектов.

## Определить класс Alien
class Alien(pg.sprite.Sprite):
    """Внешний космический корабль. Медленно движется вниз по экрану."""

    speed = 13
    animcycle = 12
    images: List[pg.Surface] = []

    def __init__(self, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect()
        self.facing = random.choice((-1, 1)) * Alien.speed
        self.frame = 0
        if self.facing < 0:
            self.rect.right = SCREENRECT.right

    def update(self):
        self.rect.move_ip(self.facing, 0)
        if not SCREENRECT.contains(self.rect):
            self.facing = -self.facing
            self.rect.top = self.rect.bottom + 1
            self.rect = self.rect.clamp(SCREENRECT)
        self.frame = self.frame + 1
        self.image = self.images[self.frame // self.animcycle % 3]

Класс Alien представляет пришельцев. Он имеет атрибуты для скорости и анимации. Пришельцы движутся вправо-влево, и их изображения циклируются. Метод update обрабатывает движение и анимацию пришельцев. Класс Alien наследуется от класса pg.sprite.Sprite, который является базовым классом для видимых игровых объектов.

✨ Проверить решение и практиковаться

Определить дополнительные классы игры

В этом шаге мы определим дополнительные классы, связанные с игрой: Explosion, Shot, Bomb и Score. Эти классы обрабатывают взрывы, выстрелы игрока, бомбы пришельцев и игровой счет.

#... (код из шага 2)

## Определить класс Explosion
class Explosion(pg.sprite.Sprite):
    """Взрыв. Надеемся, что это пришельцем, а не игроком!"""

    defaultlife = 12
    animcycle = 3
    images: List[pg.Surface] = []

    def __init__(self, actor, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect(center=actor.rect.center)
        self.life = self.defaultlife

    def update(self):
        """Вызывается каждый раз при проходе игрового цикла.

        Показывает поверхность взрыва в течение 'defaultlife'.
        Каждый игровой тик (обновление), мы уменьшаем 'life'.

        Также мы анимируем взрыв.
        """
        self.life = self.life - 1
        self.image = self.images[self.life // self.animcycle % 2]
        if self.life <= 0:
            self.kill()

## Определить класс Shot
class Shot(pg.sprite.Sprite):
    """Пуля, которую выстреливает спрайт игрока."""

    speed = -11
    images: List[pg.Surface] = []

    def __init__(self, pos, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect(midbottom=pos)

    def update(self):
        """Вызывается каждый раз при проходе игрового цикла.

        Каждый тик мы двигаем выстрел вверх.
        """
        self.rect.move_ip(0, self.speed)
        if self.rect.top <= 0:
            self.kill()

## Определить класс Bomb
class Bomb(pg.sprite.Sprite):
    """Бомба, которую сбрасывают пришельцы."""

    speed = 9
    images: List[pg.Surface] = []

    def __init__(self, alien, explosion_group, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.image = self.images[0]
        self.rect = self.image.get_rect(midbottom=alien.rect.move(0, 5).midbottom)
        self.explosion_group = explosion_group

    def update(self):
        """Вызывается каждый раз при проходе игрового цикла.

        Каждый кадр мы двигаем спрайт 'rect' вниз.
        Когда он достигает дна, мы:

        - создаем взрыв.
        - удаляем Bomb.
        """
        self.rect.move_ip(0, self.speed)
        if self.rect.bottom >= 470:
            Explosion(self, self.explosion_group)
            self.kill()

## Определить класс Score
class Score(pg.sprite.Sprite):
    """Для отслеживания счета."""

    def __init__(self, *groups):
        pg.sprite.Sprite.__init__(self, *groups)
        self.font = pg.font.Font(None, 20)
        self.font.set_italic(1)
        self.color = "white"
        self.lastscore = -1
        self.update()
        self.rect = self.image.get_rect().move(10, 450)

    def update(self):
        """Мы обновляем счет только в update(), когда он изменился."""
        if SCORE!= self.lastscore:
            self.lastscore = SCORE
            msg = f"Score: {SCORE}"
            self.image = self.font.render(msg, 0, self.color)

Эти классы обрабатывают разные аспекты игры, такие как взрывы, выстрелы, бомбы и отображение счета игрока.

✨ Проверить решение и практиковаться

Инициализировать игру и загрузить ресурсы

Теперь мы продолжим инициализацию игры и загрузку необходимых ресурсов, таких как изображения и звуки.

#... (код из шага 3)

def main(winstyle=0):
    ## Initialize pygame
    if pg.get_sdl_version()[0] == 2:
        pg.mixer.pre_init(44100, 32, 2, 1024)
    pg.init()
    if pg.mixer and not pg.mixer.get_init():
        print("Warning, no sound")
        pg.mixer = None

    fullscreen = False
    ## Set the display mode
    winstyle = 0  ## |FULLSCREEN
    bestdepth = pg.display.mode_ok(SCREENRECT.size, winstyle, 32)
    screen = pg.display.set_mode(SCREENRECT.size, winstyle, bestdepth)

    ## Load images, assign to sprite classes
    ## (do this before the classes are used, after screen setup)
    img = load_image("player1.gif")
    Player.images = [img, pg.transform.flip(img, 1, 0)]
    img = load_image("explosion1.gif")
    Explosion.images = [img, pg.transform.flip(img, 1, 1)]
    Alien.images = [load_image(im) for im in ("alien1.gif", "alien2.gif", "alien3.gif")]
    Bomb.images = [load_image("bomb.gif")]
    Shot.images = [load_image("shot.gif")]

Здесь мы инициализируем Pygame, настраиваем игровое окно и загружаем изображения для игрока, пришельцев, взрывов, бомб и выстрелов.

✨ Проверить решение и практиковаться

Завершить настройку игры и главный цикл

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

#... (код из шага 4)

def main(winstyle=0):
    #... (предыдущий код)

    ## украсить игровое окно
    icon = pg.transform.scale(Alien.images[0], (32, 32))
    pg.display.set_icon(icon)
    pg.display.set_caption("Pygame Aliens")
    pg.mouse.set_visible(0)

    ## создать фон, нарисовать тайл bgd-изображения
    bgdtile = load_image("background.gif")
    background = pg.Surface(SCREENRECT.size)
    for x in range(0, SCREENRECT.width, bgdtile.get_width()):
        background.blit(bgdtile, (x, 0))
    screen.blit(background, (0, 0))
    pg.display.flip()

    ## загрузить звуковые эффекты
    boom_sound = load_sound("boom.wav")
    shoot_sound = load_sound("car_door.wav")
    if pg.mixer:
        music = os.path.join(main_dir, "data", "house_lo.wav")
        pg.mixer.music.load(music)
        pg.mixer.music.play(-1)

    ## Initialize Game Groups
    aliens = pg.sprite.Group()
    shots = pg.sprite.Group()
    bombs = pg.sprite.Group()
    all = pg.sprite.RenderUpdates()
    lastalien = pg.sprite.GroupSingle()

    ## Create Some Starting Values
    alienreload = ALIEN_RELOAD
    clock = pg.time.Clock()

    ## инициализировать на стартовых спрайтов
    global SCORE
    player = Player(all)
    Alien(
        aliens, all, lastalien
    )  ## note, this 'lives' because it goes into a sprite group
    if pg.font:
        all.add(Score(all))

    ## Запускать главный цикл, пока игрок жив.
    while player.alive():
        ## получить ввод
        for event in pg.event.get():
            if event.type == pg.QUIT:
                return
            if event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE:
                return
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_f:
                    if not fullscreen:
                        print("Changing to FULLSCREEN")
                        screen_backup = screen.copy()
                        screen = pg.display.set_mode(
                            SCREENRECT.size, winstyle | pg.FULLSCREEN, bestdepth
                        )
                        screen.blit(screen_backup, (0, 0))
                    else:
                        print("Changing to windowed mode")
                        screen_backup = screen.copy()
                        screen = pg.display.set_mode(
                            SCREENRECT.size, winstyle, bestdepth
                        )
                        screen.blit(screen_backup, (0, 0))
                    pg.display.flip()
                    fullscreen = not fullscreen

        keystate = pg.key.get_pressed()

        ## очистить/стирать последние нарисованные спрайты
        all.clear(screen, background)

        ## обновить все спрайты
        all.update()

        ## обработать ввод игрока
        direction = keystate[pg.K_RIGHT] - keystate[pg.K_LEFT]
        player.move(direction)
        firing = keystate[pg.K_SPACE]
        if not player.reloading and firing and len(shots) < MAX_SHOTS:
            Shot(player.gunpos(), shots, all)
            if pg.mixer and shoot_sound is not None:
                shoot_sound.play()
        player.reloading = firing

        ## Создать нового пришельца
        if alienreload:
            alienreload = alienreload - 1
        elif not int(random.random() * ALIEN_ODDS):
            Alien(aliens, all, lastalien)
            alienreload = ALIEN_RELOAD

        ## Сбросить бомбы
        if lastalien and not int(random.random() * BOMB_ODDS):
            Bomb(lastalien.sprite, all, bombs, all)

        ## Определить столкновения между пришельцами и игроками.
        for alien in pg.sprite.spritecollide(player, aliens, 1):
            if pg.mixer and boom_sound is not None:
                boom_sound.play()
            Explosion(alien, all)
            Explosion(player, all)
            SCORE = SCORE + 1
            player.kill()

        ## Посмотреть, попадают ли выстрелы в пришельцев.
        for alien in pg.sprite.groupcollide(aliens, shots, 1, 1).keys():
            if pg.mixer and boom_sound is not None:
                boom_sound.play()
            Explosion(alien, all)
            SCORE = SCORE + 1

        ## Посмотреть, попадает ли бомба пришельца в игрока.
        for bomb in pg.sprite.spritecollide(player, bombs, 1):
            if pg.mixer and boom_sound is not None:
                boom_sound.play()
            Explosion(player, all)
            Explosion(bomb, all)
            player.kill()

        ## нарисовать сцену
        dirty = all.draw(screen)
        pg.display.update(dirty)

        ## ограничить частоту кадров в 40fps. Также называется 40HZ или 40 раз в секунду.
        clock.tick(40)

    if pg.mixer:
        pg.mixer.music.fadeout(1000)
    pg.time.wait(1000)


## вызвать функцию "main", если запускается этот скрипт
if __name__ == "__main__":
    main()
    pg.quit()

В функции main мы инициализируем группы для различных игровых объектов, создаем начальные значения и реализуем главный игровой цикл. В этом цикле выполняется игровая логика, обработка ввода игрока, столкновения и отрисовка игровой сцены.

Запуск игры

Теперь мы можем запустить игру, выполнив следующую команду:

cd ~/project
python aliens.py
✨ Проверить решение и практиковаться

Запуск игры

Теперь мы можем запустить игру, выполнив следующую команду:

cd ~/project
python aliens.py
Скриншот выполнения игры

Обзор

В этом проекте мы разделили процесс создания простой игры "Aliens" с использованием Pygame на несколько шагов. Мы настроили проектные файлы, определили классы игрока и пришельцев, создали дополнительные классы, связанные с игрой, и инициализировали игру с ресурсами. Наконец, мы реализовали главный игровой цикл для обработки игровой логики и взаимодействий.

В следующих шагах мы продолжим добавлять функциональность в классы игрока и пришельцев, обрабатывать движение и стрельбу, и обновлять визуальные элементы игры.