Estos artículos son parte de la serie:
- Desarrollando videojuegos con cocos2d-html5 (1): ¡Bienvenidos a cocos2d!
- Desarrollando videojuegos con cocos2d-html5 (2): Instalando la librería
- Desarrollando videojuegos con cocos2d-html5 (3): Un vistazo al Hello World
- Desarrollando videojuegos con cocos2d-html5 (4): Comenzando un proyecto
- Desarrollando videojuegos con cocos2d-html5 (5): Implementando los controles
- Desarrollando videojuegos con cocos2d-html5 (6): Entre escenas se entienden
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.
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.
Hoy publiqué la 5ta parte del tutorial de cocos2d-html5 que estoy escribiendo – http://t.co/IzY42nuL1j
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.
ya va siendo hora del siguiente 🙂
Sí! Lamento mucho el retraso, pero no he abandonado esto 😉
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!
Un detalle adicional que hay que controlar aquí también, es que lamentablemente en este momento los eventos para obtener la posición del mouse y la de los touch reportan posiciones diferentes. Más data (en inglés): http://www.cocos2d-x.org/forums/19/topics/33865?r=37776
Hola Ciro!!. Queria saber para cuando va estar el proximo capitulo!! Lo vengo siguiendo y esta muy interesante. Saludos!!
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: []
};