Arcade-pelien ohjelmointi
Pythonilla ja PygamellaChapter 16: Ruudukko pelialueena
16.1 Johdanto
Miinaharava, risti-nolla ja monet seikkailupelit tallentavat pelidataa ruudukkoon. Seuraavassa esimerkissä on risti-nolla pelin pelialusta:
O | O | |
X | ||
X |
...jossa voitaisiin esittää tyhjiä ruutuja ja merkittyjä ruutuja O'a ja X'ä tällä tavalla:
0 | 2 | 2 |
0 | 1 | 0 |
1 | 0 | 0 |
Tätä ruudukkoa voidaan ohjelmoinnissa kutsua kaksiulotteiseksi taulukoksi tai matriisiksi. Taulukon lukuarvot esittävät sitä, mitä kussakin pelilaudan ruudussa pitäisi esittää. Ensimmäisessä esimerkissä, 0 vastaa pelaamatonta pelilaudan ruutua, 1 esittää X, ja luku 2 esittää O.
Kuvassa 16.1 on esimerkki klassisesta miinaharavapelistä. Esimerrkikuvaan on lisätty myös kaksiulotteinen taulukkoesitys oikealle puolelle, jossa näkyy pelialueen tilanne numeroesityksenä.
Luku 10 esittää miinaa, luku 0 on klikkaamaton ruutu, luku 9 esittää miinoista vapaata aluetta ja luvut 1 - 8 esittävät ympärillä olevissa kahdeksassa ruudussa olevien miinojen määrän. Ruudussa esitetään luku vain, jos pelaaja klikkaa ruutua.
Miinaharavassa on oikeastaan kaksi taulukkoa. Toinen on pelinäkymänä toimiva taulukko ja toinen erillinen taulukko on peliktilannetta kuvaaville numeroille, jossa on mm. pelaajan merkitsemät “liput” miinojen merkkinä.
Klassisissa seikkailupeleissä pelialue luodaan 'tiilikuviolla'. Pelialue voi olla valtavan kokoinen taulukko, jossa jokainen kuvion paikka ja maaston tyyppi on taulukoitu numerona. Maaston tyyppejä on erilaisia; hiekkaa, savea, mutaa, tie, polku, vihreää tai ruskeaa ruohoa jne. Ohjelma Tiled Qt on esitetty kuvassa 16.2 tekee helpottaa tällaisten pelialueiden ja taulukoiden tekemistä.
Seikkailupeleissä käytetään samanlaista taulukointitekniikkaa kuin miinaharavapelissäkin. Yksi taulukko (tai taulukkotaso) esittää liikkumisessa käytettävää maastoa, jossa voidaan vaikkapa kävellä. Toinen taulukko voisi esittää taasen niitä kohtia, joissa ei voida liikkua; esim. seinät, puut jne. Jokin taulukkotaso vois sisältää vaaralliset asiat kuten laavaa, pohjattomat kuopat, suon silmäkkeet. Yhteen taulukkoon voidaan laittaa poimittavat objektit ja siirreltävät esineet ja yhdessä taulukossa on aloitustilanteessa olevat monsterit.
Tällaisten 'mappien' lataaminen Python-ohjelmaan on mahdollista, mutta tämän tarkempi eisttäminen ei ole tämän materiaalin tehtävä. Miten 'mapit' saadaan ladattua, voit selvittää tarkemmin esim. tästä projektista PyTMX, josta löytyy mappeihin tarvittavaa koodia.
16.2 Sovellus
Taulukoinnin teoriaa on tullut nyt paljon, joten on aika koodata käytännössä jotain havainnollistavaa. Tässä esimerkissä teemme ruudukon, jossa on valkoisia ja vihreitä ruutuja. Ruudun väri saadaan muutettua vihreäksi klikkaamalla hiirellä. Tämä on ensimmäinen vaihe miinaharava-tyyppisen pelin tekemisessä. Toinen esimerkki voisi olla laivan upotuspeli.
Lataa tästä esimerkkikoodi.
Tästä löytyy Pygame-template koodipohja:
ProgramArcadeGames.com/python_examples/f.php?file=pygame_base_template.py
Voit aloittaa tyhjällä Pygame-templatetiedostolla ja seuraamalla alla olevia ohjeita, voit koodata ohjelman itse. Lopullinen ohjelma löytyy tämän kappaleen lopusta, mutta älä ohita tärkeitä opittavia asioita ja kopioi valmista ohjelmaa! Kopioida osaa jokainen, mutta tällaista ohjelmaa ei kaikki osaa tehdä. Nyt on hyvä tilaisuus opetella tämä.
16.2.1 Ruudukon piirtäminen
- Aseta peli-ikkunan kooksi 255x255 pikseliä.
- Luo muuttujat width, height, ja margin. Aseta muuttujien width ja height arvoksi 20. Tämä asettaa kunkin ruudun koon. Aseta margin muuttujan arvoksi 5, joka määrää ruutujen välissä olevan tilan ja ikkunan reunojen paksuuden. Luo nämä muuttujat ennen ohjelman pääsilmukkaa.
- Piirrä valkoinen ruutu vasempaan yläkulmaan ja käytä annettuja arvoja height ja width, jotka edellä määritettiin. (Voit tietenkin asettaa värimäärityksen joksikin muuksi kuin valkoiseksi.) Tähän mennessä tehdyn ohjelman pitäisi näyttää tämän kuvan mukaiselta 16.3.
- Käytä for silmukkaa 10 ruudun piirtämiseen riville. Käytä muuttujan nimenä column for-silmukassa. Lopputulos näyttää yhdeltä pitkältä laatikolta, kunnes teemme ruutujen väliin marginaalit. Katso kuvaa 16.4.
- Säädetään piirtämistä asettamalla ruutujen väliin tyhjää margin muuttujalla. Nyt peli-ikkunan pitäisi näyttää tämän kuvan mukaiselta 16.5.
- Lisää marginaali jokaisen suorakulmion väliin ennen piirtämistä. Tällä tavalla saadaan pikkuruudut näkymään ikkunan reunasta reunaan. Katso kuvaa 16.6.
- Lisää toinen for silmukka, jolla saadaan tehtyä ruudut riveittäin. Muuttuja tähän
for silmukkaan nimetään row nimiseksi. Nyt saamme aikaiseksi täyden matriisin ruutuja.
Katso kuva 16.7.
16.2.2 Matriisin täyttö
- Seuraavaksi teemme kaksiulotteisen taulukon. Valitettavasti Python ei ole
kaikista helpoin kieli kaksiulotteisen taulukon käsittelyyn.
Pythonille löytyy ladattavia kirjastotiedostoja, joissa on käsittelyä helpottavia toimintoja.
Tässä esimerkissä emme kuitenkaan käytä näitä. Voit luoda kaksiulotteisen taulukon seuraavan koodiesimerkin
avulla:
# --- Create grid of numbers # Create an empty list grid = [] # Loop for each row for row in range(10): # For each row, create a list that will # represent an entire row grid.append([]) # Loop for each column for column in range(10): # Add a the number zero to the current row grid[row].append(0)
Lyhyemmin saman voisi tehdä alla olevalla yhden rivin koodilla. Tätä ei selitetä tarkemmin:
grid = [[0 for x in range(10)] for y in range(10)]
Käytä toista näistä kahdesta koodista luodaksesi peliruudukko. Sijoita koodi ennen pääohjelmasilmukkaa.
- Asetetaan taulukon yhteen soluun malliksi luku 1.
Kaksiulotteisessa taulukossa taulukon indeksointi esitetään niin, että ensimmäinen indeksi osoittaa rivin ja toinen indeksi osoittaa sarakkeen. Tämä 'rivi-ensin' esitystapa on käytössä useimmissa ohjelmointikielissä. Poikkeuksina mainittakoon Fortran ja MATLAB, joissa käytetään 'sarake-ensin' esitystapaa.
# Set row 1, column 5 to zero grid[1][5] = 1
Aseta tämä koodi ennen pääohjelmasilmukkaa.
- Aseta ruudun väri käyttämällä muuttujaa nimeltään color. Sijoita muuttuja siihen kohtaan koodia, jossa suorakulmiot piirretään. Ennen suorakulmioiden piirtämistä määritä colormuuttuja ja anna sille arvoksi WHITE.
- Asetetaan ruudukolle taustaväri ja tämän asetuksen lisäksi color muuttujan jälkeen lisätään if lause. If-lauseessa tutkitaan ruudukon grid[row][column] indekseissä olevien solujen arvot. Jos ruudukon solun arvo on 1, vaihdetaan ruudun väri vihreäksi. Ruudukossa pitäisi siis olla nyt yksi vihreä ruutu. Katso kuva 16.8.
- Tulosta näytölle “click”, jos käyttäjä klikkaa hiirellä ruudukkoan ruutua. Katso
bitmapped_graphics.py esimerkistä, miten saat tutkittua hiiren klikkauksen. - Tulosta hiiren koordinaatit kun käyttäjä klikkaa hiirellä.
Katso move_mouse.py tiedostosta malli siitä, miten hiiren sijainti saadaan luettua. Katso kuva 16.9. - Muunna hiiren koordinaatit ruudukon koordinaateiksi ja tulosta siis ruudukon koordinaatit. Muista tässä käyttää 'width' ja 'height' arvoja ruudukon paikkatietona ja huomio myös 'margin'. Lopulliset arvot on vielä muutettava kokonaisluvuiksi. Tämä voidaan tehdä joko käyttämällä int tai käyttämällä kokonaislukujakoa // normaalin jakolaskuoperaattorin / sijasta. Katso kuva 16.10.
- Vaihda se ruudukon ruudun väri vihreäksi, jota käyttäjä klikkasi. Katso kuva 16.11.
16.2.3 Lopullinen ohjelma
""" Example program to show using an array to back a grid on-screen. Sample Python/Pygame Programs Simpson College Computer Science http://programarcadegames.com/ http://simpson.edu/computer-science/ Explanation video: http://youtu.be/mdTeqiWyFnc """ import pygame # Define some colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) GREEN = (0, 255, 0) RED = (255, 0, 0) # This sets the WIDTH and HEIGHT of each grid location WIDTH = 20 HEIGHT = 20 # This sets the margin between each cell MARGIN = 5 # Create a 2 dimensional array. A two dimensional # array is simply a list of lists. grid = [] for row in range(10): # Add an empty array that will hold each cell # in this row grid.append([]) for column in range(10): grid[row].append(0) # Append a cell # Set row 1, cell 5 to one. (Remember rows and # column numbers start at zero.) grid[1][5] = 1 # Initialize pygame pygame.init() # Set the HEIGHT and WIDTH of the screen WINDOW_SIZE = [255, 255] screen = pygame.display.set_mode(WINDOW_SIZE) # Set title of screen pygame.display.set_caption("Array Backed Grid") # Loop until the user clicks the close button. done = False # Used to manage how fast the screen updates clock = pygame.time.Clock() # -------- Main Program Loop ----------- while not done: for event in pygame.event.get(): # User did something if event.type == pygame.QUIT: # If user clicked close done = True # Flag that we are done so we exit this loop elif event.type == pygame.MOUSEBUTTONDOWN: # User clicks the mouse. Get the position pos = pygame.mouse.get_pos() # Change the x/y screen coordinates to grid coordinates column = pos[0] // (WIDTH + MARGIN) row = pos[1] // (HEIGHT + MARGIN) # Set that location to one grid[row][column] = 1 print("Click ", pos, "Grid coordinates: ", row, column) # Set the screen background screen.fill(BLACK) # Draw the grid for row in range(10): for column in range(10): color = WHITE if grid[row][column] == 1: color = GREEN pygame.draw.rect(screen, color, [(MARGIN + WIDTH) * column + MARGIN, (MARGIN + HEIGHT) * row + MARGIN, WIDTH, HEIGHT]) # Limit to 60 frames per second clock.tick(60) # Go ahead and update the screen with what we've drawn. pygame.display.flip() # Be IDLE friendly. If you forget this line, the program will 'hang' # on exit. pygame.quit()ja aloita sitten tekemään omaa peliohjelmaasi!
16.2.4 Multiple Choice Quiz
Klikkaa tästä monivalintatehtävään.
16.2.5 Short Answer Worksheet
Klikkaa tästä kappaleen kertaustehtäviin.
You are not logged in. Log in here and track your progress.
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin