1.Каркас игры на Pygame

Pygame состоит из нескольких модулей и основан на библиотеке для программирования игр под названием SDL. Эта аббревиатура расшифровывается как «Simple DirectMe­dia Layer» и переводится как «Простой мультимедийный слой». Эта библиотека отвечает за управление графикой, звуком, клавиатурой и мышью.
Установка PyGame

Необходимо написать команду в консоли для пакета установки pip
pip install pygame
Создание первой программы
Pygame задает особые правила построения кода. Эти правила не являются строгими. Однако в большинстве случаев, чтобы игра благополучно запустилась, в программе должна быть соблюдена определенная последовательность вызова ключевых команд.

Эти команды (импорт модуля, вызовы функций, цикл) создают своего рода скелет, или каркас, программного кода. Выполнив его, вы получите "пустую" игру. Далее в этот каркас вписываются объекты и программируется логика игры.

Первое, что нужно сделать, это импортировать модуль pygame. После этого можно вывести на экран главное графическое окно игры с помощью функции set_mode() модуля display, входящего в состав библиотеки pygame:
Создания рабочего окна

from pygame import *

window =display.set_mode((600, 400))   # создание окна с именем window
display.set_caption("Моя игра")             # заголовок окна


Функция set_mode() принимает три аргумента:
1) размер окна в виде кортежа из двух целых чисел,
2) флаги (команды) #можно не указывать
3) глубину цвета. # можно не указывать

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

Флаги предназначены для переключения на аппаратное ускорение, полноэкранный режим, отключения рамки окна и др.

Например, команда display.set_mode((600, 400), pygame.RESIZABLE) делает окно изменяемым в размерах. RESIZABLE - заглавными буквами записываются константы (неизменяемые объекты) в программе.

Функция set_mode() возвращает объект типа Surface (поверхность). В программе может быть множество объектов данного класса, но тот, что возвращает set_mode() особенный. Его называют display surface, что можно перевести как экранная (дисплейная) поверхность. Она главная.

В конечном итоге все отображается на ней с помощью функции display.update() или родственной display.flip(), и именно эту поверхность мы видим на экране монитора. Мы не создавали никаких объектов, поэтому показывается черное окно.
Функции update()
Функции update() и flip() модуля display обновляют содержимое окна игры. Это значит, что каждому пикселю заново устанавливается цвет. Представьте, что на зеленом фоне движется красный круг. За один кадр круг смещается на 5 пикселей. От кадра к кадру картинка целого окна изменяется незначительно, но в памяти окно будет перерисовываться полностью.

Если частота составляет 60 кадров в секунду (FPS=60), то за секунду в памяти компьютера произойдет 60 обновлений множества значений, соответствующих экранным пикселям, что дает по большей части бессмысленную нагрузку на вычислительные мощности.

Если функции update() не передавать аргументы, то будут обновляться значения всей поверхности окна. Однако можно передать более мелкую прямоугольную область или список таковых. В этом случае обновляться будут только они!
Функция flip()
Функция flip() решает проблему иным способом. Она дает выигрыш, если в set_mode() были переданы определенные флаги (аппаратное ускорение + полноэкранный режим -pygame.HWSERFACE | pygame.FULLSCREEN, двойная буферизация – pygame.DOUBLEBUFF, использование OpenGL – pygame.OPENGL). Возможно, все флаги можно комбинировать вместе (через |). При этом, согласно документации, аппаратное ускорение работает только в полноэкранном режиме. : )
Бесконечный цикл
Почему окно сразу закрывается? Очевидно потому, что программа заканчивается после выполнения этих выражений. set_mode() не предполагают входа в "режим циклического ожидания событий".

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

Например, код, создающий классы, объекты и функции не "кладут" в цикл.
Итак, создадим в программе бесконечный цикл:

from pygame import *

window =display.set_mode((600, 400))   # создание окна с именем window
display.set_caption("Моя игра")             # заголовок окна

while True:
     for i in event.get():
        if i.type == QUIT:
            quit()
Рассмотрим выражение event.get(). Модуль event библиотеки pygame содержит функцию get(), которая забирает список событий из очереди, в которую записываются все произошедшие события. То, что возвращает get() – это список. Забранные события удаляются из очереди, то есть второй раз они уже забираться не будут, а в очередь продолжают записываться новые события.

Цикл for просто перебирает список событий. Каждое событие он присваивает переменной i или любой другой. Чтобы было понятней, перепишем программу таким образом:
Вывод событий

from pygame import *

window =display.set_mode((600, 400))   # создание окна с именем window
display.set_caption("Моя игра")             # заголовок окна

while True:
    events = event.get()
    print(events)
    for i in events:
        if i.type == QUIT:
            print(QUIT)
            print(i)
            print(i.type)
            quit()
В pygame событие – это объект класса Event. А если это объект, то у него есть атрибуты (свойства и методы). В данном случае мы отслеживаем только те события, у которых значение свойства type совпадает со значением константы QUIT модуля pygame. Это значение присваивается type тогда, когда происходят события нажатия на крестик или Alt+F4. Когда эти события происходят, то в данном случае мы хотим, чтобы выполнилась функция quit() модуля pygame, которая завершает его работу.

Теперь почему возникает ошибка. Функция quit() отключает pygame, но не завершает работу программы. Таким образом, после выполнения этой функции отключаются модули библиотеки pygame, но выхода из цикла и программы не происходит. Программа продолжает работу и переходит к следующей итерации цикла while.

В данном случае происходит переход к следующей итерации цикла while. И здесь выполнить функцию get() модуля event оказывается уже невозможным. Возникает исключение и программа завершается. Программу завершает не функция pygame.quit(), а выброшенное, но не обработанное, исключение.

Данную проблему можно решить разными способами. Часто используют функцию exit() модуля sys. В этом случае код выглядит примерно так:
Корректное завершение программы

from pygame import *
import sys

window =display.set_mode((600, 400))   # создание окна с именем window
display.set_caption("Моя игра")             # заголовок окна

while True:
    events = event.get()
    print(events)
    for i in events:
        if i.type == QUIT:
            print(QUIT)
            print(i)
            print(i.type)
            quit()
            sys.exit()
C какой скоростью крутится цикл while? С большой и бессмысленно расходует ресурсы. Человек дает команды и воспринимает изменения куда медленнее.

Для обновления экрана в динамической игре часто используют 60 кадров в секунду, а в статической, типа пазла, достаточно будет 30-ти. Из этого следует, что циклу незачем работать быстрее.

Поэтому в главном цикле следует выполнять задержку. Делают это либо вызовом функции delay() модуля time библиотеки pygame, либо создают объект часов и устанавливают ему частоту кадров. Первый способ проще, второй – более профессиональный.
Код с объектом ЧАСЫ

from pygame import *
import sys

window =display.set_mode((600, 400))   # создание окна с именем window
display.set_caption("Моя игра")             # заголовок окна

clock = time.Clock()

while True:
    for i in event.get():
        if i.type == QUIT:
            sys.exit()
    clock.tick(60)
Методу tick() класса Clock передается непосредственно желаемое количество кадров в секунду. Задержку он вычисляет сам. То есть если внутри цикла указано tick(60) – это не значит, что задержка будет 60 миллисекунд или произойдет 60 обновлений экрана за одну итерацию цикла. Это значит, что на каждой итерации цикла секунда делится на 60 и уже на вычисленную величину выполняется задержка.
Структура игры
- base_objects. Файлы классов базовых объектов (шаблонов) для классов объектов игры.
- config_files. Конфигурационные файлы игры.
- game_objects. Файлы с классами объектов игры
- images. Изображения для игры
- sound_effects. Звуковые файлы
- game.py - логика игры
-sample.py - файл запуска игры
Структура игры

import sys

from config_file.color import WHITE
from config_file.config import FPS, HEIGHT, WIDTH
from pygame import QUIT, Surface, display, event, init, time

# здесь происходит инициация,
# создание объектов

init()
window = display.set_mode((HEIGHT, WIDTH))
display.set_caption("Моя игра")
clock = time.Clock()

# Заливка фона
background = Surface(window.get_size())
background = background.convert()
background.fill(WHITE)
# если надо до цикла отобразить
# какие-то объекты, обновляем экран
window.blit(background, (0, 0))
display.flip()

# главный цикл
while True:
    # задержка
    clock.tick(FPS)

    # цикл обработки событий
    for player_event in event.get():
        if player_event.type == QUIT:
            sys.exit()

    # --------
    # изменение объектов
    # --------

    # обновление экрана
    display.flip()