Desarrollando videojuegos con cocos2d-html5 (6): Entre escenas se entienden

Este artículo es la parte 6 de 6 de la serie Desarrollando videojuegos con cocos2d-html5

cocos2d-tutorial-6

Entre escenas se entienden

En esta parte veremos cómo manejar las funciones de schedule de una escena para llamar a funciones con intervalos regulares de tiempo, cómo hacer varias escenas para agrupar funcionalidades y cómo hacer una transición entre una escena y otra.

Las escenas en cocos2d encapsulan el comportamiento de un juego. De esta manera, puedes tener una escena para el menú inicial, otra escena para el juego como tal, otra escena para los créditos, y así. En proyectos más complejos podrías incluso tener más de una escena para tu juego, por ejemplo, una escena de inventario.

Qué son las escenas

Las escenas en cocos2d ayudan a separar las funcionalidades que puedes tener en un juego. Por ejemplo, en un proyecto sencillo, tendrías una escena para mostrar un menú inicial, tendrías otra escena para implementar el juego, y tendrías otra para mostrar las mejores puntuaciones.

Una de las ventajas que ofrece cocos2d es el manejo automático de memoria, por lo que las escenas de cocos2d no solamente ayudan a organizar el código, también facilitan la utilización de recursos de la computadora.

El manejo de la memoria funciona así: cuando creas una escena es necesario tener que crear todos sus componentes: sprites, nodos, textos, etc. y agregarlos a la misma. Cuando cambias de escena, no necesitas tener que destruir esos componentes explícitamente porque las referencias que hace esa escena saliente también son eliminadas.

Ya en pasadas entregas hemos creados escenas a partir de los componentes básicos de cocos2d, por lo que vamos a ver a continuación cómo cambiar de una escena a otra.

Vamos a escribir dos escenas, una de un título, y otra con «el juego». Esta parte utiliza los elementos prestados de la sección 4 de este tutorial.

//myApp.js
var MyLayer = cc.Layer.extend({
    init:function () {
        //////////////////////////////
        // 1. super init first
        this._super();
        var size = cc.Director.getInstance().getWinSize();
        var closeItem = cc.MenuItemImage.create(
            s_buttonNormal,
            s_buttonSelected,
            function () {
                cc.Director.getInstance().replaceScene(new GameScene());
            }, this);
        closeItem.setAnchorPoint(cc.p(0.5, 0.5));
        closeItem.setPosition(cc.p(size.width/2, 40));
        var menu = cc.Menu.create(closeItem, null);
        menu.setPosition(cc.PointZero());
        this.addChild(menu, 1);
        // create new label
        this.helloLabel = cc.LabelTTF.create("El Titulo", "Arial", 38);
        // position the label on the center of the screen
        this.helloLabel.setPosition(cc.p(size.width / 2, size.height - 40));
        // add the label as a child to this layer
        this.addChild(this.helloLabel, 5);
        return true;
    },
});
var MyScene = cc.Scene.extend({
    onEnter:function () {
        this._super();
        var layer = new MyLayer();
        this.addChild(layer);
        layer.init();
    }
});
//GameScene.js
//Recordemos que debemos incluir 'src/GameScene.js' en cocos2d.js
var GameLayer = cc.Layer.extend({
    init:function () {
        this._super();
        var size = cc.Director.getInstance().getWinSize();
        var closeItem = cc.MenuItemImage.create(
            s_buttonNormal,
            s_buttonSelected,
            function () {
                cc.Director.getInstance().replaceScene(new MyScene());
            }, this);
        closeItem.setAnchorPoint(cc.p(0.5, 0.5));
        closeItem.setPosition(cc.p(size.width/2, 40));
        var menu = cc.Menu.create(closeItem, null);
        menu.setPosition(cc.PointZero());
        this.addChild(menu, 1);
        // create new label
        this.helloLabel = cc.LabelTTF.create("El Juego", "Arial", 38);
        // position the label on the center of the screen
        this.helloLabel.setPosition(cc.p(size.width / 2, size.height - 40));
        // add the label as a child to this layer
        this.addChild(this.helloLabel, 5);
        return true;
    },
});
var GameScene = cc.Scene.extend({
    onEnter:function () {
        this._super();
        var layer = new GameLayer();
        this.addChild(layer);
        layer.init();
    }
});

Veamos las líneas que corresponden exactamente a la transición entre una escena y otra.

Estas líneas podemos agregarles algo más para que hayan transiciones suaves entre escenas. Antes de pasar a las transiciones veamos cómo podemos agregar funcionalidad extra a la escena que corra mientras esté activa.

Mientras tanto, al correr este código comprobaremos que se puede hacer un cambio entre una escena y otra.

escenas
Funciones schedule
Las funciones schedule permiten ejecutar código cada cierto tiempo dentro de una escena. El caso general es en cada etapa de redibujo de la escena, pero es perfectamente posible hacerlo para intervalos arbitrarios.

Veamos cómo hacer una función que imprima algo en consola:

//myApp.js
var MyLayer = cc.Layer.extend({
    init:function () {
        this._super();
        var size = cc.Director.getInstance().getWinSize();
        // create and initialize a label
        this.helloLabel = cc.LabelTTF.create("El Titulo", "Arial", 38);
        // position the label on the center of the screen
        this.helloLabel.setPosition(cc.p(size.width / 2, size.height - 40));
        // add the label as a child to this layer
        this.addChild(this.helloLabel, 5);
        this.scheduleUpdate();
        return true;
    },
    update:function (dt) {
        cc.log("Hola");
    },
});
var MyScene = cc.Scene.extend({
    onEnter:function () {
        this._super();
        var layer = new MyLayer();
        this.addChild(layer);
        layer.init();
    }
});

Fijémonos que en la función init() llamamos a this.scheduleUpdate(), que instruye a cocos2d a que llame en cada frame a la función update que definimos a continuación. La función update tiene un parámetro llamado dt, que es un número real que indica la diferencia de tiempo desde la última llamada a update.

Veamos cómo hacer una función que mueva un objeto en el centro de la pantalla en círculos:

//myApp.js
var MyLayer = cc.Layer.extend({
    init:function () {
        this._super();
        var size = cc.Director.getInstance().getWinSize();
        this.elObjeto = cc.Sprite.create(s_buttonNormal);
        this.elObjeto.setAnchorPoint(cc.p(0.5, 0.5));
        this.elObjeto.setPosition(cc.p(size.width / 2.0 + 60, size.height / 2.0));
        this.addChild(this.elObjeto);
        this.rotacion = 0;
        // create new label
        this.helloLabel = cc.LabelTTF.create("El Titulo", "Arial", 38);
        // position the label on the center of the screen
        this.helloLabel.setPosition(cc.p(size.width / 2, size.height - 40));
        // add the label as a child to this layer
        this.addChild(this.helloLabel, 5);
        this.scheduleUpdate();
        return true;
    },
    update:function (dt) {
        var size = cc.Director.getInstance().getWinSize();
        //posicionar nuevamente elObjeto de acuerdo a una simple regla de trigonometría
        this.elObjeto.setPosition(cc.p(size.width / 2.0 + 60*Math.cos(this.rotacion), size.height / 2.0 - 60*Math.sin(this.rotacion)));
        this.rotacion += 5*Math.PI/180;
    },
});
var MyScene = cc.Scene.extend({
    onEnter:function () {
        this._super();
        var layer = new MyLayer();
        this.addChild(layer);
        layer.init();
    }
});

Transiciones

Para cerrar esta parte del tutorial, veremos cómo hacer esas transiciones suaves entre una escena y otra.

Para lograr esto, simplemente tenemos que crear la transición que necesitemos, pasándole la escena final a la que queramos llegar. Veamos un ejemplo revisando la función que llama al botón que definimos al inicio de la sección:

        var closeItem = cc.MenuItemImage.create(
            s_buttonNormal,
            s_buttonSelected,
            function () {
                var scene = cc.TransitionFade.create(1.2, new GameScene(), cc.c3b(0,0,0));
                cc.Director.getInstance().replaceScene(scene);
            }, this);

En este caso, estamos definiendo una transición que dura 1.2 segundos, que llega a GameScene, y que hace primero un fade al color negro y luego a la escena. Todas las transiciones se construyen de manera similar, especificando como mínimo el tiempo que toma la transición y a dónde lleva la escena. El resultado de esa llamada se la pasamos al director.

Todas las transiciones se exploran en la suite de pruebas de cocos2d-html5, por lo prefiero referirlos a ese código para saber cómo se hacen. Los tipos de transiciones que existen a la fecha son:

  • JumpZoom: la escena se aleja y salta fuera de la pantalla.
  • TransitionProgressRadial: su versión horaria (TransitionProgressRadialCW) y antihoraria (TransitionProgressCCW) que definen el sentido del barrido rotatorio a la nueva escena.
  • TransitionProgressHorizontal, TransitionProgressVertical
  • TransitionProgressInOut, TransitionProgressOutIn
  • FadeTransition, FadeWhiteTransition
  • ShrinkGrowTransition
  • RotoZoomTransition
  • MoveIn*Transition – L, R, T, B definen la dirección desde donde entra la escena.
  • SlideIn*Transition – L, R, T, B definen la dirección desde donde entra la escena, empujando a la anterior.

En la siguiente parte

En la siguiente parte de este tutorial veremos más a fondo los sprites, los layers, las partículas y los tiles, que son componentes básicos en la construcción de juegos. Nos veremos hasta entonces.

Navegar la seriePost anterior en la serie

3 comentarios en «Desarrollando videojuegos con cocos2d-html5 (6): Entre escenas se entienden»

Deja un comentario