Monstruos más fuertes y más niveles: ¿Cómo hacer un juego sencillo para iPhone utilizando Cocos2D? Parte 3

Ray Wenderlich

Esta entrada está disponible también en: Chino simplificado, Inglés, Ruso

Watch out for the green guy!

Watch out for the green guy!

Hasta ahora, el juego que hemos estado haciendo en Cómo hacer un juego sencillo para iPhone utilizando Cocos2D está muy bien. Contamos con una torreta giratoria, monstruos a los cuales disparar y efectos de sonidos.

Sin embargo, nuestra torre lo tiene demasiado fácil. Los monstruos mueren con sólo un disparo y hay un solo nivel. Ni siquiera está calentando aún.

En este tutorial, vamos a ampliar nuestro proyecto para que podamos hacer diferentes tipos de monstruos de diferente dificultad e implementar varios niveles en el juego.

Monstruos más resistentes

Por diversión, vamos a crear dos tipos de monstruos: un monstruo débil y rápido y un monstruo fuerte y lento. Para ayudar al jugador a distinguir entre los dos, descarga esta imagen del monstruo modificado y agrégalo a tu proyecto. Mientras, descargue el efecto de sonido de explosión que hice y también agrégalo al proyecto.

Ahora vamos a hacer nuestra clase Monster. Hay muchas formas de modelar la clase de Monster, pero nosotros vamos a hacer lo más simple, que consiste en hacer que nuestra clase Monster sea subclase de CCSprite. También vamos a crear dos subclases de Monster: una para nuestro monstruo débil y rápido y otra para nuestro monstruo fuerte y lento.

Ir a FileNew File, seleccione Cocoa Touch ClassObjective-C class, asegurese que Subclass of NSObject esté seleccionado y haga clic en Next. Nombre el archivo Monster.m y asegúrese de que “Also create Monster.h” está marcada.

A continuación, reemplace Monster.h con lo siguiente:

#import "cocos2d.h"
 
@interface Monster : CCSprite {
    int _curHp;
    int _minMoveDuration;
    int _maxMoveDuration;
}
 
@property (nonatomic, assign) int hp;
@property (nonatomic, assign) int minMoveDuration;
@property (nonatomic, assign) int maxMoveDuration;
 
@end
 
@interface WeakAndFastMonster : Monster {
}
+(id)monster;
@end
 
@interface StrongAndSlowMonster : Monster {
}
+(id)monster;
@end

Bastante sencillo: acabamos de derivar Monster de CCSprite y añadir unas pocas variables para realizar un seguimiento del estado de monster, y luego derivar dos subclases de Monster para dos tipos diferentes de monstruos.

Ahora abra Monster.m y añade en la implementación:

#import "Monster.h"
 
@implementation Monster
 
@synthesize hp = _curHp;
@synthesize minMoveDuration = _minMoveDuration;
@synthesize maxMoveDuration = _maxMoveDuration;
 
@end
 
@implementation WeakAndFastMonster
 
+ (id)monster {
 
    WeakAndFastMonster *monster = nil;
    if ((monster = [[[super alloc] initWithFile:@"Target.png"] autorelease])) {
        monster.hp = 1;
        monster.minMoveDuration = 3;
        monster.maxMoveDuration = 5;
    }
    return monster;
 
}
 
@end
 
@implementation StrongAndSlowMonster
 
+ (id)monster {
 
    StrongAndSlowMonster *monster = nil;
    if ((monster = [[[super alloc] initWithFile:@"Target2.png"] autorelease])) {
        monster.hp = 3;
        monster.minMoveDuration = 6;
        monster.maxMoveDuration = 12;
    }
    return monster;
 
}
 
@end

El único código real aquí son dos métodos estáticos que agregamos para devolver instancias de cada clase, establecer el valor por defecto de HP y la duración de movimiento.

Ahora ¡vamos a integrar nuestra nueva clase Monster con el resto del código! Primero se debe agregar el import a su nuevo archivo en la parte superior de HelloWorldScene.m:

#import "Monster.h"

A continuación vamos a modificar el método de addTarget para construir instancias de la nueva clase en lugar de crear el sprite directamente. Reemplace la línea spriteWithFile con lo siguiente:

//CCSprite *target = [CCSprite spriteWithFile:@"Target.png" rect:CGRectMake(0, 0, 27, 40)]; 
Monster *target = nil;
if ((arc4random() % 2) == 0) {
    target = [WeakAndFastMonster monster];
} else {
    target = [StrongAndSlowMonster monster];
}

Esto dará una probabilidad del 50% para crear cada tipo de monstruo. Además, dado que hemos movido la velocidad de los monstruos para dentro de las clases, modifique las líneas de duración mín/máx de la siguiente manera:

int minDuration = target.minMoveDuration; //2.0;
int maxDuration = target.maxMoveDuration; //4.0;

Por último, un par de modificaciones en el método update. Primero se debe agregar un valor booleano justo antes de la declaración de targetsToDelete:

BOOL monsterHit = FALSE;

Luego, dentro de la condición CGRectIntersectsRect, en lugar de agregar el objeto inmediatamente a targetsToDelete, agregue el siguiente código:

//[targetsToDelete addObject:target];	
monsterHit = TRUE;
Monster *monster = (Monster *)target;
monster.hp--;
if (monster.hp <= 0) {
    [targetsToDelete addObject:target];
}
break;

Así que, básicamente, en vez de matar al monstruo de inmediato, se le resta un HP y sólo es destruido si es 0 o menos. Además, tenga en cuenta que salimos del bucle si el proyectil golpea a un monstruo, lo que significa que el proyectil sólo puede alcanzar a un monstruo por disparo.

Finalmente modifica el chequeo de projectilesToDelete como sigue:

if (monsterHit) {
    [projectilesToDelete addObject:projectile];
    [[SimpleAudioEngine sharedEngine] playEffect:@"explosion.caf"];
}

Compila y ejecuta el código, y si todo va bien debería ver dos tipos diferentes de monstruos cruzando la pantalla – ¡lo que hace que la vida de nuestra torreta sea un poco más difícil!

Screenshot of different monster types

Múltiples Niveles

Con el fin de implementar el soporte de múltiples niveles, tenemos que hacer, en primer lugar, algo de refactorización. La refactorización es bastante simple, pero hay mucho de ella por realizar, e incluirla toda en este post lo haría un post largo y aburrido.

En su lugar, voy a incluir un resumen a alto nivel de lo que se hizo y se refieren a al proyecto de ejemplo para más detalles.

Abstrae una clase nivel. Actualmente, HelloWorldScene contiene en el código información sobre el “nivel” como cuales monstruos se deberían crear, la frecuencia para crearlos, etc. Por lo tanto el primer paso es sacar algo de esta información en una clase Level para que podamos volver a utilizar la misma lógica en HelloWorldScene para múltiples niveles.

Re-uso de las escenas. Actualmente, estamos creando nuevas instancias de la clase escena cada vez que cambiamos entre las escenas. Uno de los inconvenientes de esto es que sin el control adecuado, se pueden sufrir retrasos a medida que se cargan los recursos en el método init.

Ya que tenemos un juego sencillo, lo que vamos a hacer es crear una instancia de cada escena y simplemente llamar a un método reset() en ella, para limpiar cualquier estado viejo (por ejemplo, los monstruos o proyectiles del último nivel).

Utilice el delegado de aplicación “App Delegate” como conmutador. Actualmente, no tenemos ningún estado global como en qué nivel estamos o cuáles son los ajustes para ese nivel, y cada escena guarda en el código para que escenas debe cambiar.

Vamos a modificar esto para que el App Delegate almacene punteros al estado global, tales como la información de nivel, ya que es un lugar central de fácil acceso desde todas las escenas. También voy a poner métodos en el App Delegate para cambiar entre las escenas para centralizar esa lógica y reducir la dependencia entre las escenas.

Así que estos son los puntos principales que explican la refactorización – echa un vistazo al proyecto de ejemplo para ver los detalles completos. Tenga en cuenta que esto es sólo una de las muchas maneras de hacerlo – si usted tiene otra manera de organizar los objetos de la escena y el juego para su juego por favor ¡compartir!

Así que, ¡descarga el código y dale una oportunidad! Aquí tenemos un buen comienzo para un juego – una torreta giratoria, toneladas de enemigos a disparar con diferentes cualidades, múltiples niveles, escenas de victoria/derrota y, por supuesto – ¡impresionantes efectos de sonido! ;]

¡Eso es todo!

Una vez más, si no lo han descargado aún, aquí está el proyecto de ejemplo con todo el código que hemos desarrollado hasta ahora.

Now that you know how to make a simple game, why not go a step further and learn about how to make a tile-based game in Cocos2D! After all, who doesn’t like Ninjas eating watermelons?

Ahora que sabes cómo hacer un juego sencillo, ¿por qué no ir un paso más allá y aprender acerca de ¡cómo hacer un juego basado en mosaicos (tile-based) utilizando Cocos2D!? Después de todo, ¿A que Ninja no le gusta comer melones?

Espero que hayan disfrutado de la serie, y ¡la mejor de las suertes en sus proyectos de juegos con Cocos2D!

Ray Wenderlich

Ray is an indie software developer currently focusing on iPhone and iPad development, and the administrator of this site. He’s the founder of a small iPhone development studio called Razeware, and is passionate both about making apps and teaching others the techniques to make them.

When Ray’s not programming, he’s probably playing video games, role playing games, or board games.

Comentarios de los Usuarios

0 Comment

Other Items of Interest

Newsletter Mensual de Ray

Suscríbete para recibir un boletín mensual con mis enlaces favoritos sobre programación, ¡y recibe un tutorial de longitud épica gratis como bonus!

¡Anúnciate con nosotros!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in September: iOS 8 App Extensions!

Sign Up - September

RWDevCon Conference?

We are considering having an official raywenderlich.com conference called RWDevCon in DC in early 2015.

The conference would be focused on high quality Swift/iOS 8 technical content, and connecting as a community.

Would this be something you'd be interested in?

    Cargando ... Cargando ...

Nuestros libros

Nuestro Equipo

Equipo de totorales

... 49 en total!

Update Team

  • Ray Fix

Equipo Editorial

... 23 en total!

Code Team

  • Orta Therox

... 3 en total!

Equipo de Traducción

  • Heejun Han
  • Wilson Lin

... 33 en total!

Expertos en la Materia

  • Richard Casey

... 4 en total!