Desarrollo de Videojuegos Android con Cocos2D

31
1 Desarrollo de Videojuegos Android con Cocos2D Autor: Jordán Pascual Espada Apuntes, practica Guiada. Introducción a Cocos2D ............................................................................................................ 2 1 Nuevo Proyecto Android ........................................................................................................ 3 2 Director , vista y escena ......................................................................................................... 5 3 Representación gráfica de elementos: CCSprite y CGSize ..................................................... 8 4 Movimiento de elementos: CCMoveTo, CCCallFuncN, CCSequence .................................. 10 5 Eventos Touch: ccTouchesMoved ....................................................................................... 13 6 Colisiones: CGRect ............................................................................................................... 15 7 Animaciones: CCSpriteFrame , CCAnimation, CCAction ...................................................... 18 8 Fondo.................................................................................................................................... 23 9 Sistemas de partículas: CCParticleSystem ........................................................................... 24 10 Condición y pantalla de perder: CCLabel , replaceScene, CCDelayTime. ........................... 25 11 Condición y pantalla de ganar. ........................................................................................... 28 12 Generación de niveles ........................................................................................................ 29 13 Efectos de sonido: .............................................................................................................. 30

description

Desarrollo de Videojuegos Android con Cocos2D Cocos 2D es un framework muy popular en el mundo del desarrollo de videojuegos móviles, especialmente en las plataformas iOs y Android. El proyecto Cocos 2D está formado por un conjunto de librerías que nos facilitan la implementación de la mayor parte de las funcionalidades básicas de un videojuego. • Bucle del Juego • Animaciones • Colisiones • Sistemas de partículas • Sonidos Una de las principales ventajas de Cocos 2D es que utilizando resulta fácil desarrollar videojuegos ya que oculta varios de los aspectos técnicos complejos de la plataforma.

Transcript of Desarrollo de Videojuegos Android con Cocos2D

Page 1: Desarrollo de Videojuegos Android con Cocos2D

1

Desarrollo de Videojuegos Android con Cocos2D

Autor: Jordán Pascual Espada

Apuntes, practica Guiada.

Introducción a Cocos2D ............................................................................................................ 2

1 Nuevo Proyecto Android ........................................................................................................ 3

2 Director , vista y escena ......................................................................................................... 5

3 Representación gráfica de elementos: CCSprite y CGSize ..................................................... 8

4 Movimiento de elementos: CCMoveTo, CCCallFuncN, CCSequence .................................. 10

5 Eventos Touch: ccTouchesMoved ....................................................................................... 13

6 Colisiones: CGRect ............................................................................................................... 15

7 Animaciones: CCSpriteFrame , CCAnimation, CCAction ...................................................... 18

8 Fondo .................................................................................................................................... 23

9 Sistemas de partículas: CCParticleSystem ........................................................................... 24

10 Condición y pantalla de perder: CCLabel , replaceScene, CCDelayTime. ........................... 25

11 Condición y pantalla de ganar. ........................................................................................... 28

12 Generación de niveles ........................................................................................................ 29

13 Efectos de sonido: .............................................................................................................. 30

Page 2: Desarrollo de Videojuegos Android con Cocos2D

2

Introducción a Cocos2D

Cocos 2D es un framework muy popular en el mundo del desarrollo de videojuegos móviles,

especialmente en las plataformas iOs y Android.

El proyecto Cocos 2D está formado por un conjunto de librerías que nos facilitan la

implementación de la mayor parte de las funcionalidades básicas de un videojuego.

Bucle del Juego

Animaciones

Colisiones

Sistemas de partículas

Sonidos

Una de las principales ventajas de Cocos 2D es que utilizando resulta fácil desarrollar

videojuegos ya que oculta varios de los aspectos técnicos complejos de la plataforma.

Descarga:

http://cocos2d-x.org/projects/cocos2d-x/wiki/Download

Page 3: Desarrollo de Videojuegos Android con Cocos2D

3

1 Nuevo Proyecto Android

Creamos un nuevo proyecto Android “Android Application project”

Seleccionamos todos los valores por defecto.

A continuación vamos a modificar la configuración de la aplicación. Abrimos el fichero

AndroidManifest.xml, en la pestaña Manifest colocamos un nuevo Manifest Extras, de tipo

Supports Screens.

Page 4: Desarrollo de Videojuegos Android con Cocos2D

4

Pulsamos sobre el elemento Suppots Screens para configurar la adaptación de la aplicación a

las diferentes resoluciones.

Abrimos el AndroidManifest.xml en modo XML pulsando sobre la última pestaña.

Añadimos un parámetro de configuración que obligara a ejecutar la actividad en modo

portrait.

A continuación vamos a copiar la librería del framework Cocos2D en nuestra aplicación,

arrastramos el fichero cocos2d.jar a la carpeta /libs/ de nuestro proyecto.

Page 5: Desarrollo de Videojuegos Android con Cocos2D

5

2 Director , vista y escena

Lo primero que tenemos que hacer es ir a la implementación de la clase MainActivity y crear

dos variables globales.

protected CCGLSurfaceView vista;

private CCScene scene;

A continuación vamos a modificar el contenido del método onCreate:

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,

WindowManager.LayoutParams.FLAG_FULLSCREEN);

getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,

WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

vista = new CCGLSurfaceView(this);

setContentView(vista);

}

Modificamos la ventana actual para que se muestre a pantalla completa.

Creamos una vista de tipo CCGLSurfaceView y la establecemos como vista de la actividad.

Ahora vamos a sobrescribir el método onStart() de la actividad, utilizamos el elemento

CCDirector y para establecer los parámetros básicos del Juego.

public void onStart()

{

super.onStart();

// Director

CCDirector.sharedDirector().attachInView(vista);

CCDirector.sharedDirector().setDeviceOrientation(

CCDirector.kCCDeviceOrientationPortrait);

CCDirector.sharedDirector().setDisplayFPS(true);

CCDirector.sharedDirector().setAnimationInterval(1.0f / 40.0f);

}

A continuación tenemos que asociar una escena CCScene al director, la escena define y

contiene los elementos del Juego.

Pero antes de asociar la escena tenemos que definirla. Creamos la clase java CapaJuego para

implementar una escena la clase ha de heredar de alguna de las clases de tipo Layer definidas

en Cocos2D. En este caso utilizaremos la CCColorLayer.

Page 6: Desarrollo de Videojuegos Android con Cocos2D

6

El eclipse nos sugerirá que añadamos un constructor, elegimos el primero de ellos.

Ahora implementaremos un método estático en la CapaJuego que genere una escena

(CCScene) y la retorne. Creamos la escena y le asociamos una instancia de CapaJuego:

public static CCScene scene()

{

CCScene scene = CCScene.node();

CCLayer layer = new CapaJuego(ccColor4B.ccc4(255, 255, 255, 255));

scene.addChild(layer);

return scene;

}

Ahora ya podemos añadir la escena (que está completamente vacía) al CCDirector.

@Override

public void onStart()

{

super.onStart();

CCDirector.sharedDirector().attachInView(vista);

CCDirector.sharedDirector().setDeviceOrientation(

CCDirector.kCCDeviceOrientationPortrait);

CCDirector.sharedDirector().setDisplayFPS(true);

CCDirector.sharedDirector().setAnimationInterval(1.0f / 40.0f);

scene = CapaJuego.scene();

CCDirector.sharedDirector().runWithScene(scene);

}

Con el objetivo de que el ciclo de vida de la actividad principal MainActivity se vea reflejado en

el CCDirector, sobrescribimos los métodos onPause, onResume, onStop

@Override

public void onPause()

{

super.onPause();

CCDirector.sharedDirector().pause();

}

@Override

public void onResume()

{

super.onResume();

CCDirector.sharedDirector().resume();

}

@Override

Page 7: Desarrollo de Videojuegos Android con Cocos2D

7

public void onStop()

{

super.onStop();

CCDirector.sharedDirector().end();

}

Page 8: Desarrollo de Videojuegos Android con Cocos2D

8

3 Representación gráfica de elementos: CCSprite y CGSize

Actualmente la escena está vacía, pero vamos a incluir un nuevo elemento, la “bola”.

Comenzamos importando todos los recursos gráficos que nos hemos descargado en la carpeta

assets del proyecto.

Para incluir un nuevo elemento en la capa lo declaramos inicializamos en el constructor de la

capa. Cocos 2D utiliza la clase CCSprite para representar elementos en pantalla.

A continuación vamos a:

1. Crear una variable Global de tipo CCSprite para la pelota.

2. Crear una variable Global de tipo CGSize para almacenar el tamaño de la pantalla del

dispositivo (Sera útil para posicionar los elementos).

3. Inicializar CCSprite utilizando la imagen bola.png

4. Asignarle una posición a la pelota, por ejemplo el punto medio de la pantalla.

5. Añadir la pelota a la escena

private CGSize winSize;

private CCSprite pelota;

protected CapaJuego(ccColor4B color) {

super(color);

winSize = CCDirector.sharedDirector().displaySize();

pelota = CCSprite.sprite("bola.png");

pelota.setPosition(CGPoint.ccp(winSize.width / 2.0f, winSize.height / 2.0f));

addChild(pelota);

}

Page 9: Desarrollo de Videojuegos Android con Cocos2D

9

Si ejecutamos la aplicación podremos ver la escena compuesta por la capa CapaJuego y la

pelota en el punto medio.

Page 10: Desarrollo de Videojuegos Android con Cocos2D

10

4 Movimiento de elementos: CCMoveTo, CCCallFuncN, CCSequence

En primer lugar vamos a declarar dos variables globales que contendrán la velocidad de la

pelota en el eje X e Y.

private int velocidadPelotaX = 10;

private int velocidadPelotaY = -10;

Para asociarle un movimiento a la pelota tenemos que declarar una acción de tipo CCMoveTo.

Las acciones se declaran especificando:

1) El tiempo que queremos que se tarde en realizar el movimiento

2) Hacía que destino queremos que se mueva el elemento

CCMoveTo movimientoBasico = CCMoveTo.action(

1 / 40f,

CGPoint.ccp(pelota.getPosition().x + velocidadPelotaX,

pelota.getPosition().y + velocidadPelotaY));

Podemos especificar que cuando el movimiento finalice se invoque de manera automática a

una función, para especificar el siguiente movimiento, en este caso cuando el movimiento

termine se invocara la función nuevoMovimientoPelota (la implementaremos más tarde).

CCCallFuncN movimientoFinzalizado = CCCallFuncN.action(this,

"nuevoMovimientPelota");

Después tenemos que hacer una asociación entre el movimiento y la invocación de la función.

CCSequence actions = CCSequence.actions(movimientoBasico,

movimientoFinzalizado);

El último paso consiste en asociar la secuencia al elemento pelota.

pelota.runAction(actions);

El aspecto final del constructor de la capa del juego será el siguiente:

protected CapaJuego(ccColor4B color) {

super(color);

winSize = CCDirector.sharedDirector().displaySize();

pelota = CCSprite.sprite("bola.png");

pelota.setPosition(CGPoint.ccp(winSize.width / 2.0f,

winSize.height / 2.0f));

addChild(pelota);

CCMoveTo movimientoBasico = CCMoveTo.action(

1 / 40f,

CGPoint.ccp(pelota.getPosition().x + velocidadPelotaX,

pelota.getPosition().y + velocidadPelotaY));

CCCallFuncN movimientoFinzalizado = CCCallFuncN.action(this,

"nuevoMovimientPelota");

CCSequence actions = CCSequence.actions(movimientoBasico,

movimientoFinzalizado);

pelota.runAction(actions);

}

Page 11: Desarrollo de Videojuegos Android con Cocos2D

11

Si ejecutamos la aplicación la pelota se moverá mínimamente.

Vamos a implementar el método nuevoMovimientoPelota para asociar un nuevo movimiento

a la pelota (una vez haya acabado de realizar el anterior). Repetimos el mismo patrón de

movimiento:

public void nuevoMovimientPelota(Object sender) {

float destinoPelotaX = pelota.getPosition().x + velocidadPelotaX;

float destinoPelotaY = pelota.getPosition().y + velocidadPelotaY;

CCMoveTo movimientoBasico = CCMoveTo.action(1 / 40f,

CGPoint.ccp(destinoPelotaX, destinoPelotaY));

CCCallFuncN movimientoFinzalizado = CCCallFuncN.action(this,

"nuevoMovimientPelota");

CCSequence actions = CCSequence.actions(movimientoBasico,

movimientoFinzalizado);

pelota.runAction(actions);

}

Vamos a hacer que cuando la pelota se salga fuera de la pantalla “rebote”, para ello tenemos

que comprobar si la pelota está fuera de los márgenes de la pantalla y si es así invertir la

aceleración.

public void nuevoMovimientPelota(Object sender) {

// Comprobar si rebota

if ( pelota.getPosition().x <= 0 || pelota.getPosition().x >= winSize.width ){

velocidadPelotaX = velocidadPelotaX *-1;

}

if ( pelota.getPosition().y <= 0 || pelota.getPosition().y >= winSize.height ){

velocidadPelotaY = velocidadPelotaY *-1;

}

float destinoPelotaX = pelota.getPosition().x + velocidadPelotaX;

float destinoPelotaY = pelota.getPosition().y + velocidadPelotaY;

// (Paso 1) Cuando se ha movido volverlo a mover

CCMoveTo movimientoBasico = CCMoveTo.action(1 / 40f,

CGPoint.ccp(destinoPelotaX, destinoPelotaY));

CCCallFuncN movimientoFinzalizado = CCCallFuncN.action(this,

"nuevoMovimientPelota");

CCSequence actions = CCSequence.actions(movimientoBasico,

movimientoFinzalizado);

pelota.runAction(actions);

}

Si ejecutamos ahora la aplicación la pelota rebotara por la pantalla, vamos a incluir una última

comprobación para que el destino de la pelota no pueda exceder los límites de la pantalla.

Antes de asignar posición comprobamos que las posiciones destino no están fuera de la

pantalla.

float destinoPelotaX = pelota.getPosition().x + velocidadPelotaX;

float destinoPelotaY = pelota.getPosition().y + velocidadPelotaY;

if (destinoPelotaX < 0){

destinoPelotaX = 0;

}

Page 12: Desarrollo de Videojuegos Android con Cocos2D

12

if (destinoPelotaX > winSize.width){

destinoPelotaX = winSize.width;

}

if (destinoPelotaY < 0){

destinoPelotaY = 0;

}

if (destinoPelotaY > winSize.height){

destinoPelotaY = winSize.height;

}

// (Paso 1) Cuando se ha movido volverlo a mover

CCMoveTo movimientoBasico = CCMoveTo.action(1 / 40f,

CGPoint.ccp(destinoPelotaX, destinoPelotaY));

CCCallFuncN movimientoFinzalizado = CCCallFuncN.action(this,

"nuevoMovimientPelota");

CCSequence actions = CCSequence.actions(movimientoBasico,

movimientoFinzalizado);

pelota.runAction(actions);

}

Page 13: Desarrollo de Videojuegos Android con Cocos2D

13

5 Eventos Touch: ccTouchesMoved

Pala

A continuación vamos a repetir el procedimiento que seguimos para incluir la pelota pero esta

vez para el elemento pala.

Colocaremos la pala en la parte baja de la pantalla centrada en el eje X.

protected CapaJuego(ccColor4B color) {

super(color);

winSize = CCDirector.sharedDirector().displaySize();

pelota = CCSprite.sprite("bola.png");

pelota.setPosition(CGPoint.ccp(winSize.width / 2.0f,

winSize.height / 2.0f));

addChild(pelota);

pala = CCSprite.sprite("barra2.png");

pala.setPosition(CGPoint.ccp(winSize.width/2.0f, 80f));

addChild(pala);

Vamos a hacer que la pala se mueva en el eje X hacia la posición del dedo del usuario, cuando

el usuario mueva el dedo la pala se moverá.

En la clase CapaJuego y sobrescribimos uno de los tres métodos que sirven para captar

eventos táctiles ccTouchesMoved.

1) Capturamos la posición del evento X del dedo del usuario, y lo comparamos con la

posición de la pala: la diferencia entre esas dos mediciones es la distancia que la

pala tiene que recorrer.

2) Para que la velocidad del movimiento sea uniforme establecemos que el tiempo

que tardara en recorrer esa distancia será la distancia / la velocidad.

3) Por el momento fijaremos la velocidad de la pala en 400.

4) Incluimos una función de CCCallFuncN aunque en este caso no la vallamos a

utilizar, cuando la pala llega a su destino no queremos que ocurra nada.

@Override

public boolean ccTouchesMoved(MotionEvent event) {

float distancia = Math.abs(pala.getPosition().x - event.getX());

CCMoveTo moverPalaHaciaPunto = CCMoveTo.action(distancia / 400,

CGPoint.ccp(event.getX(), pala.getPosition().y));

CCCallFuncN movimientoFinzalizado = CCCallFuncN.action(this,

"finalMovimientoPala");

CCSequence actions = CCSequence.actions(moverPalaHaciaPunto,

movimientoFinzalizado);

pala.runAction(actions);

return true;

}

Page 14: Desarrollo de Videojuegos Android con Cocos2D

14

Para que la capa soporte eventos táctiles debemos incluir la directriz

this.setIsTouchEnabled(true); en el constructor de CapaJuego.

protected CapaJuego(ccColor4B color) {

super(color);

this.setIsTouchEnabled(true);

winSize = CCDirector.sharedDirector().displaySize();

// Pelota

pelota = CCSprite.sprite("bola.png");

pelota.setPosition(CGPoint.ccp(winSize.width / 2.0f,

winSize.height / 2.0f));

addChild(pelota);

Si ejecutamos el proyecto y pulsamos sobre la pantalla la pala se moverá.

Page 15: Desarrollo de Videojuegos Android con Cocos2D

15

6 Colisiones: CGRect

Bloque

A continuación vamos a repetir el procedimiento que seguimos para incluir la pelota y la pala,

en esta ocasión incluiremos el elemento bloque.

// Bloque

bloque = CCSprite.sprite("cocodrilo_1.png");

bloque.setPosition(CGPoint.ccp(winSize.width/2.0f, winSize.height-20));

addChild(bloque);

Estableceremos dos tipos de colisiones

1) Si la pelota colisiona contra la pala rebotara

2) Si la pelota colisiona contra el bloque, lo destruirá.

Necesitamos un método que compruebe las colisiones entre los elementos. En la última línea

del constructor de CapaJuego declaramos que el método gameLogic (que implementaremos a

continuación) se ejecutara 40 veces por segundo.

pelota.runAction(actions);

this.schedule("gameLogic", 1/40f);

}

Implementamos el método gameLogic.

Cocos2D ofrece un mecanismo “sencillo” para comprobar colisiones entre rectángulos.

1) Definimos los rectángulos de colisión de los 3 elementos

2) Comprobamos si la pala y la pelota colisionan.

3) En caso de que colisionen invertimos la velocidad actual de la pelota en eje Y

4) Modificamos la velocidad de la pelota en el eje X en función de la parte de la pala en la

que haya impactado.

Como la diferencia absoluta es numero demasiado grande para la velocidad (la bola

iría demasiado rápido) vamos a dividir ese resultado entre dos.

5) Por ultimo le damos un pequeño empujón a la pelota para que salga de la intersección

con la pala (modificamos la posición de la pelota).

6) Eliminamos el antiguo movimiento de la pelota y forzamos a que se vuelva a mover,

ahora ya con sus nuevas aceleraciones.

3 – 5 = -2

Nueva velocidadX = -2

7 – 5 = 2

Nueva velocidadX = 2

Page 16: Desarrollo de Videojuegos Android con Cocos2D

16

public void gameLogic(float dt){

// Crear rectangulos de colision

CGRect areaPala = CGRect.make(

pala.getPosition().x - pala.getContentSize().width / 2.0f,

pala.getPosition().y - pala.getContentSize().height / 2.0f,

pala.getContentSize().width, pala.getContentSize().height);

CGRect areaPelota = CGRect.make(

pelota.getPosition().x - pelota.getContentSize().width / 2.0f,

pelota.getPosition().y - pelota.getContentSize().height / 2.0f,

pelota.getContentSize().width, pelota.getContentSize().height);

CGRect areaBloque = CGRect.make(

bloque.getPosition().x - bloque.getContentSize().width / 2.0f,

bloque.getPosition().y - bloque.getContentSize().height / 2.0f,

bloque.getContentSize().width, bloque.getContentSize().height);

if (CGRect.intersects(areaPala, areaPelota)){

velocidadPelotaX = (int) ((pelota.getPosition().x - pala.getPosition().x)/2);

velocidadPelotaY = velocidadPelotaY *-1;

// Empujon

pelota.setPosition(CGPoint.ccp(pelota.getPosition().x,

pala.getPosition().y + pala.getContentSize().height / 2

+ pelota.getContentSize().height / 2 + 1));

pelota.stopAllActions();

nuevoMovimientPelota(pelota);

}

}

Si el la pelota colisiona con el bloque eliminamos el bloque de la escena.

if (CGRect.intersects(areaPelota, areaBloque)){

removeChild(bloque, true);

}

Lista de Bloques

En lugar de un bloque vamos a hacer que la escena tenga varios, declaramos una lista de

bloques (CCSprite) como variable global.

private CCSprite bloque;

private LinkedList<CCSprite> listaBloques = new LinkedList<CCSprite>();

Creamos un método inicializarBloques al que posteriormente invocaremos desde el

constructor de la capa.

1) Este crea e inserta n bloques en la pantalla

2) Va posicionando los bloques en columnas (empezando por la parte superior), hasta

que la columna está completa.

3) Cuando la columna está completa cambia de fila

public void inicializarBloques() {

int insertados = 0;

int fila = 0;

int columna = 0;

while (insertados < 25) {

CCSprite bloque = CCSprite.sprite("cocodrilo_1.png");

float posX = (20 + bloque.getContentSize().width / 2.0f)

+ (bloque.getContentSize().width * columna);

float posY = (winSize.height - bloque.getContentSize().height / 2.0f)

- (bloque.getContentSize().height * fila);

bloque.setPosition(CGPoint.ccp(posX, posY));

Page 17: Desarrollo de Videojuegos Android con Cocos2D

17

columna++;

/* La posicionX del bloque esta fuera de la pantalla */

if (posX + bloque.getContentSize().width > winSize.width) {

columna = 0;

fila++;

} else {

listaBloques.add(bloque);

addChild(bloque);

insertados++;

}

}

}

Incluimos la llamada al método inicializarBloques en la última línea del constructor CapaJuego

this.schedule("gameLogic", 1/40f);

inicializarBloques();

}

Ahora tenemos que modificar el método updateLogic para adaptarlo a la lista de bloques.

1) Recorremos la lista de boques.

2) Creamos el rectángulo de área de colisión para cada bloque y comprobamos si

colisiona con la pelota.

3) Si existe colisión tenemos que eliminar el bloque de la lista, pero no podemos eliminar

elementos de una lista mientras la recorremos. Guardamos el bloque seleccionado en

la lista de los bloques que vamos a destruir.

4) Si hay algún bloque en la lista de bloques pendientes por destruir invertimos la

velocidad de la bola (Rebote).

5) Recorremos la lista de los bloques que hay que destruir y los sacamos de la lista

principal y de la escena.

LinkedList<CCSprite> listaBloquesParaDestruir = new LinkedList<CCSprite>();

for (CCSprite bloque : listaBloques) {

CGRect areaBloque = CGRect.make(

bloque.getPosition().x - bloque.getContentSize().width/ 2.0f,

bloque.getPosition().y - bloque.getContentSize().height/ 2.0f,

bloque.getContentSize().width,

bloque.getContentSize().height);

if (CGRect.intersects(areaPelota, areaBloque)) {

listaBloquesParaDestruir.add(bloque);

removeChild(bloque, true);

}

}

if (listaBloquesParaDestruir.size() > 0) {

for (CCSprite bloque : listaBloquesParaDestruir) {

listaBloques.remove(bloque);

}

velocidadPelotaX = velocidadPelotaX * -1;

velocidadPelotaY = velocidadPelotaY * -1;

}

}

Page 18: Desarrollo de Videojuegos Android con Cocos2D

18

7 Animaciones: CCSpriteFrame , CCAnimation, CCAction

Modificaremos el mecanismo de creación de los bloques para insertar una animación.

public void inicializarBloques() {

int insertados = 0;

int fila = 0;

int columna = 0;

while (insertados < 25) {

CCSprite bloque = CCSprite.sprite("cocodrilo_1.png");

float posX = (20 + bloque.getContentSize().width / 2.0f)

+ (bloque.getContentSize().width * columna);

float posY = (winSize.height - bloque.getContentSize().height / 2.0f)

- (bloque.getContentSize().height * fila);

bloque.setPosition(CGPoint.ccp(posX, posY));

columna++;

// La posicionX del bloque esta fuera de la pantalla

if (posX + bloque.getContentSize().width > winSize.width) {

columna = 0;

fila++;

} else {

listaBloques.add(bloque);

addChild(bloque);

insertados++;

}

}

}

Para crear la animación vamos a:

1) Cachear el Sprite con la animación, Cocos2D utiliza unos ficheros XML

(animacioncocodrilo.plist) para definir las animaciones.

Vamos a examinar el fichero .plist para entender su estructura.

ULTIMAS LINEAS

<dict>

<key>format</key>

<integer>2</integer>

<key>realTextureFileName</key>

<string>animacioncocodrilo.png</string>

<key>size</key>

<string>{320,40}</string>

<key>smartupdate</key>

<string>$TexturePacker:SmartUpdate:0e07504f614e935fcfc28006d277442e$</string>

<key>textureFileName</key>

<string>animacioncocodrilo.png</string>

Page 19: Desarrollo de Videojuegos Android con Cocos2D

19

</dict>

DECLARACIÓN DE FRAMES <key>cocodrilo1.png</key>

<dict>

<key>frame</key>

<string>{{0,0},{40,40}}</string>

<key>offset</key>

<string>{0,0}</string>

<key>rotated</key>

<false/>

<key>sourceColorRect</key>

<string>{{0,0},{40,40}}</string>

<key>sourceSize</key>

<string>{40,40}</string>

</dict> <key>cocodrilo2.png</key>

<dict>

<key>frame</key>

<string>{{40,0},{40,40}}</string>

<key>offset</key>

<string>{0,0}</string>

<key>rotated</key>

<false/>

<key>sourceColorRect</key>

<string>{{0,0},{40,40}}</string>

<key>sourceSize</key>

<string>{40,40}</string>

</dict>

Una vez se carga este fichero tenemos toda la información de la animación en

memoria.

2) Seleccionamos los frames de la animación que nos interesan, para ello utilizamos la

clave de los frames: cocodrilo1.png, cocodrilo2.png, etc. Los introducimos en un array

de CCSpriteFrame

3) Asignar los frames a un objeto de tipo CCAnimation, y le damos un nombre, en este

caso “basica”

4) Colocamos el primero de los frames seleccionados como Sprite básico del elemento

bloque, (nos servirá para darle las dimensiones al bloque).

5) Crear una acción CCAction a partir de la animación y un boolean que determina si la

animación se ejecutara de manera continua.

6) Ordenamos al bloque que ejecute la acción que contiene la animación utilizando el

método runAction

cocodrilo2.png x=40, y=0 Alto=40 Ancho =40

Cocodrilo3.png x=80, y=0 Alto=40 Ancho =40

Page 20: Desarrollo de Videojuegos Android con Cocos2D

20

CCSpriteFrameCache.sharedSpriteFrameCache().addSpriteFrames("animacioncocodrilo.plist");

ArrayList<CCSpriteFrame> frames = new ArrayList<CCSpriteFrame>();

for(int i = 1; i<=8;++i)

{

frames.add(CCSpriteFrameCache.sharedSpriteFrameCache().spriteFrameByName("cocodrilo"+ i

+".png"));

}

CCAnimation animacionBasica = CCAnimation.animation("basica", 0.1f, frames);

CCSprite bloque = CCSprite.sprite(frames.get(5));

CCAction accionBasica = CCRepeatForever.action(CCAnimate.action(animacionBasica, true));

bloque.runAction(accionBasica);

Generación aleatoria de Bloques

Vamos a incluir un mecanismo para que los bloques sean generados de manera automática

tengan diferente aspecto gráfico.

En la carpeta de recursos gráficos tenemos las animaciones: animaciontigre y animacionpanda

con sus correspondientes ficheros de definición .plist.

Implementamos el método generaBloqueAletorio, que construye un bloque animado

utilizando uno de los tres recursos gráficos, el cocodrilo, el tigre o el panda. La selección del

recurso grafico se hace de manera aleatoria.

Creamos un array con las 3 claves {[0]cocodrilo, [1]tigre, [2]panda} y seleccionamos una de

manera automática generando un entero aleatorio comprendido entre 0 y 2.

public CCSprite generaBloqueAleatorio() {

CCSprite bloque = null;

String[] animaciones = { "cocodrilo", "tigre", "panda" };

int index = new Random().nextInt(3);

CCSpriteFrameCache.sharedSpriteFrameCache().addSpriteFrames(

"animacion" + animaciones[index] + ".plist");

ArrayList<CCSpriteFrame> frames = new ArrayList<CCSpriteFrame>();

for (int i = 1; i <= 8; ++i) {

frames.add(CCSpriteFrameCache.sharedSpriteFrameCache()

.spriteFrameByName(animaciones[index] + i + ".png"));

}

CCAnimation animacionBasica = CCAnimation.animation("basica", 0.1f,frames);

bloque = CCSprite.sprite(frames.get(0));

CCAction accionBasica = CCRepeatForever.action(CCAnimate.action(

animacionBasica, true));

bloque.runAction(accionBasica);

return bloque;

}

Modificamos la implementación inicializarBloques(), en lugar de crear el Sprite para el bloque

solicitamos un Sprite aleatorio.

Page 21: Desarrollo de Videojuegos Android con Cocos2D

21

Page 22: Desarrollo de Videojuegos Android con Cocos2D

22

public void inicializarBloques() {

int insertados = 0;

int fila = 0;

int columna = 0;

while (insertados < 25) {

CCSprite bloque = generaBloqueAleatorio();

float posX = (20 + bloque.getContentSize().width / 2.0f)

+ (bloque.getContentSize().width * columna);

float posY = (winSize.height - bloque.getContentSize().height / 2.0f)

- (bloque.getContentSize().height * fila);

bloque.setPosition(CGPoint.ccp(posX, posY));

columna++;

Page 23: Desarrollo de Videojuegos Android con Cocos2D

23

8 Fondo

Incluiremos el recurso gráfico fondo.png como fondo de pantalla de la CapaJuego. El proceso

es idéntico al que seguimos para colocar la pelota y la pala.

Como el orden de agregación de los CCSprite es relevante debemos añadir el fondo el primer

lugar (para que no oculte al resto de elementos). Posicionaremos el fondo en el punto medio

de la pantalla.

protected CapaJuego(ccColor4B color) {

super(color);

winSize = CCDirector.sharedDirector().displaySize();

// fondo

CCSprite fondo = CCSprite.sprite("fondo.png");

fondo.setPosition(CGPoint.ccp(winSize.width/2.0f, winSize.height/2.0f));

addChild(fondo);

// Pelota

pelota = CCSprite.sprite("bola.png");

pelota.setPosition(CGPoint.ccp(winSize.width / 2.0f,

winSize.height / 2.0f));

addChild(pelota);

Page 24: Desarrollo de Videojuegos Android con Cocos2D

24

9 Sistemas de partículas: CCParticleSystem

Cocos 2D incluye un sistema de soporte sencillo para definir “efectos visuales” basados en

partículas, este sistema se llama CCParticleSystem y se utiliza comúnmente para realizar

efectos especiales como: explosiones, humo, fuego, lluvia, etc.

El usuario define una serie de parámetros relevantes en las partículas, tipo de partículas,

velocidad de emisión, duración de la animación, velocidad, textura… y el más importante las

coordenadas del punto donde se producirá la emisión de partículas.

Algunos de los tipos de partículas más utilizados son: CCParticleRain, CCParticleSoke,

CCParticleMeteor, CCParticleExplosion y CCParticleGalaxy, pero hay muchos otros.

Vamos a incluir una emisión de partículas de tipo CCParticleRain cuando la pelota impacte

contra un bloque.

Localizamos la parte del método updateLogic() que elimina los bloques contra los que impacto

la pelota, a continuación incluimos la llamada al sistema de partículas, estableciendo como

punto de origen la posición de la pelota.

if (listaBloquesParaDestruir.size() > 0) {

for (CCSprite bloque : listaBloquesParaDestruir) {

listaBloques.remove(bloque);

}

velocidadPelotaX = velocidadPelotaX * -1;

velocidadPelotaY = velocidadPelotaY * -1;

/* Explosion */

CCParticleSystem emitter = CCParticleRain.node();

emitter.setEmissionRate(100.0f);

emitter.setDuration(0.01f);

emitter.setGravity(CGPoint.ccp(0,0));

emitter.setSpeed(100.0f);

emitter.setSpeedVar(70.0f);

emitter.setPosition(pelota.getPosition());

emitter.setPosVar(CGPoint.ccp(0, 0));

emitter.setTexture(

CCTextureCache.sharedTextureCache().addImage("bola.png"));

emitter.setAutoRemoveOnFinish(true);

addChild(emitter,8);

}

Page 25: Desarrollo de Videojuegos Android con Cocos2D

25

10 Condición y pantalla de perder: CCLabel , replaceScene,

CCDelayTime.

Actualmente el juego no tiene final, no se puede ganar ni perder. Vamos a incluir una

comprobación en la lógica del juego que evalué si la pelota se encuentra en una posición más

baja que la paleta (coordenada Y), en caso de que así sea, se finalizara el juego y se enviara al

usuario a una nueva pantalla (Escena) que le notifique que ha perdido.

En primer lugar vamos a definir la pantalla que aparecerá cuando el usuario pierda el juego,

creamos una nueva clase Java a la que llamaremos CapaPerder y heredara de la clase base

CCColorLayer.

public class CapaPerder extends CCColorLayer{

protected CapaPerder(ccColor4B color) {

super(color);

}

}

Al igual que hicimos en la CapaJuego en el constructor de la CapaPerder introducir aquellos elementos que se vayan a visualizar en la pantalla. En este caso queremos que tenga un fondo de color negro y un texto que le diga al usuario que ha perdido.

protected CapaPerder(ccColor4B color)

{

super(color);

this.setIsTouchEnabled(true);

CGSize winSize = CCDirector.sharedDirector().displaySize();

CCLabel texto = CCLabel.makeLabel("Has perdido", "DroidSans", 32);

texto.setColor(ccColor3B.ccWHITE);

texto.setPosition(winSize.width / 2.0f, winSize.height / 2.0f);

addChild(texto);

}

Ahora tenemos que crear el método que genera la escena a partir de la capa (de la misma manera que hicimos en CapaJuego. Creamos un método estático que inicializa un objeto CCScene (Basado en la capa) y lo retorna. public static CCScene scene()

{

CCScene scene = CCScene.node();

CapaPerder capaPerder = new CapaPerder(ccColor4B.ccc4(0, 0, 0, 255));

scene.addChild(capaPerder);

return scene;

}

Por ultimo vamos a implementar un método JugarDeNuevo() que servirá para que el jugador pueda volver a iniciar el juego. Lo único que hace este método es volver a colocar la escena generada a partir de CapaJuego como escena principal. public void jugarDeNuevo()

{

Page 26: Desarrollo de Videojuegos Android con Cocos2D

26

CCDirector.sharedDirector().replaceScene(CapaJuego.scene());

}

Vamos a hacer que este método se invoque cuando el usuario pulse sobre la pantalla y levante el dedo.

@Override

public boolean ccTouchesEnded(MotionEvent event)

{

jugarDeNuevo();

return true;

}

Implementamos la comprobación de la posición actual de la pelota al final el método gameLogic(). Si la coordenada Y de la pelota es menor de 50 (parte baja de la pantalla) se ha perdido la partida y se establece como principal la escena generada a partir de la CapaPerder. if (pelota.getPosition().y < 50){

CCDirector.sharedDirector().replaceScene(CapaPerder.scene());

}

Vamos a modificar la velocidad inicial en el eje X de la pelota para que caiga “recta”. public class CapaJuego extends CCColorLayer {

private int velocidadPelotaX = 0;

private int velocidadPelotaY = -10;

Podemos incluir una condición en el constructor de la CapaPerder para que una acción se ejecute automáticamente transcurridos N segundos.

Page 27: Desarrollo de Videojuegos Android con Cocos2D

27

Vamos a hacer que el método JugarDeNuevo (que reinicia la partida) se ejecute cuando pasen tres segundos. protected CapaPerder(ccColor4B color)

{

super(color);

this.setIsTouchEnabled(true);

CGSize winSize = CCDirector.sharedDirector().displaySize();

CCLabel texto = CCLabel.makeLabel("Has perdido", "DroidSans", 32);

texto.setColor(ccColor3B.ccWHITE);

texto.setPosition(winSize.width / 2.0f, winSize.height / 2.0f);

addChild(texto);

this.runAction(CCSequence.actions(CCDelayTime.action(3.0f),

CCCallFunc.action(this, "jugarDeNuevo")));

}

Page 28: Desarrollo de Videojuegos Android con Cocos2D

28

11 Condición y pantalla de ganar. Al igual que hicimos en la CapaPerder vamos a crear una nueva capa CapaGanar, debe de ser idéntica a la anterior pero en este caso tendrá en fondo verde y la letra blanca y le dirá al usuario que ha ganado la partida. public class CapaGanar extends CCColorLayer {

protected CapaGanar(ccColor4B color) {

super(color);

this.setIsTouchEnabled(true);

CGSize winSize = CCDirector.sharedDirector().displaySize();

CCLabel texto = CCLabel.makeLabel("Siguiente Nivel", "DroidSans", 32);

texto.setColor(ccColor3B.ccWHITE);

texto.setPosition(winSize.width / 2.0f, winSize.height / 2.0f);

addChild(texto);

this.runAction(CCSequence.actions(CCDelayTime.action(3.0f),

CCCallFunc.action(this, "siguienteNivel")));

}

public static CCScene scene() {

CCScene scene = CCScene.node();

CapaGanar capaSiguienteNivel =

new CapaGanar(ccColor4B.ccc4(0, 255, 0, 100));

scene.addChild(capaSiguienteNivel);

return scene;

}

public void siguienteNivel() {

CCDirector.sharedDirector().replaceScene(CapaJuego.scene());

}

@Override

public boolean ccTouchesEnded(MotionEvent event) {

siguienteNivel();

return true;

}

} Incluiremos un sistema muy similar al visto anteriormente que índice si el usuario ha ganado,

para ello Implementamos una comprobación al final el método gameLogic() que compruebe

que si queda algún bloque sin destruir en la lista de bloques.

if (pelota.getPosition().y < 50){

CCDirector.sharedDirector().replaceScene(CapaPerder.scene());

}

if (listaBloques.size() == 0){

CCDirector.sharedDirector().replaceScene(CapaGanar.scene());

}

}

Page 29: Desarrollo de Videojuegos Android con Cocos2D

29

12 Generación de niveles

Cuando el usuario finaliza la partida se encuentra con que aparece una pantalla que le indica

que ha superado el nivel, pero si pulsa sobre la pantalla vuelve a jugar exactamente el mismo

nivel.

Vamos a incluir un mecanismo sencillo que contabilice los niveles que el usuario logra

completar, y cada vez genere un nivel con un mayor número de bloques.

Inicialmente el nivel del juego será el nivel 1.

public class CapaJuego extends CCColorLayer {

public static int nivel = 1;

private int velocidadPelotaX = 0;

private int velocidadPelotaY = -10;

Modificamos el método inicializarBloques() para que en lugar de generar 25 bloques genere

un numero de bloques dependiente del nivel, por ejemplo 5 * nivel, que serían: 5 para el nivel

1, 10 para el nivel 2, 15 para el nivel 3, etc.

public void inicializarBloques() {

int insertados = 0;

int fila = 0;

int columna = 0;

while (insertados < 5*nivel) {

CCSprite bloque = generaBloqueAleatorio();

Modificamos el método siguienteNivel de la CapaGanar para que aumente en uno el nivel

actual del juego.

public void siguienteNivel() {

CapaJuego.nivel++;

CCDirector.sharedDirector().replaceScene(CapaJuego.scene());

}

Page 30: Desarrollo de Videojuegos Android con Cocos2D

30

13 Efectos de sonido:

Creamos la carpeta res/raw/ e introducimos en ella todos los efectos de sonido del juego

Creamos el método inicializarMusica() en la CapaJuego, este método iniciara la melodía de

fondo.

public static void iniciarMusica(){

/** Sonido */

Context context = CCDirector.sharedDirector().getActivity();

SoundEngine.sharedEngine().stopSound();

SoundEngine.purgeSharedEngine();

SoundEngine.sharedEngine().playSound(context, R.raw.sonidobucle, true);

}

En el constructor de la capa juego precargamos el sonido grunt (que utilizaremos

posteriormente) y posteriormente inicializamos la música del juego.

protected CapaJuego(ccColor4B color) {

super(color);

// Tamaño de la pantalla, util para colocar objetos

winSize = CCDirector.sharedDirector().displaySize();

/** Sonido */

Context context = CCDirector.sharedDirector().getActivity();

SoundEngine.sharedEngine().preloadEffect(context, R.raw.grunt);

iniciarMusica();

Nos situamos en el gameLogic() y reproducimos el sonido grunt cuando la pelota impacte con un bloque.

if (CGRect.intersects(areaPelota, areaBloque)){

/** Sonido */

Context context = CCDirector.sharedDirector().getActivity();

SoundEngine.sharedEngine().playEffect(context, R.raw.grunt);

listaBloquesParaDestruir.add(bloque);

removeChild(bloque, true);

}

Creamos un nuevo método en la CapaJuego que sirva para pausar la música, invocaremos a este método cuando el usuario se salga del juego. public static void pausarMusica(){

// Sonido

SoundEngine.sharedEngine().stopSound();

}

Completamos los métodos onPause() y onResume() de MainActivity para que paren e inicien

la música del juego.

Page 31: Desarrollo de Videojuegos Android con Cocos2D

31

@Override

public void onPause()

{

super.onPause();

CCDirector.sharedDirector().pause();

CapaJuego.pausarMusica();

}

@Override

public void onResume()

{

super.onResume();

CCDirector.sharedDirector().resume();

CapaJuego.iniciarMusica();

}