17. PyGame

17. PyGame

17. PyGame

8 май 2013

Защо искаме да правим игри?

Колко точно различно е?

Основните ни занимания

Показваме неща на екрана

Безкрайният цикъл

Цялата ни игра е един безкраен цикъл, в който обработваме event-и

import pygame

pygame.init()
screen = pygame.display.set_mode((640, 480))

while True:
  pass

Безкраен цикъл done right

import pygame


class Game:
    running = True

    def __init__(self, size):
        pygame.init()
        self.screen = pygame.display.set_mode(size)

    def main(self):
        while self.running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return

if __name__ == '__main__':
    Game((800, 600)).main()

Чертаене

FPS

Различни машини - различни възможности.

pygame.time.Clock

while self.running:
   clock.tick(30)
   for event in pygame.event.get():
       if event.type == pygame.QUIT:
           return

   screen.fill((200, 200, 200))
   screen.blit(image, (320, 240))
   pygame.display.flip()

Анимации

Със всяка итерация променяме координатите на нещото, което се "движи"

и всеки път подаваме новите на screen.blit:

screen.blit(image, (x, y))

User Input

key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
    image_x -= 10
if key[pygame.K_RIGHT]:
    image_x += 10
if key[pygame.K_UP]:
    image_y -= 10
if key[pygame.K_DOWN]:
    image_y += 10

Sprites

Двуизмерна картинка или анимация, интегрирана в някакво пространство

class Player(pygame.sprite.Sprite):
    def __init__(self, *groups):
        super(Player, self).__init__(*groups)
        self.image = pygame.image.load('player.png')
        self.rect = pygame.rect.Rect((320, 240), self.image.get_size())

    def update(self):
        key = pygame.key.get_pressed()
        if key[pygame.K_LEFT]:
            self.rect.x -= 10
        if key[pygame.K_RIGHT]:
            self.rect.x += 10
        if key[pygame.K_UP]:
            self.rect.y -= 10
        if key[pygame.K_DOWN]:
            self.rect.y += 10

Групи от спрайтове

В общия случай го раздаваме с десетки/стотици спрайтове.

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

class Game(object):
    def main(self, screen):
        clock = pygame.time.Clock()

        sprites = pygame.sprite.Group()
        self.player = Player(sprites)

        while 1:
            clock.tick(30)

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return

            sprites.update()
            screen.fill((200, 200, 200))
            sprites.draw(screen)
            pygame.display.flip()

Малко по-приятна сцена

screen.fill е полезно, но изключително куцо

Би било далеч по-приятно да имаме някакъв красив фон...като небе примерно:

def main(self, screen):
    clock = pygame.time.Clock()

    background = pygame.image.load('background.png')
    sprites = pygame.sprite.Group()
    self.player = Player(sprites)

    while 1:
        dt = clock.tick(30)
        for event in pygame.event.get():
        [....]

        sprites.update(dt / 1000.)
        screen.blit(background, (0, 0))
        sprites.draw(screen)
        pygame.display.flip()

Gameplay Mechanics

Пуцалка

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

Тук нещата загрубяват и имаме нужда от адекватна структура

game/
  assets/...
  main.py
  player.py
  projectile.py
  settings.py
  vec2d.py

Collision detection

4 основни подхода:

Препядствия

Ще добавим стени, че да не ни бяга човечето, също биха били полезни:

self.walls = pygame.sprite.Group()
block = pygame.image.load('block.png')
for x in range(0, 640, 32):
    for y in range(0, 480, 32):
        if x in (0, 640-32) or y in (0, 480-32):
            wall = pygame.sprite.Sprite(self.walls)
            wall.image = block
            wall.rect = pygame.rect.Rect((x, y), block.get_size())
sprites.add(self.walls)

Сблъсъци с препядствията

last = self.rect.copy()
for cell in pygame.sprite.spritecollide(self, game.walls, dokill=False):
    self.rect = last

Прецизни сблъсъци

new = self.rect
for cell in pygame.sprite.spritecollide(self, game.walls, False):
    cell = cell.rect
    if last.right <= cell.left and new.right > cell.left:
        new.right = cell.left
    if last.left >= cell.right and new.left < cell.right:
        new.left = cell.right
    if last.bottom <= cell.top and new.bottom > cell.top:
        new.bottom = cell.top
    if last.top >= cell.bottom and new.top < cell.bottom:
        new.top = cell.bottom

До тук какво разбрахме и защо всичко е толкова трудно?

Навсякъде хвърчат препиятствия, патрони, зомбита, нашия играч

Как да решим този проблеми

Свят

Но това не е всичко...

Вече можем да постигнем и далеч по-адекватен collision detection

Дебуболечкване

Физика 101

AI

За него ще си говорим точно след седмица.

И още...

Въпроси?