Как сделать стены в pygame

Обновлено: 19.04.2024

Я пытаюсь создать игру лабиринта (не генератор) для проекта в школе, используя Python/Pygame. Я посмотрел на всем протяжении для способа создать стены и сделать игрока (просто, зеленый реагирует), сталкиваются с этими стенами, и как сделать несколько уровней для этого. Как я сделал бы уровни, как я создам стены, и как я обнаружил бы столкновение между игроком и стенами?

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

Это - вероятно, самый легкий метод, чтобы произвести карты в том, потому что он может сделанным просто, делая множество знаков ASCII и обрабатывая их в Пайтоне, чтобы сделать квадратные "стенные" объекты.

Возьмите эту сетку, например:

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

Таким образом, что делает классы Sprite настолько особенными? Хорошо для одного, вы упомянули, что испытывали концептуальные затруднения при понимании стенного столкновения. У каждого эльфа в Pygame есть его собственное Признак Rect. Реагировать признак - в основном просто невидимый прямоугольник, который используется, чтобы определить вещи как обнаружение столкновений и эльфов рисунка. По определению, в чистой основанной на плитке карте, "столкновение" между предприятиями определяется следующим образом: Два предприятия сталкиваются, если их прямоугольники накладываются друг на друга. Тогда есть метод названного класса Sprite pygame.sprite.groupcollide () . У каждой стены в карте ASCII выше есть ширина, высота и местоположение, определенное их положением во множестве. Таким образом каждый характер хэш-тега непосредственно представляет прямоугольник, который является также квадратом и имеет квадратное "поверхностное" изображение.

Сделайте, чтобы ваш игрок унаследовал классу эльфа и разместил его в одну группу эльфа, "группу игрока". Сделайте, чтобы ваши стенные предприятия унаследовали классу эльфа и разместили их в другую группу эльфа, назвали это "группой препятствия" или чем-то подобным. Все, что необходимо сделать тогда, является требованием pygame.sprite.groupcollide () в каждой структуре вашей петли игры и использования словарь это возвращается, чтобы сказать, сталкивается ли игрок с какими-либо эльфами. Я обеспечил связи с документацией, если какое-либо из этого неясно. Чтение документации Pygame, вероятно, поможет вам понять это лучше, чем мой ответ может.

Так так или иначе то, с чем вы заканчиваете в конце всего этого, является словарем . Я сделаю прямую цитату из документации, чтобы объяснить мою идею:

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

Таким образом, что вы делаете с тем списком? Хорошо его рекомендуемый, что вы определяете свою собственную местную функцию (можно также сделать ее методом класса игрока, если вы желаете) для контакта с этим. Вот моя псевдо реализация кода чрезвычайно высокого уровня, которая не является близко к фактическому коду вообще:

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

Incidentally this type of maze/map structure is used in many popular games including Legend of Zelda, Pacman, Bomberman, Tetris, etc, etc. I couldn't possibly name them all but you get the point. This is a proven method since it seamlessly integrates itself with game design. But don't take my word for it, there is an entire website which explains why tile-based games are so powerful.

Обратите внимание, что этот метод намного более трудно осуществить. Это просто Граф базирующийся. Каждое пространство в графе - узел, который может пересечь игрок. Что определяет, разрешают ли предприятию перемещаться между двумя узлами (другими словами. столкновение на основе принципа ограничение ) то, действительно ли , край существует между теми двумя узлами в ненаправленном основном (или направленный, если вы желаете), граф.

Я действительно не собираюсь объяснять это подробно, потому что просто слишком трудно покрыть в одном ответе. Необходимо будет просто провести собственное исследование, если вы хотите использовать этот метод, но иметь в виду, что это намного более твердо, так как Pygame на самом деле не поддерживает вас очень для этой стратегии. Если вы действительно интересуетесь этим, лучшее место, чтобы начаться, вероятно, Google.

И вот именно! Предпримите попытку с информацией, которую я дал вам и если вы испытываете какие-либо затруднения из-за этого, можно задать другой вопрос здесь или на GameDev StackExchange. В будущем, когда вы задаете вопрос на Так, попытайтесь удостовериться, что это - определенный вопрос о программировании , или вы очень вероятно получите много downvotes.

Это четвёртая из пяти частей туториала, посвящённого созданию игр с помощью Python 3 и Pygame. В третьей части мы углубились в сердце Breakout и узнали, как обрабатывать события, познакомились с основным классом Breakout и увидели, как перемещать разные игровые объекты.

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

Распознавание коллизий

В играх объекты сталкиваются друг с другом, и Breakout не является исключением. В основном с объектами сталкивается мяч. В методе handle_ball_collisions() есть встроенная функция под названием intersect() , которая используется для проверки того, ударился ли мяч об объект, и того, где он столкнулся с объектом. Она возвращает 'left', 'right', 'top', 'bottom' или None, если мяч не столкнулся с объектом.

Столкновение мяча с ракеткой

Когда мяч стукается об ракетку, он отскакивает. Если он ударяется о верхнюю часть ракетки, то отражается обратно вверх, но сохраняет тот же компонент горизонтальной скорости.

Но если он ударяется о боковую часть ракетки, то отскакивает в противоположную сторону (влево или вправо) и продолжает движение вниз, пока не столкнётся с полом. В коде используется функция intersect() .

Столкновение с полом

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

Столкновение с потолком и стенами

Когда мяч ударяется об стены или потолок, он просто отскакивает от них.

Столкновение с кирпичами

Когда мяч ударяется об кирпич, это является основным событием игры Breakout — кирпич исчезает, игрок получает очко, мяч отражается назад и происходят ещё несколько событий (звуковой эффект, а иногда и спецэффект), которые я рассмотрю позже.

Чтобы определить, что мяч ударился об кирпич, код проверят, пересекается ли какой-нибудь из кирпичей с мячом:

Программирование игрового меню

В большинстве игр есть какой-нибудь UI. В Breakout есть простое меню с двумя кнопками, 'PLAY' и 'QUIT'. Меню отображается в начале игры и пропадает, когда игрок нажимает на 'PLAY'. Давайте посмотрим, как реализуются кнопки и меню, а также как они интегрируются в игру.

Создание кнопок

В Pygame нет встроенной библиотеки UI. Есть сторонние расширения, но для меню я решил создать свои кнопки. Кнопка — это игровой объект, имеющий три состояния: нормальное, выделенное и нажатое. Нормальное состояние — это когда мышь не находится над кнопкой, а выделенное состояние — когда мышь находится над кнопкой, но левая кнопка мыши ещё не нажата. Нажатое состояние — это когда мышь находится над кнопкой и игрок нажал на левую кнопку мыши.

Кнопка реализуется как прямоугольник с фоновым цветом и текст, отображаемый поверх него. Также кнопка получает функцию on_click (по умолчанию являющуюся пустой лямбда-функцией), которая вызывается при нажатии кнопки.


Кнопка обрабатывает собственные события мыши и изменяет своё внутреннее состояние на основании этих событий. Когда кнопка находится в нажатом состоянии и получает событие MOUSEBUTTONUP , это означает, что игрок нажал на кнопку, и вызывается функция on_click() .


Свойство back_color , используемое для отрисовки фонового прямоугольника, всегда возвращает цвет, соответствующий текущему состоянию кнопки, чтобы игроку было ясно, что кнопка активна:

Создание меню

Функция create_menu() создаёт меню с двумя кнопками с текстом 'PLAY' и 'QUIT'. Она имеет две встроенные функции, on_play() и on_quit() , которые она передаёт соответствующей кнопке. Каждая кнопка добавляется в список objects (для отрисовки), а также в поле menu_buttons .


При нажатии кнопки PLAY вызывается функция on_play() , удаляющая кнопки из списка objects , чтобы они больше не отрисовывались. Кроме того, значения булевых полей, которые запускают начало игры — is_game_running и start_level — становятся равными True.

При нажатии кнопки QUIT is_game_running принимает значение False (фактически ставя игру на паузу), а game_over присваивается значение True, что приводит к срабатыванию последовательности завершения игры.

Отображение и сокрытие игрового меню

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

Можно создать встроенное меню с собственной поверхностью, которое рендерит свои подкомпоненты (кнопки и другие объекты), а затем просто добавлять/удалять эти компоненты меню, но для такого простого меню это не требуется.

Подводим итог

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

В последней части серии мы рассмотрим завершение игры, отслеживание очков и жизней, звуковые эффекты и музыку.

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

Во второй из пяти частей туториала, посвящённого созданию игр с помощью Python 3 и Pygame, мы рассмотрим класс TextObject , используемый для рендеринга текста на экране. Мы создадим основное окно, в том числе и фоновое изображение, а затем научимся отрисовывать объекты: кирпичи, мяч и ракетку.

Класс TextObject

Класс TextObject предназначен для отображения текста на экране. Можно сделать вывод, что с точки зрения дизайна он должен быть подклассом класса GameObject , потому что тоже является визуальным объектом и его тоже иногда нужно двигать. Но я не хотел вводить глубокую иерархию классов, при которой весь отображаемый Breakout текст оставался на экране неизменным.

Класс TextObject создаёт объект шрифта. Он рендерит текст на отдельную текстовую поверхность, которая затем копируется (рендерится) на основную поверхность. Интересный аспект TextObject заключается в том, что у него нет какого-то фиксированного текста. Он получает функцию text_func() , вызываемую каждый раз, когда он рендерится.

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

Создание основного окна

Игры на Pygame выполняются в окнах. Можно даже сделать так, чтобы они выполнялись в полноэкранном режиме. Сейчас я расскажу, как отобразить пустое окно Pygame. Вы увидите многие элементы, которые мы обсуждали ранее. Сначала вызывается init() Pygame, а затем создаются основная поверхность рисования и таймер.

Затем выполняется основной цикл, который постоянно заполняет экран однотонным серым цветом и вызывает метод таймера tick() с частотой кадров.

Использование фонового изображения

Обычно однотонный цвет фона выглядит не очень интересно. Pygame очень хорошо работает с изображениями. Для Breakout я нашёл любопытную фотографию настоящего космоса, сделанную НАСА. Код очень прост. Сначала он перед основным циклом загружает фоновое изображение с помощью функции pygame.image.load() . Затем вместо того, чтобы заливать экран цветом, он выполняет блиттинг (копирование битов) изображения на экран в позицию (0,0). В результате на экране отображается изображение.


Отрисовка фигур

Pygame может рисовать всё, что угодно. В модуле pygame.draw есть функции для отрисовки следующих фигур:

  • прямоугольника (rect)
  • многоугольника (polygon)
  • круга (circle)
  • эллипса (ellipse)
  • дуги (arc)
  • отрезка (line)
  • отрезков (lines)
  • сглаженного отрезка (anti-aliased line)
  • сглаженных отрезков (anti-aliased lines)

Отрисовка кирпичей

Кирпичи — это просто прямоугольники. В Pygame есть функция pygame.draw.rect() , получающая поверхность, цвет и объект Rect (левую и верхнюю координату, ширину и высоту) и рендерящая прямоугольник. Если дополнительный параметр ширины больше нуля, то он отрисовывает контур. Если ширина равна нулю (значение по умолчанию), то рисует сплошной прямоугольник.

Стоит заметить, что класс Brick является подклассом GameObject и получает все его свойства, но также имеет и цвет, который обрабатывает самостоятельно (потому что могут существовать игровые объекты, имеющие несколько цветов). Поле special_effect мы пока рассматривать не будем.

Отрисовка мяча

Мяч в Breakout — это просто круг. В Pygame есть функция pygame.draw.circle() , получающая цвет, центр, радиус и дополнительный параметр ширины, который по умолчанию равен нулю. Как и в функции pygame.draw.rect() , если ширина равна нулю, то отрисовывается сплошной круг. Ball тоже является подклассом GameObject.

Так как мяч всегда движется (в отличие от кирпичей), он также имеет скорость, которая передаётся для обработки базовому классу GameObject . Класс Ball имеет небольшое отличие — параметры x и y обозначают его центр, а параметры x и y, передаваемые базовому классу GameObject являются верхним левым углом ограничивающего прямоугольника. Чтобы преобразовать центр в верхний левый угол, достаточно вычесть радиус.

Отрисовка ракетки

Ракетка — это ещё один прямоугольник, двигающийся влево и вправо в ответ на нажатия игроком клавиш со стрелками. Это значит, что положение ракетки в разных кадрах может отличаться, но в процессе отрисовки это просто прямоугольник, который должен рендериться в текущей позиции, какой бы она ни была. Вот как выглядит соответствующий код:

Заключение

В этой части мы узнали о классе TextObject и о том, как рендерить текст на экране. Также мы познакомились с тем, как рисовать объекты: кирпичи, мяч и ракетку.

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

До сих пор мы с вами выполняли рисование в клиентской области окна, используя переменную sc:

Эта переменная ссылается на класс Surface, который связан с окном всего приложения. Однако, при необходимости, мы можем создать дополнительные поверхности рисования, используя конструктор класса Surface, например, так:

В результате на поверхности sc будет отображена еще одна поверхность surf со всем своим содержимым:


Спрашивается: зачем создавать дополнительный объект surf, когда можно все рисовать непосредственно на основной поверхности sc? Это сделано главным образом для удобства: мы можем на дополнительной поверхности нарисовать сложную графическую информацию, а затем, переносить ее в область главного окна, просто, используя операцию:

Здесь x, y – координаты верхнего левого угла, от которого позиционируется вторая поверхность surf. Затем, достаточно менять в цикле координаты x, y для создания простой анимации нашего графического образа. Перерисовывать каждый раз сложную графическую информацию уже не придется. В этом основное достоинство таких поверхностей.

Класс Surface имеет множество полезных методов. Подробно все их можно посмотреть на странице официальной документации:

Один из них мы уже использовали – это метод:

который отображает содержимое поверхности source на поверхности Surface. Другой весьма полезный метод:

  • alpha = 0 – полностью прозрачная;
  • alpha = 255 – полностью непрозрачная;

Например, можно сделать так:

Мы здесь определяем две дополнительные поверхности: surf и surf_alpha. Причем, surf_alpha полупрозрачная и располагается на surf. В итоге, имеем такую иерархию поверхностей:


И обратите внимание на такой эффект: поверхность surf_alpha по ширине больше, чем surf. Но, так как surf является родителем для surf_alpha, то все, что выходит за пределы surf – обрезается. В результате, мы видим следующее отображение:


Вот так можно создавать иерархию объектов Surface в PyGame. Причем, если поменять порядок следования слоев:

То поверхность surf также будет отображаться как полупрозрачная – она унаследует это свойство от родителя surf_alpha:


В заключение этого занятия я приведу небольшую программу простой анимации поверхностей (слоев):

Здесь создается два слоя: surf и bita, причем bita рисуется на surf, а surf уже на основной поверхности sc. Далее, в цикле слой surf смещается вниз на 1 пиксель за итерацию, а слой bita на 5 пикселей по горизонтали. После запуска программы мы видим, что слой bita смещается относительно surf, а слой surf движется относительно основного слоя sc. Этот простой пример показывает как можно создавать более сложную анимацию с использованием поверхностей.

Видео по теме












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

Обратите внимание у всех этих функций первым параметром идет surface – поверхность, на которой выполняется рисование. Забегая вперед отмечу, что таких поверхностей можно создавать множество, накладывая друг на друга. Но на этом занятии мы будем использовать базовую поверхность, представляющую клиентскую область окна приложения:


Ссылку на этот объект можно получить в момент создания окна:

(здесь sc – ссылка на базовый объект Surface). Давайте теперь нарисуем в окне прямоугольник с помощью функции rect:

  • 0 – отсутствие цветовой составляющей;
  • 255 – полная насыщенность цветовой составляющей.

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

  • x, y – начальные координаты прямоугольника;
  • width, height – ширина и высота прямоугольника.


Обратите внимание, что координаты откладываются от верхнего левого угла и ось Oy направлена вниз (а не вверх, как мы привыкли по математике).

Итак, указание нарисовать прямоугольник даны. Но если сейчас выполнить программу, то мы ничего не увидим на экране. Почему? Это связано с тем, что базовый объект Surface использует механизм рисования, известный как буферизация вывода.

Ее принцип проще пояснить на таком рисунке. Представим, что объект Surface имеет две стороны A и B:


Изначально мы видим сторону B, но рисование выполняется на противоположной стороне – A. Затем, чтобы отобразить нарисованные объекты, мы должны перевернуть объект Surface другой стороной. В PyGame это делается с помощью функции flip():

Теперь при запуске программы мы видим сторону A с нарисованным прямоугольником. В этом заключается принцип буферизации вывода графической информации. Но спрашивается: зачем все это нужно? Почему бы сразу не рисовать на видимой стороне объекта? В этом случае при большом числе объектов и сложной анимации пользователь будет замечать процесс отрисовки текущего кадра игры. И это негативно скажется на визуальном восприятии игрового процесса. Чтобы перерисовка кадров проходила незаметно для человеческого глаза, как раз и используется механизм буферизации.

Помимо метода flip() можно использовать похожий метод:

Здесь rectangle – это прямоугольная область, которую требуется перерисовать (по умолчанию, если ничего не указано, то перерисовывается вся клиентская область и метод update повторяет метод flip).

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

Итак, мы с вами нарисовали закрашенный прямоугольник. Если нужно нарисовать не закрашенный, то следует указать следующий параметр – толщину линии:

Разумеется, все функции рисования должны следовать до метода update. Наконец, последний важный штрих. Цвета заливки или линий обычно задаются вначале в виде констант, а затем, уже используются в функциях рисования:

Так программа выглядит более читабельной. Давайте теперь посмотрим как рисуются остальные графические примитивы:

Вот такие примитивы существуют в PyGame.

Видео по теме












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

Читайте также: