Pull to refresh

Персональный зоопарк или немного о Pygame — Часть 1

Reading time 7 min
Views 39K
image
Для тех, кто не в курсе: Pygame — это очень и очень неплохой фреймворк для разработки игр на языке Python. Причем поддерживается не только работа с 2D и 3D, но и при желании можно установить биндинги ко многим популярным графическим и физическим движкам. Кстати, Pygame вовсе необязательно использовать именно для игр, можно также создавать и программы с необычным интерфейсом, например, какой-нибудь трехмерный фронтенд к базе данных.
Вот мне, собственно, и захотелось рассказать об основных принципах работы с этим фреймворком, мало ли, может, пригодится кому :)

Погнали!


Для того, чтобы получить доступ к классам и методам Pygame — его надо инициализировать. Кстати, глобальный игровой таймер запускается именно при инициализации модуля, а программист, в свою очередь, в любой момент может получить время в секундах именно с начала инициализации. Создадим простое окошко:
Copy Source | Copy HTML
  1. import pygame
  2. from pygame.locals import *
  3.  
  4. def init_window():
  5.     pygame.init()
  6.     window = pygame.display.set_mode((550, 480))
  7.     pygame.display.set_caption('My own little world')
  8.  
  9. def main():
  10.     init_window()
  11.  
  12. if __name__ == '__main__': main()

Я оформил код в Си-подобной манере, так он легче для восприятия. Собственно, воспринимать особо пока и нечего :) Сначала импортируем необходимые модули (для сишников — описываем namespace), потом инициализируем фреймворк, создаем окошко с размерами 550 на 480 и даем ему заголовок «My own little world».
Думаю, те, кто попробовал запустить данный код, заметили, что окошко сразу после появления исчезает. Это происходит потому, что мы еще не описали глобальный бесконечный цикл приема сообщений, поэтому окну просто нечего делать. Исправим эту оплошность:
Copy Source | Copy HTML
  1. import sys
  2. import pygame
  3. from pygame.locals import *
  4.  
  5. def init_window():
  6.     pygame.init()
  7.     window = pygame.display.set_mode((550, 480))
  8.     pygame.display.set_caption('My own little world')
  9.  
  10. def input(events):
  11.     for event in events:
  12.         if (event.type == QUIT) or (event.type == KEYDOWN and event.key == K_ESCAPE):
  13.             sys.exit(0)
  14.         else:
  15.             pass
  16.  
  17. def action():
  18.     while 1:
  19.         input(pygame.event.get())
  20.  
  21. def main():
  22.     init_window()
  23.     action()
  24.  
  25. if __name__ == '__main__': main()

Как мы видим, запускается бесконечный цикл приема сообщений. Если передается сообщение QUIT (щелчок по крестику окна) или нажимается кнопка ESCAPE — приложение завершает свою работу.
Но окошко пустое, черное, неинтересное. Что бы нам сделать, чтобы что-то на нем нарисовать? Для начала зададим ему background. Можно залить его сплошным цветом или, как ни странно, просто загрузить картинку подходящего размера и отобразить ее по координатам (0,0):
Copy Source | Copy HTML
  1. import os
  2.  
  3. def load_image(name):
  4.     fullname = os.path.join('data', name) # Картинки у меня лежат в папке 'data'
  5.     try:
  6.         image = pygame.image.load(fullname)
  7.     except pygame.error, message: # Мало ли :)
  8.         print "Cannot load image:", name
  9.         raise SystemExit, message
  10.     image = image.convert() # Адаптируем картинку для отображения в игре. Если на ней есть альфа-канал - тогда convert_alpha()
  11.     return image, image.get_rect()
  12.  
  13. def draw_background():
  14.     screen = pygame.display.get_surface() # Получаем поверхность, на которой будем рисовать
  15.     background = pygame.Surface(screen.get_size()) # и ее размер
  16.     background = background.convert()
  17.     background.fill((0, 0, 0)) # заполняем цветом
  18.     screen.blit(background, (0, 0)) # рисуем заполненный одним цветом бэкграунд
  19.     back, back_rect = load_image("grass.jpg") # или загружаем картинку с травой
  20.     screen.blit(back, (0, 0)) # и рисуем ее
  21.     pygame.display.flip() # переключаем буфер экрана
  22.     return back
  23.  
  24. def main():
  25.     init_window()
  26.     bk = draw_background()
  27.     action()

Самые важные строчки здесь — это screen = pygame.display.get_surface(), screen.blit(back, (0, 0)) и pygame.display.flip(). При работе с Pygame важно помнить, что рисование каждый раз идет на какой-либо поверхности — surface. При этом действует такая вещь, как backbuffer, то есть рисование идет в буфере экрана, а метод flip(), так сказать, «выворачивает» экран, отображая изменения, которые произошли в буфере, на экране.

Ну а сейчас добавим в наш маленький мир немного живности. Для этого создадим животное, допустим, слона :) Только для начала немного перепишем нашу функцию загрузки изображений. Сейчас объясню, зачем.
Copy Source | Copy HTML
  1. def load_image(name, colorkey=None):
  2.     fullname = os.path.join('data', name)
  3.     try:
  4.         image = pygame.image.load(fullname)
  5.     except pygame.error, message:
  6.         print "Cannot load image:", name
  7.         raise SystemExit, message
  8.     image = image.convert()
  9.     if colorkey is not None:
  10.         if colorkey is -1:
  11.             colorkey = image.get_at((0,0))
  12.         image.set_colorkey(colorkey, RLEACCEL)
  13.     return image, image.get_rect()
  14.  
  15. class Animal(pygame.sprite.Sprite):
  16.     def __init__(self, img, cX, cY):
  17.         pygame.sprite.Sprite.__init__(self)
  18.         self.image, self.rect = load_image(img, -1)
  19.         screen = pygame.display.get_surface()
  20.         self.area = screen.get_rect()
  21.         self.cX = cX
  22.         self.cY = cY
  23.         self.coord = (cX, cY)
  24.         print"Animal spawned at", self.coord
  25.  
  26. class Elephant(Animal):
  27.     def __init__(self, cX, cY):
  28.         Animal.__init__(self, "Elephant.bmp", cX, cY)

Предвижу возмущенные возгласы «почему bmp??». Отвечу — для опыта :) Потому что сейчас мы приобрели еще один навык — беря цвет из картинки по координатам ( colorkey = image.get_at((0,0)) ), мы можем сделать этот цвет на всей картинке полностью прозрачным ( image.set_colorkey(colorkey, RLEACCEL) )! Пригодится, если вдруг понадобится загрузить картинку без альфа-канала.

pygame.sprite.Sprite — это стандартный класс спрайта pygame. Для тех, кто не в курсе: спрайт — это плоская картинка, обладающая рядом свойств, необходимых для игрового объекта. В частности, ее можно заставить двигаться, научить всяким взаимодействиям и т.п.

И теперь все, что нам нужно — это запустить слона в наши джунгли!
Copy Source | Copy HTML
  1. def action(bk):
  2.     creatures_list = [] # Список со всем животными. Пригодится, если будем добавлять новых
  3.     screen = pygame.display.get_surface()
  4.     elephant = Elephant(10,10) # Помещаем слона по координатам х=10, у=10
  5.     creatures_list.append(elephant)
  6.     animals = pygame.sprite.RenderPlain(creatures_list) # Засовываем всех наших животных в класс RenderPlain для отображения спрайтов на экране
  7.  
  8.     while 1:
  9.         input(pygame.event.get())
  10.         screen.blit(bk, (0, 0))
  11.         animals.update() # Стандартный метод проверки, вдруг что-то изменилось. Пригодится для описания движения
  12.         animals.draw(screen)
  13.         pygame.display.flip()
  14.  
  15. def main():
  16.     init_window()
  17.     bk = draw_background()
  18.     action(bk)

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

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



Готов ответить на любые вопросы и выслушать критику.
Tags:
Hubs:
+68
Comments 37
Comments Comments 37

Articles