Введение в объектно-ориентированное программирование: создание среды для нашего объекта с Pygame

Предыдущая статья — Введение в объектно-ориентированное программирование (ООП).

Добро пожаловать в следующую часть нашей серии статей про объектно-ориентированное программирование. В этой статье мы построим окружение для нашего объекта при помощи библиотеки Pygame.

Вот наш код из предыдущей статьи:

class Blob:
    
    def __init__(self, color):
        self.x = random.randrange(0, WIDTH)
        self.y = random.randrange(0, HEIGHT)
        self.size = random.randrange(4,8)
        self.color = color

    def move(self):
        self.move_x = random.randrange(-1,2)
        self.move_y = random.randrange(-1,2)
        self.x += self.move_x
        self.y += self.move_y
        
        if self.x < 0: self.x = 0
        elif self.x > WIDTH: self.x = WIDTH
        
        if self.y < 0: self.y = 0
        elif self.y > HEIGHT: self.y = HEIGHT

Итак, мы с нашей Кляксой отлично стартовали. Давайте теперь добавим код для среды, в которой эта Клякса будет существовать:

import pygame
import random

WIDTH = 800
HEIGHT = 600
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
RED = (255, 0, 0)

game_display = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption('Blob World')
clock = pygame.time.Clock() 

Это просто базовый стартовый код и константы. Цвета — это кортежи RGB. Если вы хотите более подробно ознакомиться с библиотекой Pygame, то обратитесь к ее документации. Но, скорей всего, все необходимое вы найдете в наших статьях. Также в наших видео есть дополнительная информация.

Теперь напишем начало функции, которая обрабатывает отрисовку окружения:

def draw_environment():
    game_display.fill(WHITE)
    pygame.display.update()

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

Обычно процесс для простой игры выглядит примерно так:

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

В нашем случае мы просто заполняем экран белым цветом, а затем используем метод .update, чтобы вывести на экран последний кадр.

Теперь напишем функцию main() и условие для прекращения игры:

def main():
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        draw_environment()
        clock.tick(60)

if __name__ == '__main__':
    main() 

В нашем случае это будет выполняться бесконечно, пока не будет запущена процедура pygame.quit (пользователь нажимает клавишу «X», чтобы закрыть окно). При каждом проходе в цикле while True мы проверяем событие выхода, затем отрисовываем экран и с помощью функции clock определяем кадровую частоту (FPS). В данном примере мы установили ее равной 60 кадрам в секунду. Это не значит, что она всегда будет именно такой, просто она не превысит данное значение.

PyGame работает на вашем процессоре и, как вы только что узнали, по умолчанию будет работать на одном ядре, так что это не идеальная стартовая среда. Преобразование PyGame в многопроцессорный движок — довольно сложная задача, и мы не собираемся делать этого здесь. Но мы можем проследить за тем, чтобы наша фактическая игровая логика работала в отдельном процессе или нескольких процессах, и чтобы PyGame, по крайней мере, получала все ресурсы одного ядра.

На данный момент мы можем запустить следующий код:

import pygame
import random

WIDTH = 800
HEIGHT = 600
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
RED = (255, 0, 0)

game_display = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption('Blob World')
clock = pygame.time.Clock()


class Blob:
    
    def __init__(self, color):
        self.x = random.randrange(0, WIDTH)
        self.y = random.randrange(0, HEIGHT)
        self.size = random.randrange(4,8)
        self.color = color

    def move(self):
        self.move_x = random.randrange(-1,2)
        self.move_y = random.randrange(-1,2)
        self.x += self.move_x
        self.y += self.move_y
        
        if self.x < 0: self.x = 0
        elif self.x > WIDTH: self.x = WIDTH
        
        if self.y < 0: self.y = 0
        elif self.y > HEIGHT: self.y = HEIGHT


def draw_environment():
    game_display.fill(WHITE)
    pygame.display.update()
    
def main():
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        draw_environment()
        clock.tick(60)

if __name__ == '__main__':
    main() 

Но пока у нас есть только белое игровое поле. Мы хотим, чтобы наша клякса тоже там была! Давайте создадим объект Blob и добавим его в наше окружение.

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

def main():
    red_blob = Blob(RED)
    ...

В данном случае мы указываем один аргумент, который требуется классу Blob, а именно цвет RED, как было описано в методе __init__. Поэтому данный объект класса Blob будет красным. Теперь давайте передадим этот объект Blob в функцию draw_environment:

draw_environment(red_blob)

И определим саму функцию следующим образом:

def draw_environment(blob):
    game_display.fill(WHITE)
    pygame.draw.circle(game_display, blob.color, [blob.x, blob.y], blob.size)
    pygame.display.update()

Функция Game_display.fill (WHITE) заполнит весь экран белым цветом, затем мы рисуем кляксу в виде круга, а затем обновляем отображение.

Запустив данный код, мы получим красную кляксу на экране:

А что если мы захотим, чтобы наша клякса танцевала? Тогда мы просто добавим в нашу функцию draw_environment метод blob.move(). Мы можем это сделать в любом месте, но добавим его в конец:

def draw_environment(blob):
    game_display.fill(WHITE)
    pygame.draw.circle(game_display, blob.color, [blob.x, blob.y], blob.size)
    pygame.display.update()
    blob.move()

Наш полнй код на данный момент времени:

import pygame
import random

WIDTH = 800
HEIGHT = 600
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
RED = (255, 0, 0)

game_display = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption('Blob World')
clock = pygame.time.Clock()


class Blob:
    
    def __init__(self, color):
        self.x = random.randrange(0, WIDTH)
        self.y = random.randrange(0, HEIGHT)
        self.size = random.randrange(4,8)
        self.color = color

    def move(self):
        self.move_x = random.randrange(-1,2)
        self.move_y = random.randrange(-1,2)
        self.x += self.move_x
        self.y += self.move_y
        
        if self.x < 0: self.x = 0
        elif self.x > WIDTH: self.x = WIDTH
        
        if self.y < 0: self.y = 0
        elif self.y > HEIGHT: self.y = HEIGHT


def draw_environment(blob):
    game_display.fill(WHITE)
    pygame.draw.circle(game_display, blob.color, [blob.x, blob.y], blob.size)
    pygame.display.update()
    blob.move()
    
def main():
    red_blob = Blob(RED)
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        draw_environment(red_blob)
        clock.tick(60)

if __name__ == '__main__':
    main() 

Теперь при запуске этого кода у нас будет прекрасная, извивающаяся клякса!

Следующая статья — Введение в объектно-ориентированное программирование: много клякс.