Информационно развлекательный портал
Поиск по сайту

Программирование игр на питоне. Код игры «Угадай число». Столкновение с полом

  • Перевод

Многие разработчики приходят в разработку ПО, потому что хотят создавать игры. Не все могут стать профессиональными разработчиками игр, но любой может создавать собственные игры из интереса (а может быть, и с выгодой). В этом туториале, состоящем из пяти частей, я расскажу вам, как создавать двухмерные однопользовательские игры с помощью Python 3 и замечательного фреймворка PyGame.

(Остальные части туториала: вторая , третья , четвёртая , пятая .)

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

Мы реализуем следующие функции и возможности:

  • простые стандартные GameObject и TextObject
  • простой стандартный Game object
  • простая стандартная кнопка
  • файл конфигурации
  • обработка событий клавиатуры и мыши
  • кирпичи, ракетка и мяч
  • управление движением ракетки
  • обработка коллизий мяча с объектами игры
  • фоновое изображение
  • звуковые эффекты
  • расширяемая система спецэффектов
Не стоит ожидать , что игра будет очень красива графически. Я программист, а не художник, меня больше интересует эстетика кода. Созданный мной дизайн может неприятно удивить. С другой стороны, у вас будут почти неограниченные возможности по улучшению графики этой версии Breakout. Если вы отважитесь повторять за мной, посмотрите на скриншот:

Готовый исходный код выложен .

Краткое введение в программирование игр

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

Основной цикл

Основной цикл (main loop) игры выполняется и обновляет экран через фиксированные интервалы времени. Они называются частотой кадров и определяют плавность перемещения. Обычно игры обновляют экран 30-60 раз в секунду. Если частота будет меньше, то покажется, что объекты на экране дёргаются.

Внутри основного цикла есть три основных операции: обработка событий, обновление состояния игры и отрисовка текущего состояния на экране.

Обработка событий

События в игре состоят из всего, что происходит за пределами управления кода игры, но относится к выполнению игры. Например, если в Breakout игрок нажимает клавишу «стрелка влево», то игре нужно переместить ракетку влево. Стандартными событиями являются нажатия (и отжатия) клавиш, движение мыши, нажатия кнопок мыши (особенно в меню) и события таймера (например, действие спецэффекта может длиться 10 секунд).

Обновление состояния

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

Существует также вспомогательное состояние, позволяющее управлять игрой:

  • Отображается ли сейчас меню?
  • Закончена ли игра?
  • Победил ли игрок?

Отрисовка

Игре нужно отображать своё состояние на экране, в том числе отрисовывать геометрические фигуры, изображения и текст.

Игровая физика

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

В более сложных играх могут использоваться более изощрённые и реалистичные физические системы (особенно в 3D-играх). Стоит также отметить, что в некоторых играх, например, в карточных, физики почти нет, и это совершенно нормально.

ИИ (искусственный интеллект)

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

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

Воспроизведение звука

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

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

Жизни, очки и уровни

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

Знакомство с Pygame

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

Что такое Pygame?

Pygame - это фреймворк языка Python для программирования игр. Он создан поверх SDL и обладает всем необходимым:
  • зрелостью
  • хорошим сообществом
  • открытым исходным кодом
  • кроссплатформенностью
  • качественной документацией
  • множеством примеров игр
  • простотой изучения

Установка Pygame

Введите pip install pygame , чтобы установить фреймворк. Если вам нужно что-то ещё, то следуйте инструкциям из раздела Getting Started в Wiki проекта. Если у вас, как и у меня, macOS Sierra, то могут возникнуть проблемы. Мне удалось установить Pygame без сложностей, и код работает отлично, но окно игры никогда не появляется.

Это станет серьёзным препятствием при запуске игры. В конце концов мне пришлось запускать её в Windows внутри VirtualBox VM. Надеюсь, ко времени прочтения этой статьи проблема будет решена.

Архитектура игры

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

Структура папок и файлов

├── Pipfile ├── Pipfile.lock ├── README.md ├── ball.py ├── breakout.py ├── brick.py ├── button.py ├── colors.py ├── config.py ├── game.py ├── game_object.py ├── images │ └── background.jpg ├── paddle.py ├── sound_effects │ ├── brick_hit.wav │ ├── effect_done.wav │ ├── level_complete.wav │ └── paddle_hit.wav └── text_object.py
Pipfile и Pipfile.lock - это современный способ управления зависимостями в Python. Папка images содержит изображения, используемые игрой (в нашей версии будет только фоновое изображение), а в папке sound_effects directory лежат короткие звуковые клипы, используемые (как можно догадаться) в качестве звуковых эффектов.

Файлы ball.py, paddle.py и brick.py содержат код, относящийся к каждому из этих объектов Breakout. Подробнее я рассмотрю их в следующих частях туториала. Файл text_object.py содержит код отображения текста на экране, а в файле background.py содержится игровая логика Breakout.

Однако существует несколько модулей, создающих произвольный «скелет» общего назначения. Определённые в них классы можно будет использовать в других играх на основе Pygame.

Класс GameObject

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

From pygame.rect import Rect class GameObject: def __init__(self, x, y, w, h, speed=(0,0)): self.bounds = Rect(x, y, w, h) self.speed = speed @property def left(self): return self.bounds.left @property def right(self): return self.bounds.right @property def top(self): return self.bounds.top @property def bottom(self): return self.bounds.bottom @property def width(self): return self.bounds.width @property def height(self): return self.bounds.height @property def center(self): return self.bounds.center @property def centerx(self): return self.bounds.centerx @property def centery(self): return self.bounds.centery def draw(self, surface): pass def move(self, dx, dy): self.bounds = self.bounds.move(dx, dy) def update(self): if self.speed == : return self.move(*self.speed)
GameObject предназначен для того, чтобы быть базовым классом для других объектов. Он непосредственно раскрывает множество свойств его прямоугольника self.bounds, а в своём методе update() он перемещает объект в соответствии с его текущей скоростью. Он ничего не делает в своём методе draw() , который должен быть переопределён подклассами.

Класс Game

Класс Game - это ядро игры. Он выполняется в основном цикле. В нём есть множество полезных возможностей. Давайте разберём его метод за методом.

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

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

Import pygame import sys from collections import defaultdict class Game: def __init__(self, caption, width, height, back_image_filename, frame_rate): self.background_image = \ pygame.image.load(back_image_filename) self.frame_rate = frame_rate self.game_over = False self.objects = pygame.mixer.pre_init(44100, 16, 2, 4096) pygame.init() pygame.font.init() self.surface = pygame.display.set_mode((width, height)) pygame.display.set_caption(caption) self.clock = pygame.time.Clock() self.keydown_handlers = defaultdict(list) self.keyup_handlers = defaultdict(list) self.mouse_handlers =
Методы update() и draw() очень просты. Они обходят все управляемые игровые объекты и вызывают соответствующие им методы. Если два объекта накладываются друг на друга на экране, то порядок списка объектов определяет, какой из них будет рендериться первым, а остальные будут частично или полностью его перекрывать.

Def update(self): for o in self.objects: o.update() def draw(self): for o in self.objects: o.draw(self.surface)
Метод handle_events() слушает события, генерируемые Pygame, такие как события клавиш и мыши. Для каждого события он вызывает все функции-обработчики, которые должны обрабатывать события соответствующих типов.

Def handle_events(self): for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() elif event.type == pygame.KEYDOWN: for handler in self.keydown_handlers: handler(event.key) elif event.type == pygame.KEYUP: for handler in self.keydown_handlers: handler(event.key) elif event.type in (pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP, pygame.MOUSEMOTION): for handler in self.mouse_handlers: handler(event.type, event.pos)
Наконец, метод run() выполняет основной цикл. Он выполняется до тех пор, пока элемент game_over не принимает значение True. В каждой итерации он рендерит фоновое изображение и вызывает по порядку методы handle_events() , update() и draw() .

Затем он обновляет экран, то есть записывает на физический дисплей всё содержимое, которое было отрендерено на текущей итерации. И последнее, но не менее важное - он вызывает метод clock.tick() для управления тем, когда будет вызвана следующая итерация.

Def run(self): while not self.game_over: self.surface.blit(self.background_image, (0, 0)) self.handle_events() self.update() self.draw() pygame.display.update() self.clock.tick(self.frame_rate)

Заключение

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

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

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

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

Обработка событий

В Breakout есть три типа событий: события нажатий клавиш, события мыши и события таймера. Основной цикл в классе Game обрабатывает нажатия клавиш и события мыши и передаёт их подписчикам (вызывая функцию-обработчик).

Хотя класс Game очень общий и не обладает знаниями о реализации Breakout, сама подписка и способы обработки событий очень специфичны.

Класс Breakout

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

Нужно учесть, что все события клавиш (и для левой, и для правой «стрелки») передаются одному методу-обработчику ракетки.

# Register the handle_mouse_event() method of a button object self.mouse_handlers.append(b.handle_mouse_event) # Register the handle() method of the paddle to handle key events self.keydown_handlers.append(paddle.handle) self.keydown_handlers.append(paddle.handle) self.keyup_handlers.append(paddle.handle) self.keyup_handlers.append(paddle.handle)

Обработка нажатий клавиш

Класс Game вызывает зарегистрированные обработчики для каждого события клавиш и передаёт клавишу. Заметьте, что это не класс Paddle. В Breakout единственный объект, который интересуют подобные события - это ракетка. При нажатии или отпускании клавиши вызывается его метод handle() .

Объекту Paddle не нужно знать, было ли это событие нажатия или отпускания клавиши, потому что он управляет текущим состоянием с помощью пары булевых переменных: moving_left и moving_right . Если moving_left равна True, то значит, была нажата клавиша «влево», и следующим событием будет отжатие клавиши, которое сбросит переменную. То же самое относится и к клавише «вправо». Логика проста и заключается в переключении состояния этих переменных в ответ на любое событие.

Def handle(self, key): if key == pygame.K_LEFT: self.moving_left = not self.moving_left else: self.moving_right = not self.moving_right

Обработка событий мыши

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

Def handle_mouse_event(self, type, pos): if type == pygame.MOUSEMOTION: self.handle_mouse_move(pos) elif type == pygame.MOUSEBUTTONDOWN: self.handle_mouse_down(pos) elif type == pygame.MOUSEBUTTONUP: self.handle_mouse_up(pos) def handle_mouse_move(self, pos): if self.bounds.collidepoint(pos): if self.state != "pressed": self.state = "hover" else: self.state = "normal" def handle_mouse_down(self, pos): if self.bounds.collidepoint(pos): self.state = "pressed" def handle_mouse_up(self, pos): if self.state == "pressed": self.on_click(self) self.state = "hover"

Заметьте, что метод handle_mouse_event() , зарегистрированный для получения событий мыши, проверяет тип события и переадресует его к соответствующему методу, обрабатывающему этот тип события.

Обработка событий таймера

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

Ещё одной ситуацией является необходимость приостановки игры. Например, при отображении сообщения, которое игрок должен прочитать и чтобы при этом ничего его не отвлекало. Метод show_message() класса Breakout использует такой подход и вызывает time.sleep() . Вот соответствующий код:

Import config as c class Breakout(Game): def show_message(self, text, color=colors.WHITE, font_name="Arial", font_size=20, centralized=False): message = TextObject(c.screen_width // 2, c.screen_height // 2, lambda: text, color, font_name, font_size) self.draw() message.draw(self.surface, centralized) pygame.display.update() time.sleep(c.message_duration)

Игровой процесс

Игровой процесс (геймплей) - это то место, в котором вступают в дело правила Breakout. Геймплей заключается в перемещении различных объектов в ответ на события и в изменении состояния игры на основании их взаимодействий.

Перемещение ракетки

Вы видели ранее, что класс Paddle реагирует на нажатия клавиш со стрелками, обновляя свои поля moving_left и moving_right . Само движение происходит в методе update() . Здесь выполняются определённые вычисления, если ракетка находится близко к левой или правой границе экрана. Мы не хотим, чтобы ракетка выходила за границы экрана (с учётом заданного смещения).

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

Import pygame import config as c from game_object import GameObject class Paddle(GameObject): def __init__(self, x, y, w, h, color, offset): GameObject.__init__(self, x, y, w, h) self.color = color self.offset = offset self.moving_left = False self.moving_right = False ... def update(self): if self.moving_left: dx = -(min(self.offset, self.left)) elif self.moving_right: dx = min(self.offset, c.screen_width - self.right) else: return self.move(dx, 0)

Перемещение мяча

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

Задание исходной скорости мяча

Мяч в Breakout возникает из ниоткуда в самом начале игры каждый раз, когда игрок теряет жизнь. Он просто материализуется из эфира и начинает падать или ровно вниз, или под небольшим углом. Когда мяч создаётся в методе create_ball(), он получает скорость со случайным горизонтальным компонентом в промежутке от -2 до 2 и вертикальным компонентом, задаваемым в модуле config.py (по умолчанию задано значение 3).

Def create_ball(self): speed = (random.randint(-2, 2), c.ball_speed) self.ball = Ball(c.screen_width // 2, c.screen_height // 2, c.ball_radius, c.ball_color, speed) self.objects.append(self.ball)

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

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

Помните также, что у нас есть для продажи и для изучения в Envato Market , если вы хотите изучить больше материала, связанного с Python.

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

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




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

(Остальные части туториала: первая , вторая , третья , пятая .)

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

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

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

Def handle_ball_collisions(self): def intersect(obj, ball): edges = dict(left=Rect(obj.left, obj.top, 1, obj.height), right=Rect(obj.right, obj.top, 1, obj.height), top=Rect(obj.left, obj.top, obj.width, 1), bottom=Rect(obj.left, obj.bottom, obj.width, 1)) collisions = set(edge for edge, rect in edges.items() if ball.bounds.colliderect(rect)) if not collisions: return None if len(collisions) == 1: return list(collisions) if "top" in collisions: if ball.centery >= obj.top: return "top" if ball.centerx < obj.left: return "left" else: return "right" if "bottom" in collisions: if ball.centery >= obj.bottom: return "bottom" if ball.centerx < obj.left: return "left" else: return "right"

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

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

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

# Удар об ракетку s = self.ball.speed edge = intersect(self.paddle, self.ball) if edge is not None: self.sound_effects["paddle_hit"].play() if edge == "top": speed_x = s speed_y = -s if self.paddle.moving_left: speed_x -= 1 elif self.paddle.moving_left: speed_x += 1 self.ball.speed = speed_x, speed_y elif edge in ("left", "right"): self.ball.speed = (-s, s)

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

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

# Удар об пол if self.ball.top > c.screen_height: self.lives -= 1 if self.lives == 0: self.game_over = True else: self.create_ball()

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

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

# Удар об потолок if self.ball.top < 0: self.ball.speed = (s, -s) # Удар об стену if self.ball.left < 0 or self.ball.right > c.screen_width: self.ball.speed = (-s, s)

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

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

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

# Удар об кирпич for brick in self.bricks: edge = intersect(brick, self.ball) if not edge: continue self.bricks.remove(brick) self.objects.remove(brick) self.score += self.points_per_brick if edge in ("top", "bottom"): self.ball.speed = (s, -s) else: self.ball.speed = (-s, s)

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

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

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

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

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

Import pygame from game_object import GameObject from text_object import TextObject import config as c class Button(GameObject): def __init__(self, x, y, w, h, text, on_click=lambda x: None, padding=0): super().__init__(x, y, w, h) self.state = "normal" self.on_click = on_click self.text = TextObject(x + padding, y + padding, lambda: text, c.button_text_color, c.font_name, c.font_size) def draw(self, surface): pygame.draw.rect(surface, self.back_color, self.bounds) self.text.draw(surface)
Кнопка обрабатывает собственные события мыши и изменяет своё внутреннее состояние на основании этих событий. Когда кнопка находится в нажатом состоянии и получает событие MOUSEBUTTONUP , это означает, что игрок нажал на кнопку, и вызывается функция on_click() .

Def handle_mouse_event(self, type, pos): if type == pygame.MOUSEMOTION: self.handle_mouse_move(pos) elif type == pygame.MOUSEBUTTONDOWN: self.handle_mouse_down(pos) elif type == pygame.MOUSEBUTTONUP: self.handle_mouse_up(pos) def handle_mouse_move(self, pos): if self.bounds.collidepoint(pos): if self.state != "pressed": self.state = "hover" else: self.state = "normal" def handle_mouse_down(self, pos): if self.bounds.collidepoint(pos): self.state = "pressed" def handle_mouse_up(self, pos): if self.state == "pressed": self.on_click(self) self.state = "hover"
Свойство back_color , используемое для отрисовки фонового прямоугольника, всегда возвращает цвет, соответствующий текущему состоянию кнопки, чтобы игроку было ясно, что кнопка активна:

@property def back_color(self): return dict(normal=c.button_normal_back_color, hover=c.button_hover_back_color, pressed=c.button_pressed_back_color)

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

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

Def create_menu(self): for i, (text, handler) in enumerate((("PLAY", on_play), ("QUIT", on_quit))): b = Button(c.menu_offset_x, c.menu_offset_y + (c.menu_button_h + 5) * i, c.menu_button_w, c.menu_button_h, text, handler, padding=5) self.objects.append(b) self.menu_buttons.append(b) self.mouse_handlers.append(b.handle_mouse_event)
При нажатии кнопки PLAY вызывается функция on_play() , удаляющая кнопки из списка objects , чтобы они больше не отрисовывались. Кроме того, значения булевых полей, которые запускают начало игры - is_game_running и start_level - становятся равными True.

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

Def on_play(button): for b in self.menu_buttons: self.objects.remove(b) self.is_game_running = True self.start_level = True def on_quit(button): self.game_over = True self.is_game_running = False

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

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

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

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

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

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

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

Вы можете помочь и перевести немного средств на развитие сайта

Вступление

Статья расчитана на тех, кто имеет минимальные познания в Python .
Наверняка все вы знакомы с браузерными MMO(RPG) играми. Такие в наше время можно встретить на просторах интернета часто. Это могут быть как простейшие текстовые игры, так и агрегаты на Flash или HTML5. Но не забываем, что одному другое не мешает. Вы вполне можете использовать множество технологий в одной игре.
Сейчас мы рассмотрим с вами процесс создания веб-сайта на языке программирования Python . Конечно, есть конструкторы, которые позволяют создать подобные игры за считанные минуты (), но опыт программирования и собственное творение с нуля нам тоже не помешает.

Установка

Предполагается, что вы имеете установленный Python . Если же нет, рекомендуем скачать с официального сайта и произвести инсталляцию на ваш компьютер или сервер.
В данном примере мы будем использовать Web.Python 0.37 , однако при желании вы можете установить последнюю версию .
Распакуйте и скопируйте папку web в директорию с приложением, или же просто выполните команду:

200?"200px":""+(this.scrollHeight+5)+"px");">python setup.py install


Некоторые UNIX-подобные системы требуют выполнение команды от имени администратора:

200?"200px":""+(this.scrollHeight+5)+"px");">sudo python setup.py install


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

200?"200px":""+(this.scrollHeight+5)+"px");">sudo easy_install web.py

Самые основы

Давайте создадим новый текстовый файл и назовём его code.py .
Откроем его и наберём данный код:

200?"200px":""+(this.scrollHeight+5)+"px");">import web


Это загрузит модуль Web.Py, ради которого собственно и варилась вся каша выше.
Теперь нам необходимо объяснить серверу, какова же наша URL-структура. Напишем ниже:

200?"200px":""+(this.scrollHeight+5)+"px");">urls = (
"/", "index"
)


Теперь при входе в корень сайта будет загружена страница index. И пусть руки у вас не чешутся, сударь, ибо ничего ещё не готово.

Страницы сайта, GET\POST запросы

Многие из вас прекрасно знают различия между GET и POST запросами.
GET запрос передаётся в адресную строку, а POST запрос - в тело скрипта.
Кстати, передавать собственными руками POST-запросы без форм и скриптов - то же самое, что тянуть кота на диване за хвост вместо того, чтобы просто поднять и удалить куда-то подальше.
Сейчас надо создать класс index , под которым подразумевается главная страница нашего сайта, или там игры, не знаю, что вы задумали.

200?"200px":""+(this.scrollHeight+5)+"px");">class index:
def GET(self):
return "Hello, world!"


Теперь при входе на сайт будет показываться текст Hello, world! . Проще говоря, мы не изменяем традициям.
Теперь надо приказать нашему великому приложению, чтобы после всех трудов выводил нам HTML-код сайта. Для этого допишите такой код:

200?"200px":""+(this.scrollHeight+5)+"px");">if __name__ == "__main__":
app.run()


Давайте подытожим. Получился у нас такой код:

200?"200px":""+(this.scrollHeight+5)+"px");">import web

Urls = (
"/", "index"
)

Class index:
def GET(self):
return "Hello, world!"

If __name__ == "__main__":
app = web.application(urls, globals())
app.run()


Скриптовая часть простого сайта готова, теперь надо его лишь запустить.

Запуск сервера

Зайдите в командную строку вашего сервера (или SSH-терминала) и введите команду:

200?"200px":""+(this.scrollHeight+5)+"px");">$ python code.py
http://0.0.0.0:8080/


http://0.0.0.0:8080/ - это адрес и порт нашего веб-сайта. Вместо него можно ввести домен сайта, если он прикреплён, внешний или локальный IP-адрес. Оттуда мы собственно и получим доступ к сайту.
На этом первый урок серии Создаем свой сайт\игру на Python подходит к концу.
На следующем уроке мы будем использовать шаблонизатор, базу данных и формы.
Приятного кодинга!