Del creador de "Back To Kowloon"

dimarts, 9 de novembre del 2010

Creant un editor de mapes (I)

Podeu descarregar el paquet corresponent a aquest post d'aqui -> tar.gz rar
(Recordeu que el codi estarà disponible en aquesta web)

Genial idea, eh? I tant que si.

Bàsicament, es tractaria de crear un petit "joc" que ens servirà com a eina de suport. L'objectiu del joc, per dir-ho d'alguna manera, és fer mosaics. Com els romans, si. Tenim unes poques rajoles i les podem anar combinant sobre una superfície buida per tal de conformar un mosaic (en aquest cas, un mapa per al nostre RPG). 

Més endavant, s'intentarà fer que el joc sigui capaç de guardar aquests mosaics a un fitxer. D'aquesta manera, tindrem un editor de mapes totalment funcional al que se li poden afegir altres funcionalitats (col·locació dels NPC, etc).

Ostres! La de feina que estalvia això! :D


Oi que si? n_n

La cosa queda més o menys així:


Vale, de conya. Ensenya'ns coses.

El programa de moment només pinta. No esborra, no opera amb fitxers ni tampoc et canta nadales. Els controls són:
  • Tecles direccionals: moure el cursor principal (el que pinta el mapa)
  • A / S: moure el cursor del selector de rajoles a esquerra/dreta
  • W: pinta tot el mapa amb la rajola seleccionada
  • Intro: selecciona una rajola
Abans d'entrar al tema...us voldria comentar un parell de coses que he descobert sobre Python.
  • No existeix una sentència switch-case. Enlloc d'això s'utilitzen diccionaris (probablement això ho veurem aquí més endavant).
  • En programació hi ha una famosa tècnica per comprovar que les coses funcionen que consisteix en col·locar missatges a llocs estratègics. Per exemple, imprimint els resultats d'uns càlculs intermitjos. A Python podem utilitzar la paraula clau "print" per a aquesta feina. Si executem el programa amb la consola de comandes, veurem aparèixer els missatges allà.
  • En cas de crear un mètode buit, per fer tests o per implementar més endavant, es pot utilitzar la paraula clau "pass" per indicar que el mètode no fa res. Per exemple: 
def metode_buit(self):
       pass
OK, fins aqui. Entrem a comentar una mica el funcionament del programa.

Si mireu l'arxiu world_editor.py, veureu que és bàsicament el mateix que game.py. Els únics canvis són els referents als controls del teclat i aquest fragment de codi:
    "Pintem l'escenari i el cursor"
    screen.blit(background,(0,0))
    scn.paint_cursor(screen)
    scn.paint_selector(screen)
Noteu que es podria posar dins del bucle del joc. Però això implicaria repintar el fons i els cursors un munt de vegades per segon. El rendiment baixarà i, si us hi fixeu, no és necessari fer-ho. Només ens cal pintar-ho un únic cop.

Si mireu la imatge que es fa servir com a fons, veureu que disposa d'un espai buit al mig per a pintar el mapa. Però, en canvi, no hi ha cap forat per al selector. Per tant, si pinteu el fons dins del bucle...el selector no es veurà : )

Proveu diverses combinacions i veureu de què coi us parlo.

Molt bé, el que tu diguis. Què més tens?

Tinc la classe scene_editor. Està basada en la clase scene, però fa altres coses. Per exemple, el mètode paint_scene() aqui pinta TOT el mapa amb la rajola indicada. (Noteu que cal repintar el cursor després). Explicaré alguns aspectes dels mètodes més problemàtics.  
Els mètodes move_selector() i move_cursor() poden donar més d'un maldecap a més d'un (i d'una). La idea bàsica és:
  • Per a move_selector(): Moure el cursor a dreta i esquerra per poder seleccionar una rajola d'entre x disponibles. Com a afegit estètic, en arribar a un dels dos extrems volem que el cursor salti a l'altre. De moment, ho he fet amb les set primeres disponibles al tileset que vaig escollir. Més endavant miraré d'afegir-les totes d'alguna forma.
  • Per a move_cursor(): Moure el cursor a dreta, esquerra, amunt i avall pel que seria l'espai del mapa (és a dir, la part negra de la finestra). Recordeu que vaig escollir fer un mapa de 18x9 columnes, per emular l'estil d'alguns antics RPG. Per tant, cal que el cursor es mantingui dintre d'aquestes 18x9 columnes i que no aparegui mai fora d'aquestes.
Fixem-nos en move_selector(). A partir d'aquest, implementar move_cursor() és trivial.
    def move_selector(self, screen, keyname):
        if keyname=="a":
            if self.selector_rect.x>34:
                self.selector_rect.move_ip(-34,0)
            else:
                self.selector_rect.x=238
        elif keyname=="s":
            if self.selector_rect.x<238:
                self.selector_rect.move_ip(34,0)
            else:
                self.selector_rect.x=34
        self.paint_selector(screen)
Com podeu veure és ben senzill: un cop es detecta que el cursor està a un dels extrems del seu espai (x<34 i x>238 col·locaria el cursor fora del selector), s'actualitza la posició del cursor per tal de que es mogui a l'altre extrem. Fixeu-vos que no he fet servir move_ip() en tots dos casos. Sabeu què? Funciona, però hauria d'haver fet servir move_ip() en tot per qüestions d'estil -_-

Amb mètodes com aquest es descobreix com de útils son els objectes Rect. Al mètode __init__() de scene_editor s'han inicialitzat els dos cursors i, a més, un parell de rectangles associats a aquests. Aqui teniu la raó: un cop has "copiat" (amb blit()) la imatge a la finestra, ja no la pots moure. Per moure-la cal retenir una còpia d'aquesta imatge en algun lloc, pintar sobre la posició antiga i copiar-la novament a la nova posició. 

Com veieu al final del mètode fem una crida a paint_selector() que repinta el selector un cop ja hem mogut el rectangle del cursor. En repintar el selector, es torna a copiar el cursor a la finestra. Però, com que hem mogut el seu rectangle, ara apareixerà en una altra posició.

El funcionament de move_cursor() us el deixo a vosaltres, com feia el meu profe de mates.


NOTA: Penseu que, amb move_cursor(), cal fer una altra cosa en moure el cursor. El selector és sempre el mateix, però el mapa el podem repintar i sempre canvia...

I què passa amb paint_selector() i paint_tile()? Què son tilerects[] i last_tile EH? ¬_¬

Doncs bé. Amb paint_tile() cal adonar-se que s'ha de "recordar" la darrera casella per tal de que es mantingui a la finestra. Aqui entra last_tile, que és un objecte Surface on es guarda la rajola que s'ha pintat en l'ultim moviment. Amb això aconseguim dues coses: ser capaços de repintar sobre el cursor en moure, i fer que el cursor pinti a mesura que es mou.

Si no es guarda aquesta informació...bé, comproveu-ho vosaltres mateixos. Es podria implementar de forma que last_tile fes una comprovació primer per veure si hi ha o no una rajola pintar. D'aquesta forma, el cursor només pintaria quan nosaltres li indiquéssim (per exemple, prement INTRO).

L'objecte tilerects[] s'utilitza per guardar les rajoles disponibles (en aquest cas set) i pintar-les sobre el fons amb el mètode paint_selector(); que s'encarrega de pintar també el cursor.

Pero quina merda d'explicacions que fas últimament!!! >_<'

Fent l'indio una estona : D

Bé, no tinc gaire temps -_- Però la veritat és que aquest programa és ben senzill ¬_¬ El problema està en que no domino el llenguatge i, també, que no estava habituat a com es representen les coses a la pantalla fins fa ben poc.

De totes formes, hi ha un sistema de comentaris ¬_¬


Wu Ying Ren 死
 

dijous, 28 d’octubre del 2010

Plantant herba

Podeu descarregar el paquet corresponent a aquest post d'aqui -> tar.gz rar
(Recordeu que el codi estarà disponible en aquesta web)

Si, plantarem herba. Herba virtual, però. I de la que no es fuma ¬_¬

I això com pot ser, eh?

Doncs sent. Avui pintarem gespa a la finestreta del joc. Aprendrem a tractar imatges per mostrar-les en pantalla. I farem una parida amb el text, per entendre una mica com funciona Rect.

El codi s'ha dividit en tres fitxers (i més que en vindran) per una qüestió de sentit comú. Hi ha gent que potser creu que és millor ficar tot en un únic arxiu. Jo no ho penso.

Així doncs, tenim tres fitxers de codi: game.py, scene.py i text_panel.py. El primer és el nucli del programa (el que teníem fins ara, en gran part), el segon representa l'escenari del joc i el tercer representa un panel de text. No calia descriure-ho, oi? Gràcies.

En acabar, si seguiu els passos, tindreu això (o similar):

OH MY FUCKIN' GOOOOD!!!! O_O
Ostres, quina passada!!!! Explica ja el que ens interessa, pesat!

Primerament tenim scene.py. Pensem primer en què representa: un objecte de tipus scene representa l'escenari on es mourà el personatge. Tots els que hem jugat a algun RPG clàssic en 2D tenim una imatge clara del que representa. Els que no, podeu mirar la captura anterior...és la part verda. I com implementem això?

Doncs bé, potser hi ha dos cents mètodes millors que aquest, però jo no els conec. Així que imagineu que la vostra finestra està dividida en caselles iguals, formant una graella. La "casella" que jo faig servir té una mida de 32x32 pixels. Per tant, imagineu una graella de 20x15 sobre la finestra. Així:

Tranquils, no cal que imagineu
Tenint això, sembla fàcil. Només és qüestió de pintar una a una les caselles que ens interessi. Si es fan servir caselles d'altres mides (per exemple 128x128), la graella serà diferent.  Però el mètode és el mateix. Fins i tot fent servir tiles (així es diuen en anglès) rectangulars o d'altres formes, el mètode és el mateix o semblant (però es pot complicar bastant si sortim dels quatre costats).  

Perfecte, com fem això?

Observeu el codi següent, pertanyent a scene.py.
class scene:
  
    def __init__(self):
        self.tile = pygame.image.load("../data/tiles/grass.bmp").convert_alpha()
           
    def paint_scene(self,screen):   
        for x in range (1, 19):
            for y in range (1, 10):
                screen.blit(self.tile, (x*32, y*32))
Què tenim aquí? Doncs bé, tenim dos mètodes. El primer s'executa en crear un objecte de tipus scene. El que fa és carregar una imatge al atribut tile. De moment, pintarem tota la graella amb la mateixa imatge per simplificar. En el futur veurem com manegar un tileset. Un tileset és el resultat d'aplegar un munt de caselles diferents com la que fem servir avui (tile) en una mateixa imatge. Llavors es carrega aquesta imatge única a la memòria i després es van seleccionat parts (de 32x32 en el nostre cas) segons convingui. Per a aquest propòsit, pygame permet definir múltiples sub-surfaces d'un objecte Surface, que representen una part d'aquesta superfície (Surface) i n'hereten la informació. Però això no ve al cas ara.

Cal no oblidar convert_alpha(). El canal alpha d'una imatge permet definir el grau de transparència d'aquesta. Com que cada format d'imatge (bmp, png, jpg, ...) el guarda de forma diferent (si es que el guarda), cal aplicar convert_alpha() per tal de mostrar les transparències correctament. De moment, no en tenim...però més endavant veurem que ens resulta molt útil, per exemple, per dibuixar al personatge sobre l'escenari.

D'acord. Així doncs, en crear un objecte scene, se li adjudica automàticament una imatge.

Però, per què declares l'atribut com a self.tile i no com a tile?

Bona pregunta. Proveu a declarar sense posar la paraula clau self.

Bé, anem per feina. Què fa el segon mètode? Retorneu a la imatge de la graella. Teníem una graella de 20x15, oi? Molt bé. Si la volguéssim pintar tota sencera, podríem anar pintant una per una totes les quinze caselles de la primera fila. Després les de la segona fila, la tercera, etc...fins al final de la columna 20.

Feu la prova. Per pintar una única casella, el mètode paint_scene() només ha de fer el següent:

     def paint_scene(self,screen):   
         screen.blit(self.tile, (0*32,0*32))
Si volem pintar qualsevol altre casella, només cal canviar els 0 pel nombre de fila/columna corresponent. Feu proves.

I si volem pintar tota una fila o tota una columna? Doncs cal fer un bucle que passi casella per casella. Així:
    def paint_scene(self,screen):   
        for x in range (0, 20):
                screen.blit(self.tile, (x*32, y*32))
Tenim un bucle for que funciona mentre s'acompleix la condició 0<=x<=20 i que, per tant, pinta una per una totes les caselles de la fila. Per pintar columnes només cal posar y enlloc de x al bucle for. Però el que a nosaltres ens interessa és que les pinti totes, no? Llavors cal que després de pintar tota la primera fila, pinti la segona. O també, pintar la primera columna sencera, i passar a la segona. Justament això és el que fa el segon mètode. Pinta columna per columna fins omplir tota la graella amb un bucle dins d'un altre bucle. Els que feu recorreguts de matrius, ja sabeu de què parlo eh?

Podeu pintar la graella com us doni la gana. Jo he decidit donar al meu joc un aspecte retro "posant" un marc al voltant de l'escenari. Més endavant potser ho canviaré i utilitzaré les 20 columnes deixant només les files buides al damunt i a sota. Això si no em dona per posar més resolució.

Si us hi fixeu, aquest mètode utilitza self.tile enlloc de tile un altre cop. La paraula clau self equival al this de Java i al self de C. Si no declarem tile com a self.tile, llavors paint_scene() no el veu i no hi pot accedir.

Perfecte, diria que ho he entès. Què més tens?

Que pesats que sou, segur que no heu entès una merda ¬_¬ Mireu l'aspecte del nou main().

def main():
    screen = pygame.display.set_mode((640,480))
    clock=pygame.time.Clock()

    scn = scene()
    tpanel = text_panel()
    active=True
   
    while active:
        clock.tick()
        pygame.display.set_caption("Classic RPG v0.01"+ " FPS: "+ str(clock.get_fps()))
       
        scn.paint_scene(screen)
       
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                active=False       
            elif event.type == KEYDOWN and event.unicode == 'q':
                tpanel.hide(screen)
            
        pygame.display.update()
        pygame.time.wait(15)
Hi ha unes quantes diferències, eh? Primerament s'ha inclós un atribut clock per mesurar els FPS (fixeu-vos en l'afegit a set_caption()). Només cal cridar a tick() un cop per a cada iteració del bucle del programa. La crida a wait() està posada per "simular" que el programa fa coses. Si la treieu, veureu que els FPS es disparen n_n 

Si, és una pijada. Ningú us hi obliga a posar-ho, però és útil per veure quin marge es té en quant a rendiment. Més endavant ho entendreu, petits padawans.

A més d'això s'han creat els objectes scene() i text_panel() corresponents i dintre del bucle es crida a paint_scene() per tal de pintar la gespa (o el que hagueu posat). I més endavant...
            elif event.type == KEYDOWN and event.unicode == 'q':
                tpanel.hide(screen)

Què coi és això?

Una altra pijada. Quan el programa detecta que hem premut la tecla Q, crida a hide(). És una funció de text_panel que mostra i oculta el panell de text. Ens serà útil més endavant per amagar el text quan el personatge s'estigui movent i tal. O quan tinguem més d'un panell i ens interessi anar passant  d'un a altre. I, a més, podreu ensenyar a les amigues que la vostra merda de programa fa coses. Al cap i a la fi, això és el que importa -_-'

Sempre m'he preguntat com s'ho devien manegar per fer aquestes coses.

Si? Doncs, amb màgia :D 
def hide(self, screen):
        if self.active:
            screen.fill((0,0,0),self.textRect)
            self.textRect.move_ip(0,300)
            self.draw_text(screen)
            self.active = False
        else:
            self.textRect.move_ip(0,-300)
            self.draw_text(screen)
            self.active = True
Us adoneu del que fa? Quan el panell està marcat com a actiu, pinta de negre a sobre i mou textRect (ara us ho explico, tranquils) fora de la part visible de la finestra. I quan detecta que el panell està inactiu, el que fa es tornar a posar el rectangle textRect a la seva posició original. Cada crida a display.update() s'encarrega de mostrar els canvis per pantalla en detectar que hi ha hagut canvis en algunes zones. Recordeu que display.flip(), a diferència de update(), actualitzaria la finestra sencera (amb la consegüent pèrdua de rendiment).

És ben ridícul, oi? Doncs us foteu, se m'ha acudit a mi primer n_n

Si aneu una mica més amunt en el codi...
        font=pygame.font.Font("../data/fonts/Final Fantasy I - NES.ttf",46)
        self.text=font.render("Em penso que encara no m'has dit el teu nom...", 1, (255,255,255))
        self.textRect=self.text.get_rect()
        self.textRect.x,self.textRect.y=32,650
Si us fixeu, he ficat el text en un objecte Rect fent servir get_rect(). Per què he fet això? Molt senzill, abans havíem "incrustat" el text a la finestra. Ficant-lo en un rectangle, el tenim "per sobre" de la finestra. És una capa independent que podem moure i editar sense modificar el fons. Això ens permet, justament, amagar-lo fora de la finestra amb hide().

I si, he fet servir la font del FF1 original. Sóc així de freak. I les tiles són del RPG Maker original, per si us cou per dins el dubte. Teniu un parell de links (és a dir, dos) al final del post amb webs sobre el tema. Els que he fet servir jo són gratuits i lliures, per que no són originals (son imitacions).

Què guai, no? Jugueu amb les ilusions dels gamers ¬_¬

Si us pensàveu que els programadors es trencaven el cap fent coses gairebé impossibles...doncs potser el vostre problema és que esteu seguint al programador equivocat.

Ja està bé per avui. Hauria estat bé afegir algun nino i fer-lo moure per la pantalla. Però com veureu, això si que representa una feinada. Posar el nino és ben fàcil, però després de moure'l, cal actualitzar la pantalla repintant les parts per on passa.  Es mereix un post sencer. Jo encara no ho se fer, i vosaltres?

Només em queda remarcar que m'heu fet perdre una tarda d'estudi. Ja us val ¬_¬


Wu Ying Ren 死

Enllaços d'interès:

dimarts, 14 de setembre del 2010

Pinta i acoloreix


Perfecte, ja tenim una finestra. Però no fa res, i només es veu tot negre. Jo ho vull blanc.

Ah, jo ho vull blanc. Es que ho volem tot, eh? ¬_¬

Doncs mireu el codi que vam fer l'altre dia. Fixeu-vos en aquest fragment del mètode main():
    pygame.display.set_mode((640,480))
    pygame.display.set_caption("Hello World")
    pygame.display.flip()
Com ja vam veure, seleccionem la resolució i el títol de la finestra. I després fem que pygame actualitzi la finestra, oi? Doncs bé, si fem un parell de canvis...
    screen = pygame.display.set_mode((640,480))
    pygame.display.set_caption("Hello World")
    screen.fill((255,255,255))
    pygame.display.flip()
A la primera instrucció hi posem una assignació. Bàsicament, creem un objecte que es diu screen, i que representa a la finestra del programa. I just abans del flip(), posem una nova instrucció. Aquesta instrucció fill() s'encarrega d'omplir la pantalla amb un color que li indiquem mitjançant el seu codi RGB (Red-Green-Blue). El blanc és (255,255,255), el negre és (0,0,0), el vermell (255,0,0), etc. Si canvieu el codi, canviara el color amb que s'omple la pantalla.

Vale, super-guai. Ara vull escriure xorrades : )

Molt bé. Escriure text a la línia de comandes sol ser molt fàcil, ja que és la sortida estàndard. Però...com s'escriu text en una finestra? Doncs, si ho he entès bé, es tracta de crear una capa (representada per un objecte de tipus Surface) on es posa el text per tal de superposar-la amb la capa visible de la nostra finestra i fusionar-les. Us podria fer una dissertació sobre el tema amb tot de paraules tècniques i complicades, però la realitat és que jo també estic aprenent. La idea bàsica que heu de tenir al cap és que els objectes Surface són com fulls de paper que tenim a l'escriptori i que fem servir per "pintar" el nostre programa. La finestra principal n'és un, però en podem crear més, superposar-los, fusionar-los, etc...

I per què no "pintem" el text directament a la capa de la finestra? Doncs per que pygame no ho permet. No pots escriure el text directament a la Surface principal, n'has de crear una nova i llavors fusionar-la com s'ha dit.

Així, hauríem d'afegir un fragment de codi com aquest que segueix:
    font=pygame.font.Font(None,36)
    text=font.render("Aqui van les xorrades...", 1, (0,0,0))
    screen.blit(text,(20,450))
Amb la primera instrucció seleccionem quina font volem fer servir, i la mida. Guardem les dades en un objecte amb nom font. El primer paràmetre del constructor Font() especifica la font que es vol fer servir. El valor "None" fa que pygame seleccioni la font predeterminada. Existeix un mètode get_fonts() que retorna una llista amb les fonts disponibles, potser ens serà útil més endavant en algun menú de configuració o quelcom semblant. El segon paràmetre és la mida, evidentment.

Ara haurem de crear la capa amb el text. D'això s'encarrega la segona instrucció: font.render() crea un objecte Surface, una imatge amb el text. Així doncs, l'objecte text és un objecte de tipus Surface.

Aquí caldria esmentar una de les característiques més curioses de Python. A llenguatges com Java o C, quan es crea una variable cal especificar quin tipus de dades es vol fer servir. Python ho assigna automàticament. En aquest cas, per exemple, font.render() crea una Surface. En assignar-li el nom text, Python crea una Surface amb nom text, per que és el que s'ha de crear. Sense més. Jo prefereixo el sistema clàssic, personalment. Però crec que un cop s'assoleixen uns bons coneixements del llenguatge, probablement t'hi acostumes i dinamitza molt la feina.

Parlem ara dels paràmetres de render(). El primer de tots...està clar, no? El text que es vol escriure. En teoria no s'han de col·locar aquest tipus de missatges al codi, però de moment estem fent els primers passos i els posarem aquí directament. Més endavant, ja es veurà com fer-ho segons els cànons. El segon paràmetre indica si es vol fonts amb antialiàsing (valor 1), o sense (valor 0). Activar l'antiàliasing ens farà tenir fonts suavitzades, que llueixen millor. Evidentment, això tindrà un cost en el rendiment del programa...però crec que ens el podem permetre : ) 

El darrer paràmetre és, novament, un codi RGB per indicar el color del text (en aquest cas, negre). Mireu la diferència entre fonts suavitzades (a la dreta) i fonts no suavitzades (a l'esquerra).

 



Finalment tenim una instrucció amb la que fusionem les dues Surface en una. La funció blit() "incrustarà" el text en la pantalla. D'aquesta forma, quan fem flip() o update(), el text apareixerà en pantalla. Els paràmetres de blit() són senzillament la Surface que es vol "incrustar" (en el nostre cas text) i la posició a la pantalla indicada per unes coordinades cartesianes (x,y) de les de tota la vida. La x és la posició horitzontal a partir de la qual s'ha de col·locar el text, i la y la vertical.

Ah! No cal oblidar-se d'inicialitzar el sistema de fonts. I com es fa això? Molt fàcil, incloent la instrucció següent:
    pygame.font.init()

De conya! M'estic emocionant! Vull posar ninos i fer que es matin entre ells!!!

Para el carro. Això ho deixem per la propera lliçó. Posar sprites a la pantalla sé, però fer-los moure...ja veurem si ho aconseguim xDD Aquest post sembla curt i senzill, però té una càrrega teòrica força gran. Entendre'ls us ajudarà a avançar sense limitar-vos a copiar el codi.

Us deixo el codi complet amb les modificacions fetes avui.

'''
@author: wuyingren
'''
import pygame

def main():
    screen = pygame.display.set_mode((640,480))
    pygame.display.set_caption("Hello World")
    screen.fill((255,255,255))
    # On-game text
    font=pygame.font.Font(None,36)
    text=font.render("Aqui van les xorrades...", 1, (0,0,0))
    screen.blit(text,(20,450))
   
    pygame.display.flip()
    active=True
   
    while active:
       
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                active=False               
   
if __name__=='__main__':
    pygame.display.init()
    pygame.font.init()             #Es pot posar també a l'inici del main()
    main()



Wu Ying Ren 死

dimecres, 18 d’agost del 2010

Primeres passes : )

Al final, he escollit Python. Per què? Per que vull aprendre un llenguatge nou, i que el producte sigui open source. I poc més. No descarto, si la cosa avança i tinc temps, fer un port a XNA i matar dos pájaros de un tiro. Però, de moment, Python. Algunes característiques remarcables de Python són aquestes:

-Python és un llenguatge interpretat. Això vol dir que fa servir un intèrpret.

-Python és dinàmic. Això vol dir que pot interpretar el codi en temps d'execució.

-Python proporciona una sintaxi molt simple. Com les meves explicacions xD

I com a mostra, el típic "Hello World".

print "Hello World"

I res més. Si poseu aquesta línia en un fitxer .py i feu python fitxer.py o directament a l'intèrpret (executant python des de la línia de comandes), obtindreu el resultat esperat. Més simple? Es pot, però per fer-ho més simple ens caldrien dispositius lectors de la ment que ara mateix (crec) no estan disponibles xD

L'intèrpret pot manegar operacions matemàtiques simples com ara 3+5 directament. També pot fer càlculs en coma flotant si fem, per exemple, 3.+5 (o 3+5.). I segur que fa càlculs més complexos i altres coses que desconec i us faran flipar, segur.

Perfecte. I ara què?

Doncs ara, potser que provem a fer alguna cosa. Per exemple, crear una finestra.



Apa, què difícil!!

No ho és tant. I gràcies a pygame, encara menys. Compteu les línies de codi que es necessiten:

"@author: wuyingren
import pygame

def main():
    pygame.display.set_mode((640,480))
    pygame.display.set_caption("Hello World")
    pygame.display.flip()
    active=True
   
    while active:
       
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                active=False               
   
if __name__=='__main__':
    pygame.display.init()
    main()

No arribem a 20.

Mola! Però...què significa tota aquesta merda?

Doncs bé. És molt fàcil. Primerament, tots els que hàgiu fet programació sabreu intuir què dimonis és la instrucció "import". Per als que no ho sàpiguen: la fem servir per carregar mòduls externs que utilitzarem al codi (en aquest cas, els pertanyents a pygame). Els comentaris, per cert, es marquen amb unes cometes " .

Abans de seguir cal entendre bé una cosa. Python "interpreta" els sagnats. Què vull dir amb això? Que enlloc de fer servir els típics claudàtors { } i els típics punts i coma ";" per delimitar els mètodes, Python fa servir els dos punts ":" i  el sagnat. Així, cal respectar els diferents nivells de sagnat. Aquest fragment de codi, per exemple, seria incorrecte i l'intèrpret ens retornaria error:

    while active:
       
        for event in pygame.event.get():
        if event.type==pygame.QUIT:
       active=False       

La forma correcta és:
    while active:
       
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                active=False         

Us recomano l'ús d'algun IDE com ara Eclipse (amb el mòdul pydev) que us ajudi a formatar el codi automàticament, per que pot donar molts maldecaps. Sobretot als despistats, com un servidor.

Bé, passem a la carn. El nostre codi està format per dues parts clarament diferenciades. Una és el mètode main, i una altra...el bloc if del final. Aquest bloc és una mena de "trampa" que es fa servir per indicar què cal fer si el mòdul no s'ha importat. És a dir, si l'intèrpret arriba al nostre codi directament (i no a causa d'una instrucció mygame.main()), aquest bloc if li indica que ha de fer el següent:
    pygame.display.init()
    main()
La primer instrucció inicialitza un objecte pygame.display, que representa (i controla) una finestra i el seu contingut. La segona instrucció li indica, bàsicament, que executi el mètode main(). Ara ja sembla lògic i tot, oi? : )

Si feu la prova de treure aquest fragment de codi, veureu que la finestra no apareix. Però si cridéssiu el main() des d'un altre mòdul (o des de l'intèrpret, fent l'import corresponent), llavors SI que us apareixeria la finestra.

OK, ho entenc. I què coi fa el main()?

Doncs configura uns quants paràmetres de la finestra (les mides i el títol)

    pygame.display.set_mode((640,480))
    pygame.display.set_caption("Hello World")

I després, cridem flip() per tal de dibuixar la finestra a la pantalla. Després, creem un boolean active que ens serveix per controlar si la finestra s'ha de tancar. El bucle següent:

    while active:
       
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                active=False   

Fa que, mentre active tingui valor True, es busqui (pygame.event.get()) si existeix un event de tipus QUIT. Si es dona el cas, es marca active com a False. A la següent iteració, la condició es trenca i el programa surt del bucle. Com que no hi ha més instruccions, el programa termina.

Si feu la prova, la finestra es tanca tant fent clic al botó corresponent de la barra de títol com prement Alt+F4.

I jo diria que, de moment, ho podem deixar aquí. Si teniu preguntes, suggerències o crítiques les podeu expressar als comentaris. A la web de pygame hi ha un munt de tutorials i exemples més, i també en d'altres webs dedicades a això mateix. Podeu consultar alguns links al final del post.
Al proper post tractarem com posar cosetes a la finestra i tal...Fins llavors!

Wu Ying Ren 死


Enllaços d'interès:

divendres, 13 d’agost del 2010

Primer (bé, segon xD) obstacle: Java? Python? C#?

Confesso que ja hi ha una part de codi feta en Python, però...ja que he fet un bloc, potser que comencem des del principi, no? ^_^

Des d'un primer moment tenia clar quin tipus de joc volia crear: un RPG clàssic, d'estil japonès. Un petit homenatge a obres mestres com els primers Final Fantasy. Altres opcions que em van passar pel cap van ser crear un petit manager de futbol o F1, un joc conversacional, un arcade o un joc de preguntes i respostes en plan Trivial Pursuit. Però, finalment, em vaig decantar pel RPG per una raó: un amic em va recordar que jo ja tenia un projecte de RPG. Un que, amb sort, encara deu estar als servidors de la UPC xDD

El cas és que vaig descartar aquests altres projectes per a un futur. I, un cop decidit quin tipus de joc crear, la primera pregunta que em vé al cap és aquesta: quin llenguatge faig servir per programar el meu joc? Ràpidament em va venir un llenguatge al cap: Java. L'únic que conec una mica bé, de fet. 

Llavors vaig pensar que estaria bé crear el joc, per exemple, per a un sistema comercial com ara la PSP. Em vaig informar i vaig trobar que Microsoft té una plataforma per a indie developers, com jo ara mateix. XNA, un estàndard basat en C# creat  per Microsoft que permet crear un joc cross-platform compatible amb sistemes Windows, Xbox 360, Zune i Windows Phone. Pots enviar jocs a Live i, amb sort, algú els juga. Sony ha creat recentment les minis, uns jocs descarregables limitats a 100 mb per a PS3 i PSP on estudis independents poden trobar la seva oportunitat. Són senzills i barats. Però cal establir un estudi, presentar un projecte seriós i pagar certes quantitats. De moment, per desgràcia per a mi, l'univers PlayStation queda descartat.

Finalment vaig recordar un llenguatge que havia provat d'aprendre pel meu compte i em va semblar força flexible: Python.

Tres alternatives. Què m'ofereix cadascuna? Doncs, a grosso modo...

- Java
  • Pros: Enorme portabilitat, facilitat d'ús, el domino bastant :D
  • Contres: No em convencen les llibreries existents, sembla més indicat per a aplicacions que per a jocs, li tinc mania xD
- XNA/C#
  • Pros: Abstracció molt alta, proposta amb futur, suport de Microsoft, possibilitat de publicar el joc en un lloc "concorregut", publicar per a PC és gratuït.
  • Contres: Portabilitat limitada (per exemple, Linux es queda fora), el producte no serà open source, he d'aprendre C# (cal dir que s'assembla a Java), no tinc una X360 :(
- Python
  • Pros: Gran portabilitat (fins i tot alguns SO com Symbian), flexibilitat, pygame proporciona bona abstracció.
  • Contres: És complicat d'entendre (és un concepte diferent), encara no el domino, requereix instal·lar un software que no està tan estés com ara la JavaVM.
Si algú s'està fent la pregunta, ha d'analitzar les seves pròpies possibilitats. A mi Python, tot i agradar-me, en dóna força maldecaps. Resulta confús fer-se amb un concepte nou un cop ja t'han ficat al cervell que les coses es fan d'una altra manera. XNA és una eina que em podria ser molt útil en el futur. Java i Python, però, aporten un component de llibertat extra. Tot i que Java...alguna cosa em fa descartar-lo ja d'entrada -_-

S'accepten suggerències i consells. No ho tinc gaire clar, la veritat. No obstant, suposo que tiraré endavant el que ja tinc fet en Python i, si de cas, es podria fer un port a C# més endavant. Algú vol dir la seva?

Wu Ying Ren 死

dijous, 12 d’agost del 2010

Primer dia (més o menys)

Se suposa que jo m'havia de dedicar a fer videojocs i tal. Però en aquest país això no és gaire fàcil. Així que un dia em vaig dir: fes-ne un pel teu compte. I ja farà un any que m'hi vaig posar. Amb un amic i alguns tutorials trobats aquí i allà ens vam posar a fer un RPG clàssic d'estil japonès. Però ho vam deixar córrer als pocs mesos per diversos motius.

I avui m'he dit: allò dels rectangles ho pots fer ben fàcilment, el mapa està dividit en rectangles dropo. I vosaltres direu: com? Doncs això, que crec que sé com seguir tirant endavant el projecte. I llavors m'he dit: hauries de fer un bloc per a que quedi constància i es pugui fer servir el teu treball com a referència.

I aquí el teniu. Us presento "Name your Hero", un bloc on intentaré anar posant les meves aventures en el món de la programació de videojocs (amateur) ^_^

またね!

Wu Ying Ren 死