domingo, 11 de diciembre de 2011

Objetos del Mundo

Hola a todos, durante estos días que no he publicado nada he ido mejorando la estructura del juego para que nos sea más fácil de entender a todos.
No os asustéis, todo lo que hemos visto hasta ahora no cambia mucho. Lo que he hecho es redefinir y reubicar los métodos de algunas clases y crear otras que nos será de utilidad en futuras entregas.
Hoy voy a presentar la clase Objeto_Mundo. Esta clase establece los valores por defecto y los métodos comunes de todos los objetos con los que se quiere interactuar en el juego (Jugador, NPC, Objetos varios, etc..).
Os adjunto el código fuente con los comentarios correspondientes para que no os perdáis mucho.
# -*- coding: utf-8 -*-
import pygame
from utilidades.imagen import cortar_tileset

jueves, 13 de octubre de 2011

Hola Mundo

... y con ello no me refiero al típico programa de iniciación en todos los lenguajes de programación.

Hasta ahora teníamos un mapa y un personaje que se movía por él sin problemas.

Bien, ¿y si queremos tener más estados de juego y más personajes?, ¿en cada estado del juego tendremos que repetir el código de dibujar el mapas y los personajes?.
Pues no, para eso vamos a crear una Clase Mundo a la que le pasaremos el mapa y los personajes que van a intervenir.

Para ello creamos en el raiz del proyecto un archivo llamado mundo.py con el siguiente codigo:
# -*- coding: utf-8 -*-

import pygame
from pygame.locals import *

from mapa import *
from personaje import *

sábado, 8 de octubre de 2011

Colisiones del personaje con el mapa

Hoy vamos a detectar las colisiones con el mapa y no permitir el movimiento en las zonas no "pisables".

Para ello vamos a usar la capa llamada "Pisable" que creamos con las X rojas para indicar que por allí no se podría pasar.


Primero en la clase Mapa (mapa.py) vamos a crear un método que nos dirá si se puede pisar o no en la celda indicada.

def es_pisable(self, fila, columna):
        # Si se intenta mover fuera del mapa, no se permite
        if ((fila >= self.height) or (fila < 0) or (columna >= self.width) or (columna < 0)):
            return False
        else:
            return not (self.capas[Mapa.LAYER_PISABLE][fila][columna])

jueves, 6 de octubre de 2011

El Personaje

Bueno hoy vamos a crear un personaje que se mueva por nuestro mapa. Lo vamos a hacer inicialmente sin animaciones, solo que se mueva de celda en celda del mapa.


Inicialmente vamos a añadir un método en la clase Mapa para que nos devuelva la posición central de una celda del mismo.

class Mapa:

    ...

    def obtener_centro_celda(self, fila, columna):
        x = (self.tam_tiles[0] * columna) + (self.tam_tiles[0] / 2)
        y = (self.tam_tiles[1] * fila) + (self.tam_tiles[1] / 2)
        return (x,y)



Este método nos servirá para centrar el personaje en la celda en la que se encuentra.

Ahora vamos a crear la clase Personaje en un archivo en el directorio raíz llamado personaje.py.

# -*- coding: utf-8 -*-
from pygame.locals import *

from utilidades.imagen import *

class Personaje():

miércoles, 5 de octubre de 2011

Cargar mapas en el juego

Bueno llegó el momento de cargar el mapa creado en el articulo "Crear mapas con Tiled Map Editor".

Para ello primero crearemos el archivo mapa.py en la raiz del proyecto.

# -*- coding: utf-8 -*-
from xml.dom import minidom
import base64
import gzip
import StringIO

from utilidades.imagen import *
from utilidades.archivo import *

class Mapa:

    LAYER_PISABLE = 0
    LAYER_SUELO = 1
    LAYER_OBJETOS = 2
    LAYER_OBJETOS_SUPERPUESTOS = 3
    LAYER_CIELO = 4

Crear mapas con Tiled Map Editor

El Tiled Maps Editor es un programa que permite generar mapas de una forma sencilla para la creación de juegos.

Para instalarlo en linux, en distribuciones basadas en debian, debéis ejecutar:

apt-get install tiled

Para los usuarios de windows podeis acceder a la página del proyecto y descargaros la ultima versión.

http://www.mapeditor.org/

Bueno, ahora ejecutamos el programa y creamos un mapa nuevo.
En tamaño del patrón debeis especificar el tamaño de los tiles que forman nuestro tileset. En este caso será de 16x16 pixeles.


En Edición -> Preferencias deberemos dejar las opciones como muestro ahora. Esto es importante porque cuando carguemos el mapa en el juego, este debe estar comprimido en Base64 gzip.




Ahora en Capa -> Añadir Capa de Patrones, añadiremos las capas que muestro a continuación en el orden que veis.

Nueva galeria de imagenes

El otro día vimos como gestionar las imágenes del juego.

Pues bien, he pensado que para el proyecto es mejor que useis estas imágenes:
Descargar imágenes

(Las imágenes están extraidas de los juegos del pokemon).

Recordad, las teneis que descomprimir en la carpeta data/imagenes/.
Así pues quedarán de la siguiente forma:
data/imagenes/enemigo.png
data/imagenes/jugador.png
data/imagenes/policia.png
data/imagenes/tileset.png

En este caso los tilesets son de 16x16 pixeles.
Los nombres son bastantes descriptivos de lo que contienen ;).

Bueno, en el proximo artículo os explicaré como crear nuestro primer mapa.

Nos vemos.

viernes, 30 de septiembre de 2011

Gestión de imagenes (Tiles)


Hoy vamos a explicar como gestionar las imágenes que usaremos en el juego.

Tanto, las imágenes del terreno, como la de los personajes, etc... se gestionarán de la misma forma.

Aquí tenéis un ejemplo de como es la imagen del terreno:


Primero debéis descargaros las imagenes.
Las imágenes las puedes descargar desde aquí:
Angband

Una vez descargadas, las descomprimimos en la carpeta data/imagenes/Angband.

Dentro se os crearán subcarpetas como Characters, Ground, etc ...
Allí podéis ver las imágenes que usaremos para el juego.

Bueno empezamos, un tileset es lo que veis en la imagen de arriba un conjunto de imágenes pequeñas que forman una más grande.
Como podéis observar, todas las porciones (tiles) de la imagen tienen el mismo tamaño, en este caso 32x32 pixels.

Para poder trabajar con esa imagen tan grande, lo que vamos a hacer es crear un array unidimensional en el que cada uno de sus elemento va a ser un trocito de esa imagen tan grande.
Así, si luego queremos imprimir el tile del césped, solo deberemos acceder a la posición 10 del array y obtener la imagen.

Es muy sencillo, ahora veréis.

NOTA: mucha de la información que veréis en este tutorial la he sacado de esta página http://razonartificial.com.

Creamos la carpeta utilidades/ y dentro el archivo imagen.py.



import pygame
from pygame.locals import *

def cargar_imagen(filename, transparent=False, pixel=(0, 0)):
""" Carga una imagen y estblece un color como transparente si se desea. """
try: image = pygame.image.load(filename)
except pygame.error, message:
raise SystemExit, message
image = image.convert()
if transparent:
color = image.get_at(pixel)
image.set_colorkey(color, RLEACCEL)
return image

def cortar_tileset(filename, (w, h), con_None=False):
""" Corta un tilest y lo almacena en un array unidimensional. """
image = cargar_imagen(filename, True)
rect = image.get_rect()
col = rect.w / w
fil = rect.h / h
sprite = []
if con_None:
sprite = [None]

for f in range(fil):
for c in range(col):
sprite.append(image.subsurface((rect.left, rect.top, w, h)))
rect.left += w
rect.top += h
rect.left = 0

return sprite


Como podeis ver el metodo cargar_imagen lo que hace es cargar una imagen del disco simple y llanamente.
Ahora el cortar_tileset lo que hace es obtener recortes de la imagen grande y almacenarlos en un array.

Lo interesante de este método es como obtiene el numero de de tiles que forman la imagen.
Para ello al metodo le tenemos que indicar que vamos a trabajar con tiles de 32x32 pixeles.

Y luego él obtiene el numero de filas y columnas que tiene esa imagen con una simple fórmula:

col = rect.w / w
fil = rect.h / h

En nuestro caso rect.w (es decir el width de la imagen) es de 288 y el rect.h (height) es de 608.
col = 288 / 32 = 9
fil = 608 / 32 = 19

jueves, 29 de septiembre de 2011

Control de los Frames

Para que nuestro juego funcione correctamente debemos establecer una tasa de Frames Por Segundo (FPS). Digamos que el número de veces que se dibuja, actualiza y recoge eventos por segundo.

Todo esto lo controlaremos con la clase FpsClock.

import pygame.time


class FpsClock:
   "class for managing FPS related stuff"
   def __init__(self, desired_fps=30, do_report=0):
       "create FpsClock instance, give desired running fps and enable report"
       self.do_report = do_report
       self.frame_count = 0
       self.frame_timer = pygame.time.get_ticks()
       self.frame_delay = 0
       self.last_tick = pygame.time.get_ticks()
       self.set_fps(desired_fps)
       self.current_fps = 0.0


   def set_fps(self, desired_fps):
       "set the desired frames per second"
       if desired_fps:
           self.fps_ticks = int((0.975/desired_fps) * 1000)
           #slight fudge, not quite 1000millis
       else:
           self.fps_ticks = 0
       self.desired_fps = desired_fps


   def tick(self):
       "call this once per frame"
       #delay until milliseconds per frame has passed
       if self.fps_ticks:
           now = pygame.time.get_ticks()
           wait = self.fps_ticks - (now - self.last_tick)
           pygame.time.delay(wait)
           self.frame_delay += wait
       self.last_tick = pygame.time.get_ticks()

       #update current_fps
       self.frame_count += 1
       time = self.last_tick - self.frame_timer
       if time > 1000:
           time -= self.frame_delay
           if not time: self.current_fps = 1.0
           else: self.current_fps = self.frame_count / (time / 1000.0)
           self.frame_count = 0
           self.frame_delay = 0
           self.frame_timer = self.last_tick
           if self.do_report: self.report()


   def report(self):
       "override this for fancier fps reporting"
       subst = (0,0)
       if (self.current_fps > 0):
           subst = 1.0/self.current_fps, self.current_fps
       return 'AVG TIME: %.3f   FPS: %.2f' % subst


Esta clase la crearemos dentro de un archivo llamado fpsclock.py dentro de la carpeta de gamemanager.

Ahora solo nos quedará añadir el siguiente código en el archivo juego.py.

import pygame
from pygame.locals import *
 
from gamemanager.gamemanager import GameManager
from gamemanager.states import menustate
from gamemanager.fpsclock import *
 
if __name__ == "__main__":
 
    game = GameManager('Estados Juego Python',(320,200),False)
    game.changeState(menustate.MenuState(game))
    fps = FpsClock(35,0)
 
    while game.running:
        game.handleEvents(pygame.event.get())
        game.update()
        game.draw()
        fps.tick()
 
    game.cleanUp()


Con la instruccion fps = FpsClock(35,0) establecemos una tasa de FPS de 35.

El metodo fps.tick() espera a que se cumpla la tasa de FPS establecida.

martes, 15 de febrero de 2011

Estados del juego

Un juego se divide en distintos estados por los que puede pasar.
Por ejemplo:
  • Menú Principal
  • Opciones del Juego
  • Créditos
  • Pantalla del juego
  • etc ...
Todos estos estados comparten unos comportamientos como son:
  • Captura de eventos (teclado, ratón, etc..)
  • Dibujar (mostrar las opciones del menú, mostrar la pantalla del juego ...)
  • Actualizar estados (cambiar la opcion del menu seleccionada, actualizar la posicion del jugador ...)
  • Y otros como pueden ser, ¿que pasa cuando se inicializa el estado?, ¿que pasa cuando se finaliza?, ¿que pasa si se hace un pause?
Para controlar todos estos estados de una forma muy sencilla, existe un método que descubrí hace muchos años que es el siguiente.

Tendremos una clase llamada GameManager que controlará el paso de un estado a otro y sus eventos, y una clase abstracta llamada GameState, de la que heredarán todos los estados por los que pueda pasar nuestro juego.

Para que veais como funciona todo esto, vamos a hacer un pequeño ejemplo que luego nos servirá para nuestro juego.

Necesitamos crear inicialmente un fichero llamada juego.py y dos carpetas una llamada gamemanager y otra dentro de esta llamada states.

Dentro de la carpeta "states" crearemos un archivo llamado gamestate.py con el siguiente codigo:


class GameState(object):

    def __init__(self,parent):
        pass

    def start(self):
        pass

    def cleanUp(self):
        pass

    def pause(self):
        pass

    def resume(self):
        pass

    def handleEvents(self, events, parent):
        pass

    def update(self):
        pass

    def draw(self):
        pass

sábado, 5 de febrero de 2011

Requisitos mínimos

Para poder empezar a programar el juego necesitamos:

  • Tener instalada la versión de Python 2.X. Podeis instalarla desde linux (versiones basadas en Debian, como Ubuntu) mediante el comando "sudo apt-get install python", o descargarla desde la pagina oficial de Python .
  • Instalar las librerias de Pygame. Para ello ejecutamos "sudo apt-get install python-pygame" o las descargamos desde la pagina oficial de Pygame.
  • Por último tener algún entorno de desarrollo como Netbeans (multiplataforma) o cualquier editor de textos decentes como puede ser Gedit (linux) o Notepad++ (Windows).
Con esto ya tenemos todo lo necesario para empezar a trabajar.

miércoles, 2 de febrero de 2011

Introducción

Hola a todos.
Con este tutorial pretendo mostraros los pasos que he ido siguiendo para la creación de una aventura gráfica 2D con Pygame.

Cualquier parte del código está sujeta a mejoras y comentarios por vuestra parte.

Espero que entre todos consigamos hacer algo que nos divierte y que entretenga a los demás.