• В этом блоге почти все время я писал о PHP и немного о JS. Настало время разнообразить, и разнообразить свой блог я решил Питоном. О том, где взять Python и где поучиться я не буду, материалов полно, и большинство друг друга повторяют, в еще одном от меня смысла я не вижу. Чуть меньше примеров и статей про PyGame — свободную графическую библиотеку, базирующуюся на SDL.

    Проблем с установкой, я надеюсь у вас не возникнет — либо запускате Windows инсталлер, а если вы в Linux — наверняка сами знаете. Не будем тянуть кота за хвост, откроем IDE, создадим проект и уже создадим хотя бы окошко.
    main.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    #! /usr/bin/python
    # -*- coding: utf-8 -*-
    import pygame
     
    class Game():
        def __init__(self):
            #Initial window settings
            pygame.init()
            pygame.display.set_mode((640, 480))
            pygame.display.set_caption('I\'m super cool Python programmer')
            self.run()
     
        #Run, Forrest, run
        def run(self):
            while True:
                self.track_input(pygame.event.get())
     
        def track_input(self, events):
            for event in events:
                #Exit
                if event.type == pygame.QUIT:
                    exit(0)
     
    #Here we go
    if __name__ == "__main__":
        Game()

    Сохраняете, запускаете, смотрите. Опа, окошечко. И даже закрывается по крестику. Теперь обьясню как это произошло. Сначала нам нужно импортировать pygame, иначе ничего не получится. Запуск скрипта начинается с последних строчек, которые создают экземпляр класса Game. Такая конструкция с переменной __name__ требуется для того, чтобы при импорте этой программы она не запускалась, а срабатывала только если файл запущен непосредственно. Функция __init__ в классе Game является чем-то вроде конструктора (но не конструктором!). Она вызывает метод pygame.init(), который инициализирует библиотеку, дальше вызываем метод pygame.display.set_mode((640, 480)), который и создает собственно окно размером 640х480. Обратите внимание, мы передаем кортеж, а не просто 2 числа. Дальше даем окну заголовок. На этом можно было бы и остановиться, программа уже будет работать. Но окно выскочит и закроется, потому что программа завершится. Чтобы этого не произошло запускаем функцию run(), которая представляет из себя бесконечный цикл вызовов функции track_input, которая принимает параметром события. И если произошло событие «Выход» (то самое нажатие по крестику), то закрываем. Все можно было бы уместить в одну функцию, но это задел на будущее.

    Что же нам хочется дальше? Конечно же, чтобы что-то двигалось по стрелочкам. Почти в любой игре что-нибудь надо двигать, и у нас не будет исключение. Что же нужно? Нужно все так же следить за событиями, только теперь обрабатывать нажатия на кнопки стрелок. Чтобы удобнее было использовать направления (вверх, вправо, вниз, влево) обозначим их цифрами 0, 1, 2, 3. А чтобы не запутаться, введем переменные UP, RIGHT, DOWN, LEFT, которым и дадим эти значения. А переменные положим в отдельный файл directions.py и наступит ваще красота.
    directions.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    #! /usr/bin/python
    # -*- coding: utf-8 -*-
     
    if __name__ == "__main__":
        exit(0)
     
    UP    = 0
    RIGHT = 1
    DOWN  = 2
    LEFT  = 3
    DIRECTIONS = [UP, RIGHT, DOWN, LEFT]
    VERTICAL = [UP, DOWN]
    HORIZONTAL = [LEFT, RIGHT]

    Обьявили наши переменные и три списка, которые нам пригодятся в будущем. Вернемся в main.py. Добавим к списку импортов directions.py:

    1
    
    import directions

    В класс Game добавим словарь и 3 переменные:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    x = 100
    y = 100
    speed = 3
     
    keymap = {
        pygame.K_LEFT  : directions.LEFT,
        pygame.K_UP    : directions.UP,
        pygame.K_RIGHT : directions.RIGHT,
        pygame.K_DOWN  : directions.DOWN
    }

    Три переменные будут отвечать за положение и скорость перемещения объекта. А словарь ставит в соответствие клавиши и направления. Я надеюсь, что очевидно, что когда мы будем нажимать на какую-то кнопку, благодаря этому словарю мы узнаем в каком направлении надо двигать наш игровой объект, из положения x,y на speed пикселей. Далее в track_input добавляем в цикл:

    1
    2
    
    if event.type == pygame.KEYDOWN and event.key in self.keymap.keys() and self.keymap[event.key] in directions.DIRECTIONS:
        self.move(self.keymap[event.key])

    Здесь мы проверяем, что нажата кнопка (нажатия мышки и т.п. нас сейчас не интересуют) и что кнопка указана среди keymap, и то, что по выбранной кнопке в keymap стоит нормальное направление. И если все в порядке — вызываем функцию move с параметром «направление» из словаря.
    В Python, насколько я знаю, нету операции switch/case, поэтому здесь мы будем делать финт ушами. Вот функция move, которую мы кладем все в тот же класс Game:

    1
    2
    3
    4
    5
    6
    7
    8
    
    def move(self, direction):
        movement = {
            directions.UP       : [self.x,            self.y-self.speed],
            directions.RIGHT  : [self.x+self.speed, self.y],
            directions.DOWN  : [self.x,            self.y+self.speed],
            directions.LEFT    : [self.x-self.speed, self.y]
        }
        self.x, self.y = movement[direction]

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

    1
    2
    3
    4
    5
    
    pygame.display.get_surface().fill(pygame.Color(0, 0, 0, 0))
    block = pygame.Surface((10, 10))
    block.fill(pygame.Color(255, 255, 255, 0))
    pygame.display.get_surface().blit(block, (self.x, self.y));
    pygame.display.flip()

    Давайте теперь посмотрим что у нас получилось.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    
    #! /usr/bin/python
    # -*- coding: utf-8 -*-
    import pygame
    import directions
     
    class Game():
     
        x = 100
        y = 100
        speed = 3
     
        keymap = {
            pygame.K_LEFT  : directions.LEFT,
            pygame.K_UP    : directions.UP,
            pygame.K_RIGHT : directions.RIGHT,
            pygame.K_DOWN  : directions.DOWN
        }
     
        def __init__(self):
            #Initial window settings
            pygame.init()
            pygame.display.set_mode((640, 480))
            pygame.display.set_caption('I\'m super cool Python programmer')
            self.run()
     
        def run(self):
            while True:
                self.track_input(pygame.event.get())
                pygame.display.get_surface().fill(pygame.Color(0, 0, 0, 0))
                block = pygame.Surface((10, 10))
                block.fill(pygame.Color(255, 255, 255, 0))
                pygame.display.get_surface().blit(block, (self.x, self.y));
                pygame.display.flip()
     
        def track_input(self, events):
            for event in events:
                #Exit
                if event.type == pygame.QUIT:
                    exit(0)
                if event.type == pygame.KEYDOWN and event.key in self.keymap.keys() and self.keymap[event.key] in directions.DIRECTIONS:
                    self.move(self.keymap[event.key])
     
     
        def move(self, direction):
            movement = {
                directions.UP : [self.x,            self.y-self.speed],
                directions.RIGHT : [self.x+self.speed, self.y],
                directions.DOWN : [self.x,            self.y+self.speed],
                directions.LEFT : [self.x-self.speed, self.y]
            }
            self.x, self.y = movement[direction]
     
     
    if __name__ == "__main__":
        Game()

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