Предыдущие части: Загрузка уровня, INI-файл, Пишем Питона на Питоне!
Для графической визуализации будем использовать модуль pygame. Основные принципы использования были разъяснены в материалах по игре Robots, поэтому рекомендую их изучить, особенно часть про графику.
Создание окна размером 800*600 и получение графического контекста будет выглядеть так:
import pygame
pygame.init()
ctx = pygame.display.set_mode((800, 600))
Однако я не хочу в игре обращаться к pygame напрямую. Графические библиотеки бывают разные, и может быть, я потом захочу использовать какую-то другую.
Поэтому я сделаю класс-прослойку с собственными методами. Игра будет общаться только с этим классом, а он будет общаться с pygame. Если понадобится заменить pygame на что-то другое, то это будет сделано только внутри класса. В игре ничего менять не придётся.
Итак, сделаем папку app/graphics и поместим в неё файл graphics.py (ссылка):
Класс Graphics в конструкторе получает размеры окна и немедленно создаёт его. Попутно он запоминает размеры в собственных свойствах объекта (screen_width, screen_height), а также создаёт объект класса Rect (свойство rect). Дело в том, что для рисования прямоугольника в pygame необходимо передавать объект типа Rect. В игре Robots этот объект каждый раз создавался заново. Так как прямоугольников придётся рисовать очень много, я решил сделать один объект rect и просто использовать его повторно, меняя в нём координаты. Как это работает, видно в методе draw_rect().
Наконец, метод update() выводит всё нарисованное на экран. Дело всё так же в специфике pygame: все операции рисования делаются в некой области памяти, и только update() копирует эту область целиком на экран. Это хороший и вообще рекомендуемый подход, так как множество мелких операций рисования (а у нас их будут сотни) будет работать быстрее.
Добавим тестовый код в main.py:
from app.graphics.graphics import Graphics
graphics = Graphics(800, 600)
graphics.draw_rect(100,100,100,100,(255,0,0))
graphics.update()
1) импортируем класс Graphics. 2) Создаём объект класса Graphics, передавая в конструктор размеры окна – 800 на 600. 3) Вызываем метод graphics.draw_rect() для рисования квадрата по координатам (100,100), шириной и высотой 100, красного цвета (R=255, G=0, B=0). 4) Вызываем update(), чтобы всё отобразить.
При запуске программы должно появиться окно размером 800*600, а в нём должен нарисоваться красный квадрат. После чего окно сразу закроется. Это нормальное поведение.
Теперь уберём рисование квадрата и вместо него сделаем рисование игрового уровня. Для этого будем перебирать все элементы байтового массива level.data и рисовать квадраты соответствующего цвета (ссылка):
Переменные x и y отвечают за положение квадрата на экране. Когда мы сдвигаемся в массиве на следующий байт, координата x сдвигается на 16 пикселов (это размер квадрата). Переменная offset считает, сколько байтов мы уже перебрали в массиве. Когда мы перебрали количество байтов, равное длине строки (level.line_length), мы обнуляем координату x и увеличиваем координату y на 16, то есть начинаем новую строку. И также обнуляем offset, чтобы он начал считать заново.
Ну и сравниваем байт из массива с нашими значениями: если это стена (Level.WALL), то рисуем серый квадрат (R=128, G=128, B=128), а если поле (Level.FIELD), то бледно-зелёный (R=128, G=255, B=128).
С немного доработанным файлом level.txt мы видим вот такой уровень:
Добавим это всё в main.py, а также сделаем примитивную задержку в конце на 5 секунд, чтобы окно сразу не закрывалось:
import time
time.sleep(5)
Как вы можете догадаться, код рисования уровня не будет оставлен в таком виде. В следующей части мы сделаем для него специальный класс View.
Архив текущего проекта выложен на Яндекс-диск.
Следующая часть: