Timer

Como indicaba Dani en la entrada anterior, la clase Timer no funcionaba como es debido tras la tarde del martes. Esa noche la terminé, utilizando para ello las librerías de python en vez de las de pygame.

Además, añadí la posibilidad de parar y reanudar el reloj, para que en el futuro se pueda implementar la pausa.

Es un código bastante sencillo, aquí lo tenéis:

# $Id: Timer.py 14 2007-11-01 21:07:45Z penyaskito $

import pygame
import time
import ResourceLoader

class Timer(pygame.sprite.Sprite):

    TIME = 60
    
    ESTADO_PARADO = 0
    ESTADO_CORRIENDO = 1
    
    def __init__(self, screen):
        self._posicion = (screen.get_width() / 2 - 20, 10)
        self._screen = screen
        self._inicial = time.time()
        self._estado = self.ESTADO_CORRIENDO
        self._seconds = self.TIME
        self._font = pygame.font.SysFont("Verdana",40)
        
    def update(self, t): 
        s = self._font.render(str("%(var)02d" % {"var":self._seconds}),True,(255,230,50))

        self._screen.blit (s, self._posicion)        
        if self._estado == self.ESTADO_CORRIENDO:
            self._seconds = self.TIME - (time.time() - self._inicial)
        if self._seconds < 0:
            self._seconds = 0
            self._estado = self.ESTADO_PARADO
    
    def get_seconds(self):
        return self._seconds
        
    def pause(self):
        self._estado = self.ESTADO_PARADO
        # almacenamos el instante en el que fue parado.
        self._stopped = time.time()
        
    def resume(self):
        # calcula por donde debe seguir la cuenta.
        self._inicial -= self._stopped - time.time()
        self._estado = self.ESTADO_CORRIENDO

No me acaba de convencer que la clase responsable de llevar el tiempo sea la responsable de pintarlo, pero como decía Dani, ya iremos refinando en el futuro. Recuerda: Commit early. Commit often.

Próximamente, veremos como usar un sistema de eventos en python, de manera que se pueda reducir el acoplamiento entre algunas clases.

Accesos a disco: ResourceLoader

Es mi primera entrada en este blog de desarrollo, así que lo primero que debería hacer es presentarme. Soy Penyaskito, y junto a Danigm y otros colegas de Sugus vamos a desarrollar este juego. Es una experiencia nueva, y espero que sea muy divertida y didáctica. Al contrario que mis compañeros, yo usaré Windows para el desarrollo, de manera que la compatibilidad estará garantizada.

Nunca había usado pygame antes, así que uno de los primeros pasos es aprender como funciona esta plataforma. Para ello, Dani preparó un taller en el que realizó un jueguecito de naves, que puedes descargar aquí: Shooter2D.

Una de las cosas que me llamó la atención al revisar el código era que cada objeto de la clase Nave cargaba sus propias imágenes. Pensé que quizá pygame se encargaría de controlar y gestionar los accesos a disco, así que decidí comprobarlo usando FileMon. Gracias a esta herramienta descubrí que no era así:

filemon-config.png

Configuración de la captura de FileMon

pygame-load.png

Captura del juego corriendo, con FileMon registrando los accesos al fichero de imagen de la nave enemiga.

Visto que no, es necesaria una clase que se encargue de gestionar los accesos a disco, de modo que los recursos que se accedan repetidamente sean cacheados en memoria. Para ello, desarrollé esta sencilla clase ResourceLoader:

import pygame
import sys

class ResourceLoader():
    '''
    Clase encargada de gestionar la carga de imagenes. 
    Cachea estas si es necesario.
    '''
    images = {}
        
    def load (self, uri, cache=False):
        '''
        Carga una imagen (uri). Permite el cacheo si cache=True.
        '''
        img = None
        if uri in self.images:
            img = ResourceLoader.images[uri]
        else:
            img = pygame.image.load(uri)
            if cache:
                ResourceLoader.images[uri] = img
        return img

A esta clase aún le falta un correcto manejo de excepciones, así como comprobaciones de que la uri pasada sea válida y se pueda utilizar como clave de un diccionario.

Tras adaptar la clase Nave para que utilicé el ResourceLoader, al lanzar el juego con FileMon obtenemos la siguiente captura:
resourceloader-load.png

Captura de FileMon registrando los accesos al fichero de imagen de la nave enemiga tras los cambios.

En la captura podemos ver que los accesos a disco se han reducido, ya que sólo se realizan la primera vez.

Esto ha sido todo por hoy. Pronto trabajaremos en las clases principales del juego, y tendremos a luchadores sudorosos brincando por la pantalla. ¡Hasta pronto!