El Chigüire Literario Desarrollo de videojuegos con denominación de origen

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.

Un currículum para C y C++ de bajo nivel en #AltDevBlogADay

#AltDevBlogADay es uno de los mejores blogs de desarrollo/programación de videojuegos que existen y debería formar parte de tu lectura habitual. Actualmente están publicando una serie de artículos que hablan sobre lo que deberías aprender para entender bien los fundamentos de implementación a bajo nivel de C y C++, más que enseñarte a ser programador de bajo nivel. Si eres capaz de entender estos conceptos, tendrás menos probabilidad de crear bugs y mayor facilidad en rastrearlos. Hasta ahora van doce artículos, así que tómate tu tiempo para leerlos, no son lecturas sencillas.

  1. Introducción
  2. Tipos de datos
  3. La pila
  4. Más sobre la pila
  5. Aún más sobre la pila
  6. Condicionales
  7. Más sobre condicionales
  8. Mirando ensamblador optimizado
  9. Loops
  10. Tipos definidos por el usuario
  11. Herencia
  12. Herencia múltiple

Unity básico ahora permite exportar gratuitamente a Android y iOS

Varias personas me han enviado los enlaces de Edgadget y Gamasutra sobre la noticia de esta mañana, que Unity ahora permitirá a los usuarios de la versión básica de su software exportar a estas dos grandes plataformas móviles. Anteriormente cada plataforma costaba 400$, por lo que representa una bajada de la barrera que muchos desarrolladores recibirán con agrado.

Las licencias Pro para estas plataformas siguen costando 1500$, y los usuarios de las licencias Pro de Unity las pueden comprar para exportar utilizando las características avanzadas de estos plugins.

Así que si no lo has probado, te recomiendo que descargues Unity y juegues un rato con él.

Segundo episodio de Menos charla, más juegos, con el equipo de Klaus

menoscharlamasjuegosEn esta segunda entrega de Menos charla, más juegos, Saúl y Robert entrevistan al equipo de La Cosa Entertainment, ganadores del concurso Square-Enix con su juego Klaus, para Android. Les recomiendo escucharlo para conocer más de ellos y su proceso de desarrollo.

Modern GPU

Nvidia ha publicado en su cuenta de Github un manual sobre programación con el GPU llamado Modern GPU. Una suerte de librería, libro de algoritmos, tutorial y guía de mejores prácticas para el uso de CUDA, el framework para trabajar programación de propósito general con el GPU.

Recordemos que Nvidia ha publicado gratuitamente el contenido de su libro GPU Gems 1 para aprender la clase de cosas que se pueden hacer con buena programación para la tarjeta gráfica.