martes, 9 de agosto de 2011

Tutorial Cocos2D: Juego completo (parte 2)

Por fin he tenido tiempo para preparar la segunda parte del tutorial. Esta vez mostraré como animar CCSprites para moverlos por pantalla, así como usar la directiva @selector y reproducir efectos de sonido.

Empezaré por la cabecera del monstruo, así que pensemos antes de escribir... ¿Qué métodos y funciones debe tener esta clase para poder interactuar con ella? Yo he pensado que como variables debe de tener las acciones de movimiento, y además, un array de strings donde está almacenado cada tipo de monstruo. Además, el monstruo necesita saber que nivel de "agresividad" tener acorde a un parametro de puntuación y con el tiempo de juego.




#import 
#import "cocos2d.h"
#import "SimpleAudioEngine.h"

@interface Monstruo : CCSprite {
    float escalaInicial;               //Escala inicial del monstruo (para reiniciarlo)
    CGPoint posicionInicial;           //Posición inicial del monstruo (para reiniciarlo)
    CCFiniteTimeAction *aparece;       //Acción de aparecer desde el hoyo
    CCFiniteTimeAction *desaparece;    //Acción de desaparecer hasta el agujero
    CCSequence *apareceDesaparece;     //Secuenciación de ambas acciones anteriores
    CCFiniteTimeAction *salta;         //Acción de salto (para otro tipo de movimiento)
    NSArray *colores;                  //Array que guarda los tipos de monstruos
    bool muerto;                       //Variable que indica si el monstruo está vivo o no
    int nivel;                         //Variable relativa a la "agresividad" del monstruo
    float reduccion;                   //Reducción del tiempo de animación según la "agresividad"
    int contador;                      //Contador para aumentar la agresividad
    int nivelPuntuacion;               //Puntuación actual del juego
}
-(void) aumentaNivel:(int) p;          //Constantemente se está subiendo el nivel por puntuación
-(bool) estaMuerto;                    //Devuelve el estado del monstruo
-(void) recibeGolpe;                   //Crea una animación cuando es golpeado por el mazo
-(void) marcaOrigen;                   //Procedimiento que establece la posición y tamaño original
@end

Ahora hagamos la implementación de la clase Monstruo.

#import "Monstruo.h"


@implementation Monstruo

// Aquí en init, vamos a usar schedule, esto lo que hace es llamar al procedimiento que pongamos
// en el selector cada x segundos. Así implementamos un poco de aletoriedad en nuestro monstruo.
-(id) init
{
	// always call "super" init
	// Apple recommends to re-assign "self" with the "super" return value
	if( (self=[super init])) {
        colores = [[NSArray alloc] initWithObjects:@"Rosa.png", @"Azul.png",@"Verde.png", @"Naranja.png", nil];
        [self schedule:@selector(update:) interval:0.8f];
        [self schedule:@selector(sonidos:) interval:0.5 + ((rand()%10)/10.0f)];

	}
	return self;
}

//En esta clase he usado el arc4random()! Muy importante, puesto que es el algoritmo más aleatorio que podemos usar (de otra manera, con rand(), siempre obtenemos el mismo patrón de números)
-(void) cambiaTipo{
    int r = arc4random() % 4;
    [self setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[colores objectAtIndex:r]]];
}

//Este procedimiento lo llamaremos cuando el monstruo a muerto... Aquí lo revivo
-(void) reinicia{
    self.opacity = 255;
    self.position = posicionInicial;
    self.rotation = 0;
    [self schedule:@selector(update:) interval:0.8f + ((rand()%7)/10.0f)];
    [self schedule:@selector(sonidos:) interval:0.5 + ((rand()%10)/10.0f)];
    muerto = false;
}

//En este procedimiento animo el monstruo para que desaparezca por un instante.
-(void) recibeGolpe{
    [self stopAllActions];
    [self unscheduleAllSelectors];
    CCMoveBy *mueve = [CCMoveBy actionWithDuration:1 position: ccp(20-(rand()%40),80)];
    CCRotateBy *rota = [CCRotateBy actionWithDuration:1 angle:360-(rand()%720)];
    CCFadeTo *fade = [CCFadeTo actionWithDuration:1 opacity:0];
    CCSpawn *anima = [CCSpawn actions:mueve,rota, fade, nil];
    CCSequence *secuencia = [CCSequence actions:anima, [CCCallFunc actionWithTarget:self selector:@selector(reinicia)],nil];
    [self runAction:secuencia];
    muerto = true;
    
}

//Trivial
-(bool) estaMuerto{
    return muerto;
}

//Con este intento dar un poco de vida al juego, reproduciendo sonidos aleatoriamente (buena técnica)
-(void)sonidos:(ccTime)dt
{
    int r = arc4random() % 100;
    int n = 1;
    if (r>90){
        [[SimpleAudioEngine sharedEngine] playEffect:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"Fondo0%d", n] ofType:@"mp3"] pitch:1.0 pan:0 gain:0.25f];
    }
    
}


//Metodo que se llama cada X segundos y crea las animaciones de aparecer.
-(void)update:(ccTime)dt
{
    int r = arc4random() % 100;
    if (r>75 && (self.position.y == posicionInicial.y)){
        r = arc4random() % 2;
        if (r== 0){
            aparece = [CCMoveBy actionWithDuration:0.8*reduccion position:ccp(0,75)];
            desaparece = [CCMoveBy actionWithDuration:0.8*reduccion position:ccp(0,-75)];
            apareceDesaparece = [CCSequence actions:aparece, desaparece, nil];
            [self runAction:apareceDesaparece];
            [[SimpleAudioEngine sharedEngine] playEffect:[[NSBundle mainBundle] pathForResource:@"Aparece" ofType:@"mp3"]];
        }else{
            salta = [CCJumpTo actionWithDuration:1*reduccion position:posicionInicial height:80 jumps:1];
            [[SimpleAudioEngine sharedEngine] playEffect:[[NSBundle mainBundle] pathForResource:@"Salto" ofType:@"mp3"]];
            [self runAction:salta];
        }
        [self cambiaTipo];
            
    }
    
    contador++;
    if (contador > 4){
        contador = 0;
        nivel++;
        reduccion = 1.0f -((nivel+nivelPuntuacion)/100.0f);
    }
}

//Guardamos posiciones iniciales
-(void) marcaOrigen{
    escalaInicial = self.scale;
    posicionInicial = self.position;
}

//Aumento el nivel de dificultad en base a la puntuación
-(void) aumentaNivel:(int) p{
    nivelPuntuacion = (p/500);
}

@end

Me gustaría aclarar de todo el código que significa y qué valores dar a pitch, pan, y gain
Gain es el volumen, y un valor de 1 lo mantiene igual, 2 lo duplica y 0.5 lo reduce a la mitad.
Pitch es el tono, un valor de 2 lo hará más agudo, y por de bajo de 1 más grave
Pan es el "estéreo" 0 quiere decir que saldrá por ambos auriculares, -1 por el izquierdo totalmente y 1 por el derecho.

Y para realizar esta clase, necesitáis el archivo de imagenes y propiedades, que como es habitual, subo para vosotros.

Imágenes gratis / Free Sprites

Continua leyendo la parte 3 del Tutorial de cocos2D

No hay comentarios:

Publicar un comentario