Phaser con Brython (Tutorial)
01 - Inicio y configuración

Bienvenido al tutorial de Phaser con Brython. Como ya sabrás, Phaser es un framework javascript para crear videojuegos en dos dimensiones. No hay ningún problema a la hora de usar una librería/framework javascript con Brython, pero en este tutorial trataremos de hacerlo del modo mas pythonico posible, de manera que iremos escribiendo un módulo llamado bryphaser para usarlo en nuestros proyectos, de tal modo que podamos olvidarnos de que estamos usando javascript.

Lo primero que vamos a hacer es crear e iniciar un proyecto.



Crear e iniciar el proyecto

Tips:

El siguiente paso será editar los archivos del proyecto:



Archivo index.html

Este será el contenido del archivo:

<!DOCTYPE html>

<html>

<head>

    <meta charset="utf-8"/>
    <title>JUEGO PHASER-BRYTHON</title>

    <!-- CARGAMOS PHASER Y BRYTHON -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.min.js"></script>
    <!-- Ahora window.Phaser está disponible (lo usaremos en game.py) -->
    <script src="https://cdn.jsdelivr.net/gh/brython-dev/brython/www/src/brython.js"></script>
    <!-- Ahora brython está disponible (lo ejecutamos al cargarse body) -->

</head>

<body onload="brython();">

    <!-- CREAMOS UN DIV QUE SERÁ EL CONTENEDOR DEL JUEGO -->
    <div id="gamebox"></div>

    <!-- CARGAMOS EL SCRIPT DEL JUEGO (game.py). Empieza la fiesta :D -->
    <script type="text/python" src="game.py"></script>

</body>

</html>

Dentro de la etiqueta head hemos cargado Phaser y Brython. En body hemos creado un div con id="gamebox" que será el contenedor del juego. Además cargamos el script del juego (game.py). No olvides ejecutar brython() en respuesta al evento onload de body.



Módulo bryphaser.py

En este archivo vamos a definir una Clase Scene, que será una clase base sobre la que crearemos las escenas de nuestros juegos mediante programación orientada a objetos (OOP).

Lo primero que hay que hacer es importar a browser.window:

from browser import window

Ahora puedes acceder al objeto Phaser:

Phaser = window.Phaser

Aunque no es estrictamente necesario, lo que vamos a hacer es desarrollar videojuegos utilizando clases python. En Phaser, un juego se compone de una o mas escenas. Si el juego está formado por una sola escena podría prescindirse de la OOP, pero es mejor empezar a trabajar del modo mas escalable para que no sea tan difícil pasar, posteriormente, al desarrollo de juegos con varias escenas.

Los objetos de Phaser son tipos de datos de javascript, de manera que no se pueden crear clases python que hereden de estos tipos de datos, pero vamos a hacer lo que yo llamo "herencia fake".

A continuación definimos la clase base Scene:

class Scene:

def __init__(self, active=True):

    self.this = Phaser.Scene.new(self.__class__.__name__)
    self.this.active = active
    self.this.init = self.init
    self.this.preload = self.preload
    self.this.create = self.create
    self.this.update = self.update

def __getattr__(self, attr):

    if attr not in self.__dict__:
        return self.this[attr]

    def __setattr__(self, attr, value):

        self.__dict__[attr] = value
        self.this[attr] = value

    def __call__(self):
        return self.this

Con esta clase base crearemos escenas personalizadas mediante herencia. No voy a entrar en detalles sobre su funcionamiento, todo está documentado en Phaser Brython Pong!.

Dicho esto, el código del módulo bryphaser.py finalmente luce así:

# Módulo bryphaser
# Archivo: bryphaser.py
# Descripción: Funcionalidad común a todo juego desarrollado con Brython y Phaser

from browser import window

Phaser = window.Phaser

class Scene:
    def __init__(self, active=True):
        self.this = Phaser.Scene.new(self.__class__.__name__)
        self.this.active = active
        self.this.init = self.init
        self.this.preload = self.preload
        self.this.create = self.create
        self.this.update = self.update

    def __getattr__(self, attr):
        if attr not in self.__dict__:
            return self.this[attr]

    def __setattr__(self, attr, value):
        self.__dict__[attr] = value
        self.this[attr] = value

    def __call__(self):
        return self.this

A lo largo de este tutorial añadiré mas funcionalidad al módulo bryphaser.py para no tener que escribir constantemente la funcionalidad común y necesaria de todo juego desarrollado con Brython y Phaser.



Juego game.py

En este archivo lo primero será importar al módulo bryphaser:

from bryphaser import Phaser, Scene

Ahora vamos a crear una variable config que contiene la configuración del juego. La configuración puede definirse en un archivo json para procesarlo y asignar su contenido a la variable config, pero yo voy a utilizar diccionarios python:

config = dict(

    width=320, height=180,
    parent='gamebox', type=Phaser.AUTO,
    scene=[MainScene()()]

)

Si ahora mismo ejecutas mediante F5 en el navegador se producirá un error (se debe a que todavía no hemos definido a MainScene).

De momento y como puedes observar, hemos indicado el ancho width=320 y alto height=180 del lienzo del juego. Hemos indicado que el div con id="gamebox" es el padre/contenedor del juego parent='gamebox', y hemos indicado type=Phaser.AUTO (para que el juego se renderice en canvas o WebGL según el soporte del navegador).

type puede tomar los siguientes valores:

scene contiene la lista de escenas que componen el juego: scene=[MainScene()()]. Si solo hay una escena puede omitirse la lista y simplemente pasar la escena: scene=MainScene()().

Vamos a definir la escena del juego:

class MainScene(Scene):

    def __init__(self, active=True):
        Scene.__init__(self, active)

    def init(self, *args):
        print('init')

    def preload(self, *args):
        print('preload')

    def create(self, *args):
        print('create')

    def update(self, time, delta):
        print('update')

La clase MainScene hereda de bryphaser.Scene. Toda escena debe tener 4 métodos (init, preload, create y update). Estos 4 métodos se ejecutarán secuencialmente, siendo el último de ellos (update) el bucle que dará vida a la escena.

De momento he añadido un print a cada uno de los métodos de la escena para que compruebes que se ejecutan secuencialmente y que update es un bucle (se ejecuta de forma recursiva).

Ya solamente falta un paso mas para poder ejecutar el juego mediante F5 y comprobar que todo ha funcionado correctamente. Tenemos que crear un Phaser.Game con la configuración que previamente habíamos definido:

Phaser.Game.new(config)

El código de game.py queda finalmente así:

from bryphaser import Phaser, Scene


class MainScene(Scene):

    def __init__(self, active=True):
        Scene.__init__(self, active)

    def init(self, *args):
        print('init')

    def preload(self, *args):
        print('preload')

    def create(self, *args):
        print('create')

    def update(self, time, delta):
        print('update')



config = dict(

    width=320, height=180,
    parent='gamebox', type=Phaser.AUTO,
    scene=[MainScene()()]

)


Phaser.Game.new(config)

Presiona ahora F5 en el navegador web y presiona Ctrl + Mayús + J para abrir la consola. Comprobarás que se ha creado un lienzo de color negro en la página (que es nuestro juego, al que no le hemos añadido nada). En la consola observarás que se imprimen secuencialmente los textos init, preload, create y update. Observa que update se imprime recursivamente.

Hasta aquí la primera parte del tutorial de desarrollo de videojuegos con Phaser y Brython. Pronto escribiré el próximo capítulo! ^^