Gra izometryczna cz.6 – rysowanie mapy izometrycznej

przez Adam Sobociński w dniu 21 kwietnia 2012 w kategorii: python

Po dłuższej przerwie, udostępniam obiecaną lekcje. W poprzednich lekcjach, pokazywałem jak obsługiwać duszki, klawiaturę, jak rysować świat 2D i jak poruszać w nim obiektami. Dla jednych była to ciekawa przygoda, dla innych być może przypomnienie tego co już znali. Celem było wprowadzenie do właściwego tematu tego kursu, jakim jest tworzenie świata w rzucie izometrycznym. W tej lekcji, pokaże jak tworzyć kafle i jak budować z nich świat.

Rzut izometryczny, dodaje efekt trójwymiarowości. Świat nie jest pokazany z góry, ale jest lekko obrócony, a dokładnie o 45 stopni.


Aby przygotować kafle (ang. tiles) w rzucie izometrycznym, rysujemy kwadrat o wymiarach 64x64px, obracamy o 45 stopni, oraz zmniejszamy jego wysokość o połowę. Tak jak to widać na rysunku powyżej.
Jeśli teraz wykorzystamy funkcję do wyświetlenia planszy z poprzedniej lekcji…

MAP_WIDTH = 4
MAP_HEIGHT = 4
TILE_WIDTH = 64
TILE_HEIGHT = 32

for x in range(MAP_WIDTH):
     for y in range(MAP_HEIGHT):
          tile = MAP[y][x]
          self.tiles[tile].draw(x*TILE_WIDTH,y*TILE_HEIGHT)

uzyskamy taki efekt…

Jak widzisz plansza składa się z 4×4 kafli, ale między nimi są dziury. Tak generowana plansza jest dla nas nieprzydatna. Jeśli wypełnilibyśmy dziury w środku, to efekt byłby taki

Znacznie lepiej, ale nadal nie jest to, czego potrzebujemy. Musimy generować świat, który wygąladem bedzie przypominać diament (ang. diamond).

Jest to dokładnie, to o co nam chodzi. Aby wygenerować taką planszę, muszę wykorzystać troszkę obliczeń matematycznych. Aby ułatwić Ci zrozumienie zasady jej tworzenia, posłużę się rysunkiem.

Na rysunku powyżej, możesz zobaczyć schemat, oraz kolejność układania kafli. Na każdym kaflu umieściłem jego pozycję x,y. Np. wartość 2,3 oznacza pozycję w drugim rzędzie i trzeciej kolumnie. Każdy kafel ma rozmiar 64px szerokości i 32px wysokości. Umiejętność układania kafli w odpowiedniej kolejności jest ważna, ponieważ pozwoli nam później przesłaniać duszki innymi kaflami jeśli znajduje się przed nim. Ale o tym opowiem w innej części. Wszystkie kafle mają identyczną wielkość, upraszcza nam to znacznie program.

Do utworzenia planszy, będą nam potrzebne dwa proste wzory matematyczne. Jeden dla przesunięć w poziomie, drugi w pionie. Z rysunku, da się zauważyć pewną prawidłowość, po wyświetleniu kafla (1,1), aby wyświetlić kafel (2,1) musimy przesunąć o połowę jego szerokości w prawo, oraz o połowę wysokości w doł. Da to efekt nakładających się obrazków. Aby obrazki nie przykrywały jeden drugiego, muszą mieć przeźroczyste tło. W ten sposób, kafle będą przylegać do siebie.

Aby obliczyć pozycje x kafli, posłużymy się wzorem

tile_x= (x-y)*(TILE_WIDTH/2)-map_x

gdzie x i y to pozycja kafla na planszy, TILE_WIDTH – szerokośc kafla (dzielimy przez 2, aby przesunięcie było o połowe jego szerokości, a nie o całość). Ostatnia wartość map_x, ustala nam pozycję całej planszy na ekranie. Czyli gdzie dokładnie znajdzie się kafel (1,1).

Do obliczenia pozycji y kafli, zastosujemy wzór

tile_y = (x+y)*(TILE_HEIGHT/2)-map_y

oznaczenia wzoru, takie same jak dla tile_x, tylko zamiast szerokości, wstawiamy wysokośc kafla TILE_HEIGHT

Zmodyfikowany kod wyświetląjacy mapę izometryczną, będzie wyglądał teraz tak

MAP_WIDTH = 4
MAP_HEIGHT = 4
TILE_WIDTH = 64
TILE_HEIGHT = 32

for x in range(MAP_WIDTH):
     for y in range(MAP_HEIGHT):
          tile = MAP[y][x]
          tile_x = (x-y)*(TILE_WIDTH/2)+self.map_x
          tile_y = (x+y)*(TILE_HEIGHT/2)+self.map_y
          self.tiles[tile].draw(tile_x,tile_y)

Jak widać zmienił się tylko sposób wyświetlania, linie 54,55 to obliczenia pozycji, a linia 56 wyświetla kafel. Cały kod, wygląda tak:

from pygame.locals import Color
import pygame, time
from pygame.locals import *
from OpenGL.GL import *
from texture import *

SCREEN_WIDTH  = 400
SCREEN_HEIGHT = 300

MAP = [[1,1,1,1,1,1],
       [1,1,1,1,1,1],
       [1,1,1,1,1,1],
       [1,1,1,1,1,1]]

MAP_WIDTH  = 4
MAP_HEIGHT = 4
TILE_WIDTH = 64
TILE_HEIGHT = 32


class Game(object):
  def __init__(self):
      self.stategame = 1
      self.tiles = []
      self.map_x = 100
      self.map_y = 10
      
      pygame.init()
      flag = OPENGL | DOUBLEBUF
      self.surface = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT),flag)
      self.opengl_init()
      self.tiles.append(Texture('grass.png'))
      
      self.loop()

  def opengl_init(self):
      #init gl
      glClearColor(0.0,0.0,0.0,1.0)
      glClear(GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT)

      glMatrixMode(GL_PROJECTION)
      glLoadIdentity()
      glOrtho(0,SCREEN_WIDTH,SCREEN_HEIGHT,0,0,1)
      glMatrixMode(GL_MODELVIEW)

      #set textures
      glEnable(GL_TEXTURE_2D)
      glEnable(GL_BLEND)
      glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)


  def draw_map(self):
        for x in range(MAP_WIDTH):
          for y in range(MAP_HEIGHT):
            tile = MAP[y][x] - 1
            tile_x = (x-y)*(TILE_WIDTH/2)+self.map_x
            tile_y = (x+y)*(TILE_HEIGHT/2)+self.map_y
            self.tiles[tile].draw(tile_x,tile_y)
            
  def loop(self):
      while self.stategame==1:
        for event in pygame.event.get():
           if event.type == QUIT \
           or (event.type == KEYDOWN and event.key ==  K_ESCAPE):
              self.stategame = 0
        
        self.draw_map();    
        pygame.display.flip()


if __name__ == '__main__':
    Game()  

Przykład korzysta z klasy Texture. Nie opisuje jej, ponieważ nie zmieniła się od ostatniej lekcji. Zmienia się tutaj tylko funkcja draw_map().
I jak zwykle, gotowy działający przykład do pobrania isogame_tut6. W paczce, znajdziesz dwa przykładowe kody z tej lekcji, aby porównać sposób generowania zwykłego 2D i 2,5D czyli w rzucie izometrycznym.

W tej lekcji to wszystko, zachęcam do eksperymentowania i podzielenia się wnioskami i pomysłami. A w kolejnej lekcji nauczymy się obsługiwać myszkę w świecie izometrycznym.

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


Jedna odpowiedź na „“Gra izometryczna cz.6 – rysowanie mapy izometrycznej””

  1. Początkujący pisze:

    Witam, mam problem z odpaleniem programu, pokazuje mi białe okienko, a po chwili znika, nie pokazuje błędu, mógłby mi ktoś podpowiedzieć gdzie musiałem spartolić sprawę? :)

Dodaj komentarz

Current month ye@r day *