Gra izometryczna cz.2 – Duszki

przez Adam Sobociński w dniu 6 listopada 2011 w kategorii: python

W poprzedniej części, nauczyliśmy się tworzyć okno, obsługiwać pętle zdarzeń i wyświetlać obrazki na ekranie. W tej części nauczymy się tworzyć duszki i poruszać nimi.

Co to są duszki
Duszki (ang. sprites), to małe ruchome obrazki wyświetlane na ekranie. Są nimi np, postacie, potworki wszelkie ruchome elementy gry. Duszki mogą być statyczne, które nie zmieniają kształtu ani wyglądu i animowane. Najpierw zajmiemy się duszkami statycznymi.

Tworzymy duszka
Tworzymy więc jakiś obrazek, którym będziemy sterować w formacie PNG, oczywiście bez tła, albo pobierz gotowy, który przygotowałem.


Ładujemy naszego duszka do programu, będzie on dostępny przez self.player_image, oraz inicjujemy pozycje duszka na ekranie.

   self.player_image = pygame.image.load('spaceship.png')
   self.speed = 1.2     # szybkość poruszania duszka
   self.player_x = 50   # pozycja x duszka na ekranie
   self.player_y = 30   # pozycja y duszka na ekranie

Poruszanie duszkiem
Aby poruszać naszym duszkiem, musimy dodać obsługę klawiszy W, S, A, D. Dalej będzie odrobinę matematyki, ale jeśli ktoś poważnie myśli o tworzeniu gier, to bez matematyki się nie obejdzie. Spójrz na wykres:

Mamy na niej 2 osie, oś X i Y, które oznaczają kierunek poruszania się duszka, możemy odczytać z niego, że podstawiając do współrzędnych XY odpowiednie wartości, możemy poruszać duszkiem w dowolnym kierunku. Czyli:

w góre: X =  0  i Y = -1
w dół:  X =  0  i Y =  1
w lewo  X = -1  i Y =  0
w prawo X =  1  i Y =  0

Łatwo zauważyć, że odpowiednio łącząc wartości X i Y, można poza kierunkami góra, dół, prawo i lewo, dodać pośrednie, np jednocześnie w górę i prawo, będzie to X = -1 i Y = -1. Skoro wiemy już jak wygląda ustalanie kierunku poruszania, przejdźmy do kodu odczytu stanu klawiszy.

  keys = pygame.key.get_pressed() # odczytujemy stan klawiszy

  if keys[K_s]:
      self.move(0,1)  # ruch w dol

  if keys[K_w]:
      self.move(0,-1)   # ruch w gore

  if keys[K_d]:
      self.move(1,0)  # ruch w prawo

  if keys[K_a]:
      self.move(-1,0)   # ruch w lewo

Jak widać, wywoływana jest metoda self.move(x,y), która odpowiednio przelicza parametry pozycji duszka.
Poniżej przedstawiam wzór na obliczenie współrzędnych duszka.

pos_x = pos_x + (dirx * speed)
pos_y = pos_y + (diry * speed)

Oznacza to tyle, ze do każdej pozycji X lub Y dodajemy odpowiednią wartość kierunku -1,0 lub 1 z naszej tabelki powyżej, gdzie po pomnożeniu przez współczynnik prędkości, ustalamy nowe współrzędne duszka.

def move(dirx,diry):
    self.player_x = self.player_x + (dirx * self.speed)
    self.player_y = self.player_y + (diry * self.speed)

Następnie wymazujemy zawartość ekranu (można samego duszka, ale dla uproszczenia czyścimy całość), i wyświetlamy duszka na nowych współrzędnych, co da nam wrażenie poruszania.

  self.screen.fill((0,0,0))  # czyscimy ekran, mało wydajne ale wystarczy
  self.surface.blit(self.player_image,(self.player_x,self.player_y))

Mamy już wszystkie części programu, aby wyświetlić i poruszać naszym duszkiem. Cały program wygląda tak.

import pygame                # importujemy biblioteki pygame
from pygame.locals import *  # importujemy nazwy [QUIT, KEYDOWN,K_ESCAPE] itp.
from sys import exit         # importujemy funkcje systemowa exit

screen_size = (800,600)      # ustalamy rozmiar ekranu

class IsoGame(object):
    def __init__(self):
        pygame.init()       # incjalizujemy biblioteke pygame
        flag = DOUBLEBUF    # wlaczamy tryb podwojnego buforowania

        # tworzymy bufor na  grafike
        self.surface = pygame.display.set_mode(screen_size,flag)

        # zmienna stanu gry
        self.gamestate = 1  # 1 - run, 0 - exit

        self.player_image = pygame.image.load('spaceship.png')
        self.speed = 1.2     # szybkosc poruszania duszka
        self.player_x = 50   # pozycja x duszka na ekranie
        self.player_y = 30   # pozycja y duszka na ekranie

        self.tree_image = pygame.image.load('tree.png') # ladujemy obrazek do pamieci
        self.loop()                             # glowna petla gry

    def move(self,dirx,diry):
       """ poruszanie duszkiem """
       self.player_x = self.player_x + (dirx * self.speed)
       self.player_y = self.player_y + (diry * self.speed)

    def game_exit(self):
        """ funkcja przerywa dzialanie gry i wychodzi do systemu"""
        exit()

    def loop(self):
        """ glowna petla gry """
        while self.gamestate==1:
           for event in pygame.event.get():
               if event.type==QUIT or (event.type==KEYDOWN and event.key==K_ESCAPE):
                   self.gamestate=0

           keys = pygame.key.get_pressed() # odczytujemy stan klawiszy

           if keys[K_s]:
              self.move(0,1)  # ruch w dol

           if keys[K_w]:
              self.move(0,-1)   # ruch w gore

           if keys[K_d]:
              self.move(1,0)  # ruch w prawo

           if keys[K_a]:
              self.move(-1,0)   # ruch w lewo

           self.surface.fill((0,0,0))  # czyscimy ekran, malo wydajne ale wystarczy
           self.surface.blit(self.tree_image, (10,20))  # gracz zaslania obrazek

           # umieszczamy gracza
           self.surface.blit(self.player_image,(self.player_x,self.player_y))

           self.surface.blit(self.tree_image, (220,20))  # obrazek zaslania gracza
           pygame.display.flip()   # przenosimy bufor na ekran

        self.game_exit()

if __name__ == '__main__':
   IsoGame()

Wyjaśnienia wymagają linie 57 i 62. Linia 57 wyświetla drzewo, wcześniej niż gracza, dzięki czemu, nasz statek kosmiczny zasłania je. Natomiast linia 62, wyświetla inne drzewo, po wyświetleniu gracza, dzięki czemu drzewo zasłania nasz statek. Wniosek płynie z tego jeden. Wszystkie grafiki, które gracz ma przesłaniać, wyświetlamy zanim umieścimy gracza na ekranie, a wszystkie grafiki, które mają zasłaniać gracza, wrzucamy na ekran, po narysowaniu gracza.

Po uruchomieniu programu, zobaczymy taki ekran.

Klawiszami W, S, A, D poruszamy statkiem. Klawisz [ESC] zamyka okno.

Pobierz plik isogame_tut2.zip

Animacja
Pora na animowane duszki. Animacja to nic innego, jak wyświetlanie różnych klatek, tego samego obiektu, w różnych pozach, różnym kształcie, czy jak w naszym przykładzie, przy poruszaniu, będzie wyświetlana klatka, która będzie sugerowała włączone silniki naszego pojazdu. Klatki animacji mogą znajdować się w osobnych plikach, lub wszystkie w jednym. My zastosujemy drugi sposób, ponieważ jest to znacznie wygodniejsze. Mamy więc taki obrazek

Witać, że nasza animacja składać się będzie z 3 klatek. Pierwsza klatka, będzie wyświetlana zawsze, gdy duszkiem nie poruszamy. Gdy zaczniemy nim poruszać, na zmianę będą wyświetlane klatki nr 2 i 3.
Zmodyfikujemy nasz kod ładujący, aby wycinał klatki. Każda klatka musi mieć identyczny rozmiar, w naszym przypadku jest to 127x49px, jeśli pominiemy ramkę, będzie to 125x47px. Ramka jest tak naprawdę niepotrzebna, ale znacznie ułatwia pozycjonowanie każdej klatki w programie graficznym. Kod ładujący, będzie wyglądał teraz tak:

  images = pygame.image.load('spaceship2.png') # wczytujemy grafike
  frame_rect = pygame.Rect(1,0,125,47)         # ustalamy wielkosc ramki
  self.player_image = [None] * 3  # tworzymy pusta liste, do przechowywania klatek
  self.player_frame = 0  # numer aktualnie wyswietlanej klatki

  # w petli wycinamy kazda ramke i umieszczamy w tablicy,
  # dzieki temu ze maja taki sam rozmiar jest to bardzo proste
  for im in range(0,3):
     frame_rect.top = im*48+1 # ustawiamy obszar wycinania klatki +1px ramki
     self.player.image[im] = images.subsurface(frame_rect) # wycinamy

Komentarze w kodzie wszystko wyjaśniają. Linia 8, przesuwa wycinany obszar o wysokość klatki,
czyli mamy kolejno (1,0,124,47), (49,0,124,47), (98,0,124,47). Dwa pierwsze parametry określają początek klatki [left,top], dwa ostatnie odpowiednio szerokość i wysokość klatki, a ponieważ ona się nie zmienia, to zostawiamy te wartości bez zmian.
Teraz modyfikujemy główną pętle gry. Dodajemy

player_anim = 0  # wylaczamy animacje
if keys[K_s]:
     self.move(0,1)  # ruch w dol
     player_anim = 1

if keys[K_w]:
     self.move(0,-1)   # ruch w gore
     player_anim = 1

if keys[K_d]:
    self.move(1,0)  # ruch w prawo
    player_anim = 1

if keys[K_a]:
    self.move(-1,0)   # ruch w lewo
    player_anim = 1

if player_anim == 1:
   self.player_frame+=1  # ustawiamy kolejna klatke

   # gdy aktualna klatka wieksza niz 2, to animujemy od nowa
   if self.player_frame > 2:
       self.player_frame = 1
   else:
       self.player_frame = 0 # gdy gracz sie nie porusza, wylaczamy silniki

Na koniec modyfikujemy kod, odpowiedzialny za wyświetlenie gracza

self.surface.blit(self.player_image[self.player_frame],(self.player_x,self.player_y))

Zmienna player_anim, to flaga, która informuje, czy gracz się porusza. Potrzebujemy jej, aby przestawiać animacje. Zachęcam do eksperymentów, np. zmianą prędkością poruszania duszka. Nie będę umieszczał, po raz kolejny kodu w całości na stronie, cały kod z obrazkami, możesz pobrać, jako paczkę.

Pobierz plik isogame_tut2a.zip

I jak zwykle zrzut ekranu, jak to wygląda

W następnej lekcji ciąg dalszy duszków, oraz porozmawiamy sobie o kolizjach.

Podziel się na:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Blogplay
  • Blogger.com
  • Gadu-Gadu Live


3 odpowiedzi na „“Gra izometryczna cz.2 – Duszki””

  1. Krystian pisze:

    zauważyłem błąd w składni, zamiast:
    self.player.image[im] = images.subsurface(frame_rect)
    powinno być:
    self.player_image[im] = images.subsurface(frame_rect)
    Druga rzecz, jeśli chodzi o wycinanie ramki. Czy na pewno każda otrzymana ramka będzie miała parametry (1,0,124,47), (49,0,124,47), (98,0,124,47)? Może źle interpretuje wynik działania pętli, ale wydaje mi się, że klatki po wycięciu powinny wyglądać tak: (1,0,125,47),(49,0,125,47),(97,0,125,47). Pozdrawiam. Życzę sukcesów w prowadzeniu bloga, jest świetny!

  2. Adam Sobociński pisze:

    Tak masz rację, dzięki za zwrócenie uwagi :)
    Również pozdrawiam

  3. kateb pisze:

    Super zabawa, dzięki ;-) dwa błędy znalazłem:

    1. self.player.image[im] = images.subsurface(frame_rect) #
    powinno byc:
    self.player_image[im] = images.subsurface(frame_rect) #

    2. oraz nastepujący else, powinien być przesunięty o poziom wyżej (jeden tab w lewo)
    else:
    self.player_frame = 0 # gdy gracz sie nie porusza, wylaczamy silniki

Dodaj komentarz

Current month ye@r day *