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

    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, об этом расскажу в следующий раз.



    Готов ответить на любые вопросы и выслушать критику.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 37

      –5
      > Зеленые контуры вокруг картинок — это огрехи bmp
      Понятно, что вам не охото вдаваться в подробности, но и откровенно ерунду говорить тоже не стоит. Сколько заком с форматом bmp, ни о каких зеленых линиях не слышал.
        +1
        Ну правильно, я имел в виду, что использовал bmp и тот самый способ с отсечением цвета, а не изображения с альфой. А из-за огрехов обработки цвет вокруг объектов на какие-то мизерные доли изменился и остался в виде рамочки.
          –2
          Срите, срите в карму. Остаюсь при своем мнении, у формата BMP нет огрехов в виде зеленой обводки. Более того, в формате бмп возможно сохранять 32-х битные изображения и использовать четвертый канал как альфу. Зеленая обводка — это огрехи программиста (выбрал не тот формат) или дизайнера (нарисовал с зеленой обводкой).
            0
            Все по вашему виноваты — и программисты и дизайнеры.
            Да какая вам раздница? :)
          0
          ммм. а можно под шумок вопрос по питону
          усть такой цикл

          while counter<100:
          canvas.create_line(10,10,20,counter)
          print ('aaaa')
          time.sleep(0.005)
          counter+=1
          в консооль 'аааа' выводит ежесекундно а вот линии рисует уже по ззавершению цикла, а не во время. т.е. анимации нет.
          Почему? Спасибо.
            +1
            Не понятно, как у вас с отступами. У меня, например, функция сетки выглядит так:

            def coord(screen):
                x = 0
                y = 0
                for ln in xrange(12):
                    line(screen, (0,255,0), (x,0), (x,450))
                    x+=50
                for ln in xrange(10):
                    line(screen, (0,255,0), (0,y), (550,y))
                    y+=50

            и вроде как все работает
              0
              И потом, вы не забыли про отображение backbuffer на каждом этапе? Иначе изменений видно не будет.
                0
                отступы скушал хабр, я пока пробую на стандартных билиотеках. backbuffer — где можно почитать? это своего рода алгоритм так понимаю?
                  0
                  нет, это механизм отрисовки на экране, я же написал :) То есть сначала все изменения вносятся в так называемый задний буфер экрана, а по команде, например, pygame.display.flip(), отображаются на экране.
                      0
                      премного благодарен, буду изучать
                +1
                canvas.update() после слипа
                и вообще было бы очень хорошо указывать, что вы тут с Tkinter работаете
              0
              а подскажите биндинги SDL к C++.
                0
                не кажется, что вопрос немного не в тему? :)
                  0
                  Кажется.
                  pygame — это биндинг SDL для питона.

                  Т.е. я спросил про эту же самую библиотеку, про которую данный пост.
                    0
                    SDL — это библиотека разработанная на Си, так что вы просто скачиваете пакет sdl-devel, и сразу имеете нужные хидеры.
                      0
                      Эм… я это знаю.
                      Так вот. Возвращаясь к моему вопрросу: я хотел бы биндинги для C++.

                      P.S. C и C++ — абсолютно разные языки, у них мало общего.
                        0
                        Они не разные языки. Си++ расширение языка Си, причем обратная совместимость сохранена на 99.999%. Так что подключаете сишные хидеры и вперед.
                          –2
                          ага, а питон — это расширение ассемблера с 99.998% совместимостью.
                          ты не путаешь ObjectiveC и C++? Либо еще что-то…

                          Просто я доверяю мнению Страуструпа гораздо больше твоего ;)
                            +2
                            Исходник на си, с 99% вероятностью скомпилится на си++ компиляторе. А у sdl 100% совместимость.
                            П.С.
                            Если ты читал невнимательно Страуструпа, хоть википедию почитай.
                            П.П.С.
                            Sdl компилируется под visual c++ 6, visual c++ 6 не поддерживает стандарт С99. Следовательно sdl написан на Ansi C. И отсюда частично следует то, что он без проблем компилируется на любом Си++ компиляторе. То бишь он нативно совместим с Си++.
                            • UFO just landed and posted this here
                                –3
                                И что, что компилится? Это все равно C.
                                Меня не прельщает перспектива писать кучу ООПных оберток для его классов, чтобы можно было его удобно использовать.
                                • UFO just landed and posted this here
                                  • UFO just landed and posted this here
                                      0
                                      А вы Гвидо ван Россума наняли, чтобы он pygame вым использовать разрешил? о_О
                                        0
                                        Он написал pygame для себя, и поделился со всеми. Последуйте его примеру. А вообще погуглите, я видел обертку к sdl на ООП.
                                        П.С.
                                        Можно было сразу сказать, что вы ищите ООП обертку над SDL. С++ является мультипарадигменном языком, и он не навязывает ООП.
                                        0
                                        Я все это знаю и юзал. Это C/-стиль
                                  0
                                  Почитайте — Бьёрн Страуструп «Дизайн и эволюция C++» и поймёте. что это никакое нафиг не расширение, а полноценный, самодостяточный язык.
                                    0
                                    Я использовал термин расширение с точки зрения теории множеств. Я читал Страуструпа лет 5 назад. Просто я пытался понятными словами сказать, что большая часть Си кода прекрасно компилируется на Си++ компиляторе. А в данном случае 100% кода библиотеки компилируется на Си++ компиляторе без проблем.
                        0
                        print slef.name не будет работать, так-как свойство name не определено.
                          0
                          прошу прощения, исправил
                          0
                          Отличная статья! Меня самого давно интересует разработка казуальных игр на питоне, но никаких материалов на русском на эту тему я не встречал. Вы восполнили этот пробел. С нетерпением жду продолжения!

                          И еще: стоит отметить, что документация на pygame частично переведена на русский, а в одном из номеров журнала «Linux Format» также есть неплохая вводная статья про pygame (Номер 03 (103) Март 2008).
                            0
                            На реддите была куча ссылок по поводу pygame и таких вот туториалов, посмотрите там.
                            0
                            Спасибо за статью. Люблю Python.
                              0
                              Спасибо за статью :)

                              Only users with full accounts can post comments. Log in, please.