SlideShare una empresa de Scribd logo
1 de 31
Descargar para leer sin conexión
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
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
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.
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.
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.
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
7
public void onStop()
{
super.onStop();
CCDirector.sharedDirector().end();
}
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);
}
9
Si ejecutamos la aplicación podremos ver la escena compuesta por la capa CapaJuego y la
pelota en el punto medio.
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);
}
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;
}
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);
}
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;
}
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á.
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
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));
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;
}
}
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>
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
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.
21
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++;
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);
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);
}
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()
{
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.
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")));
}
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());
}
}
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());
}
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.
31
@Override
public void onPause()
{
super.onPause();
CCDirector.sharedDirector().pause();
CapaJuego.pausarMusica();
}
@Override
public void onResume()
{
super.onResume();
CCDirector.sharedDirector().resume();
CapaJuego.iniciarMusica();
}

Más contenido relacionado

Similar a Desarrollo de Videojuegos Android con Cocos2D

HTML Tour - Programación de Videojuegos HTML5
HTML Tour - Programación de Videojuegos HTML5HTML Tour - Programación de Videojuegos HTML5
HTML Tour - Programación de Videojuegos HTML5Plain Concepts
 
Curso de Desarrollo Web 2
Curso de Desarrollo Web 2Curso de Desarrollo Web 2
Curso de Desarrollo Web 2juliocombativo
 
Renderización en java
Renderización en javaRenderización en java
Renderización en javaaleja0940
 
LabAndroid: Taller "Mi Primera Aplicación Android"
LabAndroid: Taller "Mi Primera Aplicación Android"LabAndroid: Taller "Mi Primera Aplicación Android"
LabAndroid: Taller "Mi Primera Aplicación Android"Alberto Ruibal
 
Manual de android
Manual de androidManual de android
Manual de androidJarboledah
 
Introducción al desarrollo Web: Frontend con Angular 6
Introducción al desarrollo Web: Frontend con Angular 6Introducción al desarrollo Web: Frontend con Angular 6
Introducción al desarrollo Web: Frontend con Angular 6Gabriela Bosetti
 
Introducción a react + redux
Introducción a react + reduxIntroducción a react + redux
Introducción a react + reduxMiguel Mendoza
 
Codemotion 2017 - Taller de JHipster
Codemotion 2017 - Taller de JHipsterCodemotion 2017 - Taller de JHipster
Codemotion 2017 - Taller de JHipsterAdolfo Sanz De Diego
 
Desarrollo de un Prototipo de Entornos Virtuales para Fines Didacticos en Emp...
Desarrollo de un Prototipo de Entornos Virtuales para Fines Didacticos en Emp...Desarrollo de un Prototipo de Entornos Virtuales para Fines Didacticos en Emp...
Desarrollo de un Prototipo de Entornos Virtuales para Fines Didacticos en Emp...EILLENMILAGROSVEGASA1
 
Introducción a las librerías PyGame y PyOpenGL
Introducción a las librerías PyGame y PyOpenGLIntroducción a las librerías PyGame y PyOpenGL
Introducción a las librerías PyGame y PyOpenGLkdeespana
 
TEMPIC-45 Práctica IV. Voltímetro de CD
TEMPIC-45 Práctica IV. Voltímetro de CDTEMPIC-45 Práctica IV. Voltímetro de CD
TEMPIC-45 Práctica IV. Voltímetro de CDiihhhii
 
Segundo laboratorio de Android
Segundo laboratorio de AndroidSegundo laboratorio de Android
Segundo laboratorio de AndroidGDG Cali
 
Reactividad en Angular, React y VueJS
Reactividad en Angular, React y VueJSReactividad en Angular, React y VueJS
Reactividad en Angular, React y VueJSJavier Abadía
 

Similar a Desarrollo de Videojuegos Android con Cocos2D (20)

Expo kinect
Expo kinectExpo kinect
Expo kinect
 
Tfg ciudad .fernandez_2017
Tfg ciudad .fernandez_2017Tfg ciudad .fernandez_2017
Tfg ciudad .fernandez_2017
 
HTML Tour - Programación de Videojuegos HTML5
HTML Tour - Programación de Videojuegos HTML5HTML Tour - Programación de Videojuegos HTML5
HTML Tour - Programación de Videojuegos HTML5
 
Curso de Desarrollo Web 2
Curso de Desarrollo Web 2Curso de Desarrollo Web 2
Curso de Desarrollo Web 2
 
Renderización en java
Renderización en javaRenderización en java
Renderización en java
 
LabAndroid: Taller "Mi Primera Aplicación Android"
LabAndroid: Taller "Mi Primera Aplicación Android"LabAndroid: Taller "Mi Primera Aplicación Android"
LabAndroid: Taller "Mi Primera Aplicación Android"
 
Curso android studio
Curso android studioCurso android studio
Curso android studio
 
Curso android studio
Curso android studioCurso android studio
Curso android studio
 
Manual de android
Manual de androidManual de android
Manual de android
 
Introducción al desarrollo Web: Frontend con Angular 6
Introducción al desarrollo Web: Frontend con Angular 6Introducción al desarrollo Web: Frontend con Angular 6
Introducción al desarrollo Web: Frontend con Angular 6
 
Introducción a react + redux
Introducción a react + reduxIntroducción a react + redux
Introducción a react + redux
 
Codemotion 2017 - Taller de JHipster
Codemotion 2017 - Taller de JHipsterCodemotion 2017 - Taller de JHipster
Codemotion 2017 - Taller de JHipster
 
Desarrollo de un Prototipo de Entornos Virtuales para Fines Didacticos en Emp...
Desarrollo de un Prototipo de Entornos Virtuales para Fines Didacticos en Emp...Desarrollo de un Prototipo de Entornos Virtuales para Fines Didacticos en Emp...
Desarrollo de un Prototipo de Entornos Virtuales para Fines Didacticos en Emp...
 
Introduccion a Android
Introduccion a AndroidIntroduccion a Android
Introduccion a Android
 
Java tema06a
Java tema06aJava tema06a
Java tema06a
 
Introducción a las librerías PyGame y PyOpenGL
Introducción a las librerías PyGame y PyOpenGLIntroducción a las librerías PyGame y PyOpenGL
Introducción a las librerías PyGame y PyOpenGL
 
TEMPIC-45 Práctica IV. Voltímetro de CD
TEMPIC-45 Práctica IV. Voltímetro de CDTEMPIC-45 Práctica IV. Voltímetro de CD
TEMPIC-45 Práctica IV. Voltímetro de CD
 
Segunda sesion
Segunda sesionSegunda sesion
Segunda sesion
 
Segundo laboratorio de Android
Segundo laboratorio de AndroidSegundo laboratorio de Android
Segundo laboratorio de Android
 
Reactividad en Angular, React y VueJS
Reactividad en Angular, React y VueJSReactividad en Angular, React y VueJS
Reactividad en Angular, React y VueJS
 

Último

DIDÁCTICA DE LA EDUCACIÓN SUPERIOR- DR LENIN CARI MOGROVEJO
DIDÁCTICA DE LA EDUCACIÓN SUPERIOR- DR LENIN CARI MOGROVEJODIDÁCTICA DE LA EDUCACIÓN SUPERIOR- DR LENIN CARI MOGROVEJO
DIDÁCTICA DE LA EDUCACIÓN SUPERIOR- DR LENIN CARI MOGROVEJOLeninCariMogrovejo
 
Actividad transversal 2-bloque 2. Actualización 2024
Actividad transversal 2-bloque 2. Actualización 2024Actividad transversal 2-bloque 2. Actualización 2024
Actividad transversal 2-bloque 2. Actualización 2024Rosabel UA
 
Contextualización y aproximación al objeto de estudio de investigación cualit...
Contextualización y aproximación al objeto de estudio de investigación cualit...Contextualización y aproximación al objeto de estudio de investigación cualit...
Contextualización y aproximación al objeto de estudio de investigación cualit...Angélica Soledad Vega Ramírez
 
Abregú, Podestá. Directores.Líderes en Acción.
Abregú, Podestá. Directores.Líderes en Acción.Abregú, Podestá. Directores.Líderes en Acción.
Abregú, Podestá. Directores.Líderes en Acción.profandrearivero
 
NUEVO PLAN Y PROGRAMAS DE ESTUDIO 2022.pdf
NUEVO PLAN Y PROGRAMAS DE ESTUDIO  2022.pdfNUEVO PLAN Y PROGRAMAS DE ESTUDIO  2022.pdf
NUEVO PLAN Y PROGRAMAS DE ESTUDIO 2022.pdfEDNAMONICARUIZNIETO
 
Amor o egoísmo, esa es la cuestión por definir.pdf
Amor o egoísmo, esa es la cuestión por definir.pdfAmor o egoísmo, esa es la cuestión por definir.pdf
Amor o egoísmo, esa es la cuestión por definir.pdfAlejandrino Halire Ccahuana
 
Fichas de MatemáticA QUINTO DE SECUNDARIA).pdf
Fichas de MatemáticA QUINTO DE SECUNDARIA).pdfFichas de MatemáticA QUINTO DE SECUNDARIA).pdf
Fichas de MatemáticA QUINTO DE SECUNDARIA).pdfssuser50d1252
 
Actividades eclipse solar 2024 Educacion
Actividades eclipse solar 2024 EducacionActividades eclipse solar 2024 Educacion
Actividades eclipse solar 2024 Educacionviviantorres91
 
SISTEMA INMUNE FISIOLOGIA MEDICA UNSL 2024
SISTEMA INMUNE FISIOLOGIA MEDICA UNSL 2024SISTEMA INMUNE FISIOLOGIA MEDICA UNSL 2024
SISTEMA INMUNE FISIOLOGIA MEDICA UNSL 2024gharce
 
PRIMER GRADO SOY LECTOR PART1- MD EDUCATIVO.pdf
PRIMER GRADO SOY LECTOR PART1- MD  EDUCATIVO.pdfPRIMER GRADO SOY LECTOR PART1- MD  EDUCATIVO.pdf
PRIMER GRADO SOY LECTOR PART1- MD EDUCATIVO.pdfGabrieldeJesusLopezG
 
LOS AMBIENTALISTAS todo por un mundo mejor
LOS AMBIENTALISTAS todo por un mundo mejorLOS AMBIENTALISTAS todo por un mundo mejor
LOS AMBIENTALISTAS todo por un mundo mejormrcrmnrojasgarcia
 
HISPANIDAD - La cultura común de la HISPANOAMERICA
HISPANIDAD - La cultura común de la HISPANOAMERICAHISPANIDAD - La cultura común de la HISPANOAMERICA
HISPANIDAD - La cultura común de la HISPANOAMERICAJesus Gonzalez Losada
 
ENSEÑAR ACUIDAR EL MEDIO AMBIENTE ES ENSEÑAR A VALORAR LA VIDA.
ENSEÑAR ACUIDAR  EL MEDIO AMBIENTE ES ENSEÑAR A VALORAR LA VIDA.ENSEÑAR ACUIDAR  EL MEDIO AMBIENTE ES ENSEÑAR A VALORAR LA VIDA.
ENSEÑAR ACUIDAR EL MEDIO AMBIENTE ES ENSEÑAR A VALORAR LA VIDA.karlazoegarciagarcia
 
IV SES LUN 15 TUTO CUIDO MI MENTE CUIDANDO MI CUERPO YESSENIA 933623393 NUEV...
IV SES LUN 15 TUTO CUIDO MI MENTE CUIDANDO MI CUERPO  YESSENIA 933623393 NUEV...IV SES LUN 15 TUTO CUIDO MI MENTE CUIDANDO MI CUERPO  YESSENIA 933623393 NUEV...
IV SES LUN 15 TUTO CUIDO MI MENTE CUIDANDO MI CUERPO YESSENIA 933623393 NUEV...YobanaZevallosSantil1
 
DIGNITAS INFINITA - DIGNIDAD HUMANA; Declaración del dicasterio para la doctr...
DIGNITAS INFINITA - DIGNIDAD HUMANA; Declaración del dicasterio para la doctr...DIGNITAS INFINITA - DIGNIDAD HUMANA; Declaración del dicasterio para la doctr...
DIGNITAS INFINITA - DIGNIDAD HUMANA; Declaración del dicasterio para la doctr...Martin M Flynn
 
4° UNIDAD 2 SALUD,ALIMENTACIÓN Y DÍA DE LA MADRE 933623393 PROF YESSENIA CN.docx
4° UNIDAD 2 SALUD,ALIMENTACIÓN Y DÍA DE LA MADRE 933623393 PROF YESSENIA CN.docx4° UNIDAD 2 SALUD,ALIMENTACIÓN Y DÍA DE LA MADRE 933623393 PROF YESSENIA CN.docx
4° UNIDAD 2 SALUD,ALIMENTACIÓN Y DÍA DE LA MADRE 933623393 PROF YESSENIA CN.docxMagalyDacostaPea
 
PÉNSUM ENFERMERIA 2024 - ECUGENIUS S.A. V2
PÉNSUM ENFERMERIA 2024 - ECUGENIUS S.A. V2PÉNSUM ENFERMERIA 2024 - ECUGENIUS S.A. V2
PÉNSUM ENFERMERIA 2024 - ECUGENIUS S.A. V2Eliseo Delgado
 
5° Proyecto 13 Cuadernillo para proyectos
5° Proyecto 13 Cuadernillo para proyectos5° Proyecto 13 Cuadernillo para proyectos
5° Proyecto 13 Cuadernillo para proyectosTrishGutirrez
 

Último (20)

DIDÁCTICA DE LA EDUCACIÓN SUPERIOR- DR LENIN CARI MOGROVEJO
DIDÁCTICA DE LA EDUCACIÓN SUPERIOR- DR LENIN CARI MOGROVEJODIDÁCTICA DE LA EDUCACIÓN SUPERIOR- DR LENIN CARI MOGROVEJO
DIDÁCTICA DE LA EDUCACIÓN SUPERIOR- DR LENIN CARI MOGROVEJO
 
¿Amor o egoísmo? Esa es la cuestión.pptx
¿Amor o egoísmo? Esa es la cuestión.pptx¿Amor o egoísmo? Esa es la cuestión.pptx
¿Amor o egoísmo? Esa es la cuestión.pptx
 
Actividad transversal 2-bloque 2. Actualización 2024
Actividad transversal 2-bloque 2. Actualización 2024Actividad transversal 2-bloque 2. Actualización 2024
Actividad transversal 2-bloque 2. Actualización 2024
 
Contextualización y aproximación al objeto de estudio de investigación cualit...
Contextualización y aproximación al objeto de estudio de investigación cualit...Contextualización y aproximación al objeto de estudio de investigación cualit...
Contextualización y aproximación al objeto de estudio de investigación cualit...
 
Abregú, Podestá. Directores.Líderes en Acción.
Abregú, Podestá. Directores.Líderes en Acción.Abregú, Podestá. Directores.Líderes en Acción.
Abregú, Podestá. Directores.Líderes en Acción.
 
NUEVO PLAN Y PROGRAMAS DE ESTUDIO 2022.pdf
NUEVO PLAN Y PROGRAMAS DE ESTUDIO  2022.pdfNUEVO PLAN Y PROGRAMAS DE ESTUDIO  2022.pdf
NUEVO PLAN Y PROGRAMAS DE ESTUDIO 2022.pdf
 
Amor o egoísmo, esa es la cuestión por definir.pdf
Amor o egoísmo, esa es la cuestión por definir.pdfAmor o egoísmo, esa es la cuestión por definir.pdf
Amor o egoísmo, esa es la cuestión por definir.pdf
 
Fichas de MatemáticA QUINTO DE SECUNDARIA).pdf
Fichas de MatemáticA QUINTO DE SECUNDARIA).pdfFichas de MatemáticA QUINTO DE SECUNDARIA).pdf
Fichas de MatemáticA QUINTO DE SECUNDARIA).pdf
 
Actividades eclipse solar 2024 Educacion
Actividades eclipse solar 2024 EducacionActividades eclipse solar 2024 Educacion
Actividades eclipse solar 2024 Educacion
 
SISTEMA INMUNE FISIOLOGIA MEDICA UNSL 2024
SISTEMA INMUNE FISIOLOGIA MEDICA UNSL 2024SISTEMA INMUNE FISIOLOGIA MEDICA UNSL 2024
SISTEMA INMUNE FISIOLOGIA MEDICA UNSL 2024
 
PRIMER GRADO SOY LECTOR PART1- MD EDUCATIVO.pdf
PRIMER GRADO SOY LECTOR PART1- MD  EDUCATIVO.pdfPRIMER GRADO SOY LECTOR PART1- MD  EDUCATIVO.pdf
PRIMER GRADO SOY LECTOR PART1- MD EDUCATIVO.pdf
 
LOS AMBIENTALISTAS todo por un mundo mejor
LOS AMBIENTALISTAS todo por un mundo mejorLOS AMBIENTALISTAS todo por un mundo mejor
LOS AMBIENTALISTAS todo por un mundo mejor
 
HISPANIDAD - La cultura común de la HISPANOAMERICA
HISPANIDAD - La cultura común de la HISPANOAMERICAHISPANIDAD - La cultura común de la HISPANOAMERICA
HISPANIDAD - La cultura común de la HISPANOAMERICA
 
ENSEÑAR ACUIDAR EL MEDIO AMBIENTE ES ENSEÑAR A VALORAR LA VIDA.
ENSEÑAR ACUIDAR  EL MEDIO AMBIENTE ES ENSEÑAR A VALORAR LA VIDA.ENSEÑAR ACUIDAR  EL MEDIO AMBIENTE ES ENSEÑAR A VALORAR LA VIDA.
ENSEÑAR ACUIDAR EL MEDIO AMBIENTE ES ENSEÑAR A VALORAR LA VIDA.
 
El Bullying.
El Bullying.El Bullying.
El Bullying.
 
IV SES LUN 15 TUTO CUIDO MI MENTE CUIDANDO MI CUERPO YESSENIA 933623393 NUEV...
IV SES LUN 15 TUTO CUIDO MI MENTE CUIDANDO MI CUERPO  YESSENIA 933623393 NUEV...IV SES LUN 15 TUTO CUIDO MI MENTE CUIDANDO MI CUERPO  YESSENIA 933623393 NUEV...
IV SES LUN 15 TUTO CUIDO MI MENTE CUIDANDO MI CUERPO YESSENIA 933623393 NUEV...
 
DIGNITAS INFINITA - DIGNIDAD HUMANA; Declaración del dicasterio para la doctr...
DIGNITAS INFINITA - DIGNIDAD HUMANA; Declaración del dicasterio para la doctr...DIGNITAS INFINITA - DIGNIDAD HUMANA; Declaración del dicasterio para la doctr...
DIGNITAS INFINITA - DIGNIDAD HUMANA; Declaración del dicasterio para la doctr...
 
4° UNIDAD 2 SALUD,ALIMENTACIÓN Y DÍA DE LA MADRE 933623393 PROF YESSENIA CN.docx
4° UNIDAD 2 SALUD,ALIMENTACIÓN Y DÍA DE LA MADRE 933623393 PROF YESSENIA CN.docx4° UNIDAD 2 SALUD,ALIMENTACIÓN Y DÍA DE LA MADRE 933623393 PROF YESSENIA CN.docx
4° UNIDAD 2 SALUD,ALIMENTACIÓN Y DÍA DE LA MADRE 933623393 PROF YESSENIA CN.docx
 
PÉNSUM ENFERMERIA 2024 - ECUGENIUS S.A. V2
PÉNSUM ENFERMERIA 2024 - ECUGENIUS S.A. V2PÉNSUM ENFERMERIA 2024 - ECUGENIUS S.A. V2
PÉNSUM ENFERMERIA 2024 - ECUGENIUS S.A. V2
 
5° Proyecto 13 Cuadernillo para proyectos
5° Proyecto 13 Cuadernillo para proyectos5° Proyecto 13 Cuadernillo para proyectos
5° Proyecto 13 Cuadernillo para proyectos
 

Desarrollo de Videojuegos Android con Cocos2D

  • 1. 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
  • 2. 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
  • 3. 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.
  • 4. 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.
  • 5. 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.
  • 6. 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
  • 8. 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); }
  • 9. 9 Si ejecutamos la aplicación podremos ver la escena compuesta por la capa CapaJuego y la pelota en el punto medio.
  • 10. 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); }
  • 11. 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; }
  • 12. 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); }
  • 13. 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; }
  • 14. 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á.
  • 15. 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
  • 16. 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));
  • 17. 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; } }
  • 18. 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>
  • 19. 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
  • 20. 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.
  • 21. 21
  • 22. 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++;
  • 23. 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);
  • 24. 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); }
  • 25. 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() {
  • 26. 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.
  • 27. 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"))); }
  • 28. 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()); } }
  • 29. 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()); }
  • 30. 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.
  • 31. 31 @Override public void onPause() { super.onPause(); CCDirector.sharedDirector().pause(); CapaJuego.pausarMusica(); } @Override public void onResume() { super.onResume(); CCDirector.sharedDirector().resume(); CapaJuego.iniciarMusica(); }