martes, 16 de agosto de 2011

JQuery games 2

Hola a todos!

Siguiendo el post JQuery games, donde se tratan los temas básicos para empezar con GameQuery, en este tutorial trataremos lo que yo creo que son los puntos básicos para hacer cualquier juego 2D simple o mínimamente complejo para una web:

- Añadir sprites dinámicamente a un group.
- Capturar eventos del mouse con Jquery.
- Mover todos los sprites de una misma clase css.
- Detectar colisiones entre sprites.
- Eliminar sprites de un group.

Para aprender estos puntos, realizaremos un pequeño minijuego en el que irán cayendo burbujas desde arriba y con el mouse podremos poner burbujas donde queramos.
Cuando una burbuja que caiga de arriba toque a alguna que hayamos añadido, ambas desapareceran.


Aquí tenéis los archivos necesarios para realizar el tutorial:
bluesky.png
burbuja.png

JQuery GAMES: Estructura del juego

Seguimos exactamente con la misma estructura que expliqué en el post JQuery Games, con un apartado nuevo, EVENTOS:

$(window).load(function(){

//DECLARACIÓN DE CONSTANTES
//DECLARACIÓN DE VARIABLES

//MAIN FUNCTION//
$(function(){
//CARGA DE IMÁGENES
//CONFIGURACION DEL PLAYGROUND
//INICIALIZACIÓN DE LAS CAPAS DEL PLAYGROUND
//DEFINICIÓN DE LAS CALLBACK FUNCTIONS
//EVENTOS
//INICIAR EL JUEGO : $.playground().startGame();
});
});

JQuery GAMES: Imágenes y Playground

Las variables que almacenaran los sprites de imágenes son:

var background1;
var burbuja = new Array();

Carga de las imágenes:

background1 = new $.gameQuery.Animation({imageURL: "bluesky.png"});
burbuja["normal"] = new $.gameQuery.Animation({imageURL: "burbuja.png"});

La estructura del playground estará compuesta por 3 groups o capas, una capa para el background, una para las burbujas que genera el juego dinámicamente y otra para las burbujas que genera el usuario clicando en la pantalla.

$.playground()
.addGroup("background", {width: PLAYGROUND_WIDTH,
height: PLAYGROUND_HEIGHT})
.addSprite("background1", {animation: background1,
width: PLAYGROUND_WIDTH,
height: PLAYGROUND_HEIGHT})
.end()
.addGroup("bubbleCPULayer", {width: PLAYGROUND_WIDTH,
height: PLAYGROUND_HEIGHT})
.end()
.addGroup("bubblePlayerLayer", {width: PLAYGROUND_WIDTH,
height: PLAYGROUND_HEIGHT});

JQuery GAMES: generar dinámicamente sprites

Para crear dinámicamente sprites cada cierto tiempo, vamos a crear una callback function que se ejecute cada 1000 milisegundos.

/**CALLBACK BUBBLE GENERATOR**/
$.playground().registerCallback(function(){
var name = "bubbleCPU"+Math.ceil(Math.random()*1000);
$("#bubbleCPULayer").addSprite(name, {animation: burbuja["normal"], posx: Math.random()*PLAYGROUND_WIDTH, posy: 0, width: 33, height: 33 });
$("#"+name).addClass("bubbleCPU");
}, 1000);

Primero creamos el nombre que tendrá la nueva burbuja, el nuevo sprite que añadiremos:
var name = "bubbleCPU"+Math.ceil(Math.random()*1000);

Seguidamente añadimos un nuevo sprite al group "bubbleCPULayer" del playground, esto lo hacemos mediante un selector Jquery por id:
$("#bubbleCPULayer").addSprite(name, {.. atributos ..});

Atributos:
animation - imagen que vamos a cargar
posx - posición x donde aparecerá el sprite, es relativa al group en el que se añade
posy - posición y donde aparecerá el sprite, es relativa al group en el que se añade
width - ancho de la imagen que se mostrará
height - alto de la imagen que se mostrará

Esto es IMPORTANTÍSIMO:
Añadimos una clase css a la burbuja que hemos creado dinámicamente:

$("#"+name).addClass("bubbleCPU");

Sin este paso, sin añadir la clase no podremos recoger fácilmente el grupo de todas las burbujas que añadamos dinámicamente, a no ser que vayamos apuntando las id's (algo engorroso si pensamos en que se irán creando y destruyendo).

JQuery GAMES: capturar eventos del mouse

Los eventos del mouse los capturamos mediante una función estándar de JQuery.
Como queremos que cuando cliquemos en la pantalla, se genere dinámicamente una nueva burbuja en la capa o group "bubblePlayerLayer", emplearemos la función click:

/**EVENTOS**/
$("#playground").click(function(e){
mousex = e.pageX;
mousey = e.pageY;
var newx = mousex-20;
var newy = mousey-20;
var name = "bubble_"+Math.ceil(Math.random()*1000);
$("#bubblePlayerLayer").addSprite(name, {animation: burbuja["normal"], posx: newx, posy: newy, width: 33, height: 33 });
$("#"+name).addClass("bubblePlayer");
});

e.pageX - Indica la posición del eje x relativa al div que dispara el evento.
e.pageY - Indica la posición del eje y relativa al div que dispara el evento.

Cuando hemos recogido las coordenadas x e y del mouse, restamos o sumamos los píxels de desplazamiento del sprite, para que quede centrado en el punto donde clicamos. Y seguidamente añadimos un nuevo sprite dinámicamente, de la misma forma que en el punto anterior, pero indicando la capa "bubblePlayer".

JQuery GAMES: mover todos los sprites de una misma clase css

Ahora por cada segundo que pasa se está creando una nueva burbuja en la parte superior del playground, y si clicamos sobre el playground se crean burbujas donde indicamos con el mouse.
Para hacer que las burbujas vayan cayendo tenemos que crear una nueva callback function que gestione lo siguiente:

"Por cada sprite de la clase css 'bubbleCPU' incrementa su posición y"

Esta callback function queda de la siguiente forma:

$.playground().registerCallback(function(){
$(".bubbleCPU").each(function(){
var posy = parseInt($(this).css("top"))+5;
$(this).css("top",""+posy+"px");
});
}, REFRESH_RATE);

Por cada sprite de la clase css 'bubbleCPU':
$(".bubbleCPU").each(function(){.........});

incrementa su posición y:
var posy = parseInt($(this).css("top"))+5;
$(this).css("top",""+posy+"px");

JQuery GAMES: detectar colisiones entre sprites

Para detectar las colisiones entre dos sprites, emplearemos la función "collision()" de gameQuery.
Esta función retorna la lista de elementos que han colisionado con el elemento que invoca la función:
Como parámetro podemos añadirle un filtro, identificador de elemento, clase css de elemento con el que queremos ver si ha colisionado:
Por ejemplo:
var collided = $("#elementoA").collision("#elementoB,.group");

Aquí vemos si un elemento con id "elementoA" ha colisionado con un elemento con id "elementoB", para ello comprobaremos que la variable collided <-- (recuerdo que es una lista), tiene más de 0 elementos:

if(collided.length > 0)

Bien, ahora vamos a seguir con nuestro ejemplo, veamos como detectamos que sprites de la clase "bubbleCPU" ha colisionado con un sprite de la clase "bubblePlayer":

Para ello modificamos la callback function que controla el movimiento de caída de las burbujas:

$.playground().registerCallback(function(){
$(".bubbleCPU").each(function(){
var posy = parseInt($(this).css("top"))+5;
$(this).css("top",""+posy+"px");
if(posy > PLAYGROUND_HEIGHT){
$(this).remove();
return;
}
var idBubbleCPU = $(this).attr('id');
var collided = 0;
$(".bubblePlayer").each(function(){
collided = $(this).collision("#"+idBubbleCPU+",.group");
if(collided.length > 0){
$(this).remove();
}
});

if(collided.length > 0){
$(this).remove();
}
});
}, REFRESH_RATE);


Lo primero que hacemos es crear una "colisión" con el suelo, es decir, que las burbujas desaparezcan al llegar al suelo:

if(posy > PLAYGROUND_HEIGHT){
$(this).remove();
return;
}

remove() elimina el sprite del playground y de todo el DOM, así ya no lo procesaremos más.

Seguidamente para crear la colisión entre objetos de dos clases css, tenemos que tener dos .each anidados, lo que queremos decir es:
"por cada objeto de tipo A mira si ha colisionado con algún elemento de tipo B"

$(".bubbleCPU").each(function(){
...
var idBubbleCPU = $(this).attr('id');
var collided = 0;
$(".bubblePlayer").each(function(){
collided = $(this).collision("#"+idBubbleCPU+",.group");
if(collided.length > 0){
$(this).remove();
}
});
if(collided.length > 0){
$(this).remove();
}
});

Como vemos, dentro del segundo each(), para ver si alguna burbuja creada por el usuario ha colisionado con la burbuja que estamos tratando en la iteracción actual, empleamos la idBubbleCPU, que hemos recogido antes de entrar en el .each() mediante:
var idBubbleCPU = $(this).attr('id');

Si la variable collided es mayor que cero, se ha producido una colisión, y debemos eliminar ambas burbujas!!

Con esto termina el tutorial 2!

Aquí tenéis el link al ejemplo funcionando!
Bubbles!!

10 comentarios:

  1. Podría poner el ejemplo de este tutorial?
    Gracias

    ResponderEliminar
  2. Ola Cagmaster!
    Sí, mañana agrego el link con el ejemplo!
    Un saludo!

    ResponderEliminar
  3. He puesto el link al final del tutorial, ahora podéis verlo en funcionamiento!
    Como veréis el código es bastante simple!

    ResponderEliminar
  4. Muchas gracias por estos tutoriales, me son de gran utilidad. ¿Tienes pensado sacar mas? Saludos!!

    ResponderEliminar
  5. Me alegro que sean útiles!
    Pronto sacaré otro tutorial para tratar sprites animados.
    Últimamente estoy un poco liado con desarrollos en android, en cuanto encuentre un hueco acabo el tutorial y lo subo.
    Un saludo!

    ResponderEliminar
  6. Muchas gracias iwokloco.

    Como veo que haces desarrollos para Android me gustaria que me dieras tu opinion: Estoy realizando un mini-juego en Android usando PhoneGap(accelerometro)+Html5(Canvas)+Javascript. Para hacerlo con Game Query, en lugar de usar el canvas usaria el DOM...¿Sabes si esto tendria un mejor o peor rendimiento, has usado los dos sistemas? El problema es que no me quiero usar Flash (por ser un sistema propietario), pero la potencia que da este no sé si puede suplirse con el Canvas o con Game Query. ¿Que opinas?

    Gracias.

    ResponderEliminar
  7. Hola Afly!
    Realmente el canvas es mucho más potente que emplear el GameQuery y el DOM.
    El único problema que puedes encontrar al usar HTML5 es que no está implementado en todos los navegadores para Android.
    Para juegos simples no se apreciaría la diferencia, pero si por ejemplo aumentamos el número de gráficos y transformaciones la diferencia puede ser impresionante.
    Igualmente si solamente vas a desarrollar juegos para android y van a ser 2D, si sabes Java te recomiendo la librería AndEngine y el tutorial:
    http://knol.google.com/k/juan-de-dios-maldonado-s%C3%A1nchez/gu%C3%ADa-tutorial-para-programar-juegos-2d/yf7xkfmg7vie/3#
    Esta sería la opción con un rendimiento superior, pero perderás la portabilidad que te da programar en html5.

    Espero haberte ayudado un poco!
    Un saludete!

    ResponderEliminar
  8. muchisimas gracias iwokloco!!

    No conocia esa libreria y tutorial (directa para favoritos). Creo que de momento me decantaré por la portabilidad. El otro dia estuve trasteando con Phonegap build y para hacer una apps para todas las plataformas es realmente facil y el ahorro en tiempo es bestial. (La unica pega es que te pueden sacar el codigo fuente)

    Saludos!!

    ResponderEliminar
  9. y el ejemplo?? podrias ponerlo por favor? o el codigo fuente en todo caso. gracias

    ResponderEliminar
  10. Buenas Santiago!
    El enlace por lo visto estaba roto, he agregado el ejemplo en esta dirección del blog:
    http://iwokloco-appweb.blogspot.com.es/p/window.html

    El código no es del todo compatible con últimas versiones de Jquery, por eso verás que se utiliza jquery-1.4.2.js, básicamente son las funciones que modifican css, son independientes de GameQuery, si trabajas con un jquery más actual deberías cambiar esas funciones en el código de ejemplo.

    Un saludo!!

    ResponderEliminar