Desarrollando videojuegos con cocos2d-html5 (5): Implementando los controles

cocos2d-tutorial-5

Implementando los controles

En esta parte del tutorial veremos las tres formas básicas de controlar un juego. Veremos el código que hay que agregar para que un juego soporte el control del mouse (para navegadores de escritorio), para control con touch (para navegadores móviles, incluyendo multitouch), y soporte para el teclado.

En la sección anterior vimos cómo usar un tipo muy específico de nodo, el botón, que tiene una interacción limitada. A veces necesitamos hacer más con la entrada del jugador, y eso es lo que haremos hoy.

Cómo funciona la inclusión de controles

Para incluir estas funcionalidades basta con definir funciones dentro del sprite con los nombres correspondientes. Veremos que en el código que los Sprites y Layers son el resultado de llamar a una función llamada extend() de su superclase. Esta es la forma como se hace la herencia de clases, y está basada en el Simple JavaScript Inheritance de John Resig. Lo que tienes que saber es que debes definir tus variables o funciones dentro del gran objeto que se le pasa a la función extend().

Control con el mouse

Tanto los sprites como los layers tienen la capacidad de recibir los eventos provenientes de hacer click con el mouse. Cuando el jugador presiona un botón, se hace la llamada a la función onMouseDown() correspondiente al sprite. Cuando el jugador mueve el mouse se llama a la función onMouseMoved() y cuando deja de presionar un botón, a la función onMouseUp(). Cada una de estas funciones acepta un parámetro, que es el objeto del evento con todas las propiedades que describen lo que pasó.

Veamos un sencillo ejemplo donde imprimimos todas estas acciones a la consola. Podemos usar el proyecto de la parte anterior, después de cerrar la definición del init, incluyamos una coma y definamos estas nuevas funciones.

//myApp.js, función init()
        lazyLayer.addChild(this.sprite, 0);

        return true;
    },

    onMouseDown: function (event) {
        this.logClick(event, "mouse", "comenzó");
    },

    onMouseUp: function (event) {
        this.logClick(event, "mouse", "terminó");
    },

    onMouseMoved: function (event) {
        this.logClick(event, "mouse", "se movió");
    },

    logClick: function (event, tipo, txt) {
        var b;
        if (event.button == cc.MOUSE_RIGHTBUTTON) {
            b = "derecho";
        } else if (event.button == cc.MOUSE_LEFTBUTTON) {
            b = "izquierdo";
        } else {
            b = "otro";
        }
        cc.log("Evento del "+tipo+" "+txt+", boton " + b + " posición: (" + event.getLocation().x + ", " + event.getLocation().y+")");
    }

Necesitamos también indicarle a nuestra capa que debe aceptar los eventos del mouse, cosa que agregaremos al final de la función init().

//myApp.js, función init(), comenzando por la misma línea del código anterior
        lazyLayer.addChild(this.sprite, 0);

        if ('mouse' in sys.capabilities) {
            this.setMouseEnabled(true);
        }

        return true;
    },

Una vez hecho eso, accedemos al juego y veremos la impresión en la consola.

cocos2d-html5-5-image1

Si quieres ver todas las funciones disponibles para touch, revisa el archivo cocos2d/touch_dispatcher/CCMouseDispatcher.js .

Control con el touch

El touch es el método de entrada estándar para muchos smartphones y dispositivos móviles. Visualmente , la entrada con el touch no tiene la presencia de un cursor, aunque se comporta de manera similar al mouse. La diferencia fundamental del touch es que el evento incluye un identificador del touch, lo que permite manejar varios toques simultáneamente. El manejo es similar que con el mouse, pero tenemos un primer argumento en las funciones, touches, que es un arreglo de los toques que se estén haciendo en el momento.

Agreguemos las funciones correspondientes a la detección de touch en el proyecto.

//myApp.js, en cualquier lado que no sea la definición de una función, dentro de cc.Layer.extend({...})
    onTouchesBegin: function (touches, event) {
        this.logClick(touches[0], "touch", "comenzó");
    }

    onTouchesEnded: function (touches, event) {
        this.logClick(touches[0], "touch", "terminó");
    },

    onTouchesMoved: function (touches, event) {
        this.logClick(touches[0], "touch", "se movió");
    },

Fíjense que para la función que hemos escrito, logClick, le pasamos la posición 0 de touches que corresponde al primer toque de la superficie. Tenemos también que indicarle a la capa que acepte touches, en init():

//myApp.js, función init()
        if ('mouse' in sys.capabilities) {
            this.setMouseEnabled(true);
        }
        if ('touches' in sys.capabilities) {
            this.setTouchEnabled(true);
        }

Probar el touch con la consola es más dificil. Al momento de escribir este tutorial la única manera de tener una consola en un dispositivo móvil es con Safari en iOS6 (si sabes depurar en otras plataformas móviles déjalo en los comentarios). Sin embargo, créeme en esta ocasión que esto funciona.

Si quieres ver todas las funciones disponibles para touch, revisa el archivo cocos2d/touch_dispatcher/CCTouchDelegateProtocol.js.

Control con el teclado

Como cocos2d-x es una librería orientada a las plataformas móviles, el soporte para teclado es muy pobre, a menos que se trate de un campo de texto. Sin embargo, Cocos2d-html5 incluye este método para el manejo con los navegadores de escritorio, y es muy fácil de incluir. Lo primero que hay que hacer es incluir la capacidad del teclado en la función init():

//myApp.js, función init()
        if ('keyboard' in sys.capabilities) {
            this.setKeyboardEnabled(true);
        }

Luego hay que incluir las funciones onKeyDown y onKeyUp, que aceptan como parámetro el número identificador de la tecla. Aquí vamos a emplear una técnica que se suele utilizar en los juegos de ejemplo que trae la librería, veamos las funciones:

//myApp.js, en cualquier lado que no sea la definición de una función, dentro de cc.Layer.extend({...})
    onKeyDown: function (e) {
        LG.KEYS[e] = true;
    },

    onKeyUp: function (e) {
        LG.KEYS[e] = false;
    },

El objeto LG que vemos referenciado aquí es una variable global que crearemos, y que contiene el objeto KEYS. Esto nos va a permitir poder comprobar en otra función si una tecla está presionada en el momento o no. Ahora, ¿cómo hacemos para crear este objeto? Lo más recomendable es crearlo al inicio del archivo, antes de crear la capa, de esta manera:

//myApp.js, al principio, primera línea después del comentario del copyright, fuera de la función cc.Layer.extend({...})
var LG = {
    KEYS: []
};

Ahora, necesitamos alguna función que nos permita comprobar en cada instante si una tecla está presionada. Para eso emplearemos la función update(), la cual hay que indicarle a la capa que vamos a utilizar. Agreguemos en init() después de activar el teclado:

//myApp.js, función init(), justo antes de return true;
    this.scheduleUpdate();

Esto te obliga a crear una función dentro de la capa que se llame update() y no acepte ningún parámetro:

//myApp.js, dentro de cc.Layer.extend({...})
    update: function() {
        for (var i in LG.KEYS) {
            if (LG.KEYS[i]) {
                cc.log(“Presionado el botón “+i);
            }
        }
    }

Cocos2d-html5 incluye unas constantes definidas en cc.KEY, ejemplo, cc.KEY.left corresponde a la flecha izquierda. Podrás ver todas las definiciones en cocos2d/keyboard_dispatcher/CCKeyboardDispatcher.js.

Una última técnica que podemos aplicar es agregar un objeto JUSTPRESSED a ese objeto LG (veremos que más adelante ese objeto nos será de gran utilidad). El objeto JUSTPRESSED necesita ser actualizado por esa función update; con estas dos cosas podemos determinar en cualquier momento del update cuándo ha sido presionada una tecla y hacer una y sólo una vez una acción que necesitemos hacer por cada vez que se presiona dicha tecla.

Cambiemos la definición de LG:

//myApp.js, al inicio del archivo, después del comentario del copyright
var LG = {
    KEYS: [],
    JUSTPRESSED: []
};

Modifiquemos también onKeyDown y onKeyUp:

//myApp.js, dentro de cc.Layer.extend({...})
    onKeyDown: function (e) {
        VT.KEYS[e] = true;
        VT.JUSTPRESSED[e] = true;
    },

    onKeyUp: function (e) {
        VT.KEYS[e] = false;
        VT.JUSTPRESSED[e] = false;
    },

Ahora, dentro de la función update():

//myApp.js, dentro de cc.Layer.extend({...})
    update: function() {
        for (var i in LG.KEYS) {
            if (LG.JUSTPRESSED[i]) {
                cc.log(“Presionado el botón “+i);
            }
            if (VT.KEYS[i] && VT.JUSTPRESSED[i]) {
                VT.JUSTPRESSED[i] = false;
            }
        }
    }

Notemos que en la versión anterior del código, cuando mantenemos presionado el botón, la cónsola imprimirá que estamos presionando la tecla toda vez que update compruebe que la tecla está siendo presionada en el momento. En esta nueva versión, JUSTPRESSED será para cada tecla verdadero en en la primera pasada de update en la que se haya presionado el botón. En las siguientes pasadas este valor será falso, aunque mantengamos la tecla presionada.

Concluyendo…

Hemos escrito código el día de hoy que nos permite controlar cuidadosamente la entrada del jugador. Esto lo podremos usar más adelante para tener buenos controles que respondan a tiempo ante el jugador.

Hemos también dado a ver rápidamente cómo tener una función que se llama en cada tick del juego, la función update() y su compañera scheduleUpdate(). Veremos en la siguiente parte cómo se manejan estas funciones dentro de cada escena, y cómo transicionar entre escenas.

Navegar la seriePost anterior en la seriePróximo post en la serie

8 comentarios en «Desarrollando videojuegos con cocos2d-html5 (5): Implementando los controles»

  1. La función update recibe el parámetro dt, aunque si no lo pones, sigue funcionando. Pero este parámetro puede llegar a ser muy importante.
    Y muchas gracias por el tutorial, que muy pocos de cocos2d-html5, y la mayoría están desactualizados.

  2. Buenos tutos, sigo esperando por si hay más, pero sabes, me parece un poco enredado cocos2d, yo he estado viendo otras librerias como impactjs, o melonjs, y no son ta complicadas, estaría bueno que también iniciaras un curso con melonjs, que por lo que veo esta muy buena, pero no tengo la capacidad para empezar a desarrollar por mi mismo, en cuanto a impactjs, sin duda es en la que he visto mejores resultados, pero es de pago, si pudieras hacer tutos de melon te lo agradecería muchisimo, saludos!

  3. No se si alguien logro hacer funcionar el teclado, no logro imprimir en la consola al presionar las teclas de dirección..;

    Otro comentario, supongo que donde dice; «Cambiemos la definición de LG»

    Debe decir BT en lugar de LG

    var VT = {
    KEYS: [],
    JUSTPRESSED: []
    };

Deja un comentario