tag:blogger.com,1999:blog-28523338359992911282024-02-20T03:33:56.915+01:00IndieVeloper: Tutoriales de Cocos2D en castellanoProgramación de juegos iOS y Cocos2D en xCode y Objective C, tutoriales, ayuda, snippets, recursos, ideas... ¡En Español!Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.comBlogger24125tag:blogger.com,1999:blog-2852333835999291128.post-51094769438723280732019-02-17T18:57:00.001+01:002019-02-17T18:57:08.078+01:00Me moví a crear otros blogs por placer y para el SEO<div dir="ltr" style="text-align: left;" trbidi="on">
Este blog lo abandoné hace muchísimos años, si queréis seguir leyéndome entonces visitad<br />
<br />
¿<a href="https://www.quepasaria.info/">Qué pasaría si</a>? En este blog podréis encontrar respuestas a preguntas del tipo:<br />
<ul>
<li><a href="https://www.quepasaria.info/no-pudieramos-dormir/">¿Qué pasa si no duermes?</a></li>
<li><a href="https://www.quepasaria.info/no-desayunas/">¿Qué pasa si no desayunas?</a></li>
<li><a href="https://www.quepasaria.info/duermo-mucho-las-9-posibles-causas/">¿Qué pasa si duermes mucho?</a></li>
<li><a href="https://www.quepasaria.info/no-se-recicla/">¿Qué pasa si no reciclamos?</a></li>
<li><a href="https://www.quepasaria.info/no-pago-una-multa-trafico/">¿Qué pasa si no pago una multa de tráfico?</a></li>
<li><a href="https://www.quepasaria.info/fumo-oregano/">¿Qué pasa si fumas orégano?</a></li>
<li>¿Qué pasa si tengo mi <a href="https://www.quepasaria.info/mi-colchon-se-hunde/">colchón hundido</a>?</li>
<li><a href="https://www.quepasaria.info/si-no-hubiera-agua/">¿Qué pasaría si no hubiera agua?</a></li>
<li><a href="https://www.quepasaria.info/cataluna-se-independizara/">¿Qué pasaría si Cataluña se independiza?</a></li>
</ul>
<br />
<a href="https://vitaminasyminerales.net/">Vitaminas y minerales</a><br />
En este blog tienes toda la información disponible sobre vitaminas y minerales, así como de <a href="https://vitaminasyminerales.net/macrominerales">macrominerales</a>, vitaminas hidrosolubles, etc.<br />
<pre class="brush:objc;"></pre>
</div>
Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-57557328886513238032012-05-17T13:28:00.001+02:002012-05-17T13:32:04.167+02:00La capa de un Super Héroe. Efecto bandera / Flag Effect<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaN7E4lMy9sEU_k0SOMUcvAL5roYE3LEZ-1svH4TQQXl7lzZ-yELce_DljPjvlibPs6vCodChynCYmJ4KJcTReBJ0orfOhqBuIDR21rpARFRiQJ5TW3cyo_lhWwNoID1V-yyWk_oS-VoBB/s1600/Capa-1-hd.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaN7E4lMy9sEU_k0SOMUcvAL5roYE3LEZ-1svH4TQQXl7lzZ-yELce_DljPjvlibPs6vCodChynCYmJ4KJcTReBJ0orfOhqBuIDR21rpARFRiQJ5TW3cyo_lhWwNoID1V-yyWk_oS-VoBB/s200/Capa-1-hd.png" width="200" /></a><br />
Recientemente ha salido al mercado el último juego desarrollado por Bluelephant, cuyo personaje está compuesto por varias imágenes para lograr una fluidez total en sus animaciones. Esto son las llamadas "Animaciones procedurales" las cuales no dependen de un grafista para diseñar la manera en la que corre un personaje, o vuela. De esta manera podemos hacer que cualquier cosa con 2 piernas, y dos brazos, corra de manera natural.<br />
<div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaN7E4lMy9sEU_k0SOMUcvAL5roYE3LEZ-1svH4TQQXl7lzZ-yELce_DljPjvlibPs6vCodChynCYmJ4KJcTReBJ0orfOhqBuIDR21rpARFRiQJ5TW3cyo_lhWwNoID1V-yyWk_oS-VoBB/s1600/Capa-1-hd.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><br /></a></div>
<div>
Pero el punto fuerte de esta entrada no son las animaciones procedurales, sino la animación de la capa en sí, ya que es una simple imagen, pero usando llamadas openGL sobre cocos2D para lograr el efecto ondulante.</div>
<div>
<br /></div>
<div>
<a name='more'></a></div>
<div>
En este tutorial vamos a enseñar como hacer una capa ondulante para poder añadir a cualquier héroe, y ya vuestra imaginación podrá también descubrir como hacer el efecto de una bandera, el cual es más simple (en lo que a cálculo matemático en el código se refiere)</div>
<div>
<br /></div>
<div>
Este es el efecto que conseguiremos:</div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/7p6Vd-HAoqQ?feature=player_embedded' frameborder='0'></iframe></div>
<div>
Antes de nada, recordar que Cocos2D tiene este mismo efecto, pero puesto que se puede aplicar a todas los sprites que estén como child dentro del nodo, su velocidad es NEFASTA. Tirando los FPS del juego por debajo de los 30fps tan pronto como se añaden dos o tres pequeños sprites a la escena. Por lo tanto, desaconsejo totalmente el uso de esa función en cocos2D y usar esta, para mejorar la velocidad global NOTABLEMENTE en el juego.<br />
<br />
Para empezar crearemos una clase SuperCapa.m con su correspondiente cabecera, SuperCapa.h.<br />
<br />
<pre class="brush:objc;">#import <foundation foundation.h="">
#import "cocos2d.h"
@interface SuperCapa : CCSprite {
int LADOS;
float t;
float velocidad;
float fps;
CGPoint texturePos[30];
}
-(void) setVelocidad:(float) v DT:(ccTime) fp;
@end</foundation></pre>
<br />
<br />
Tenemos un método para poder controlar la velocidad a la que se mueve y la frecuencia de actualización de la pantalla (los parámetros v y fp) por otro lado tenemos como variables de instancia LADOS que definirá como de "bien hecha" está la capa, es decir, por cuantos segmentos estará formada, t, que será el momento en el que se encuentra la animación y las posiciones de la textura que vamos a mover.<br />
<br />
Ahora vamos a ver la clase Super.m<br />
<br />
<pre class="brush:objc;">#import "SuperCapa.h"
@implementation SuperCapa
-(id) init{
if (self = [super init]) {
LADOS = 9;
int c = 0;
for (int i = 0; i<LADOS; i++) {
for (int j = 0; j<2; j++) {
texturePos[c] = ccp(((float) i/(float)(LADOS-1)),1-(float)j);
c = c + 1;
}
}
NSString* capa = @"Nombre-de-tu-capa.png" // DEBE DE SER CUADRADA Y POTENCIA DE 2!
[self setTexture:[[CCTextureCache sharedTextureCache] addImage:capa]];
t = 0;
}
return self;
}
-(void) dealloc{
[super dealloc];
}
-(void) setVelocidad:(float) v DT:(ccTime) fp{
fps = fp;
velocidad = v;
}
-( void )draw{
CGPoint segmentPos[LADOS*2];
float x,y;
int c = 0;
t = t + (fps*velocidad);
if (t>(M_PI*2))
t = t - (M_PI*2);
float porcentaje = 0;
for (int i = 0; i<LADOS; i++) {
for (int j = 0; j<2; j++) {
porcentaje = MAX(0,((float)(LADOS- (i+1))/LADOS));
x = i*(128/LADOS);
y = j*128 + cosf(t + (i*(2*M_PI/LADOS))) * porcentaje * 25;
segmentPos[c] = ccp(x,y);
segmentPos[c] = ccpAdd(ccp(-115,-64), segmentPos[c]); //My anchor point
c = c + 1;
}
}
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, [self texture].name );
glDisableClientState( GL_COLOR_ARRAY);
glTexCoordPointer( 2, GL_FLOAT, 0, texturePos );
glVertexPointer( 2, GL_FLOAT, 0, segmentPos );
glDrawArrays( GL_TRIANGLE_STRIP, 0, LADOS*2);
glEnableClientState( GL_COLOR_ARRAY ) ;
}
@end</pre>
<br />
<br />
La mágia está en el método draw, que es el método que estamos sobre escribiendo de la clase CCSprite, para poder animar nuestra capa a nuestro antojo. Como veis no tiene mucho misterio y el resultado es simple y optimizado.</div>Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com1tag:blogger.com,1999:blog-2852333835999291128.post-9243132583600828722012-05-09T16:47:00.000+02:002012-05-09T16:53:37.489+02:00Super Tomate<div id="fb-root">
</div>
<script>
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/es_ES/all.js#xfbml=1&appId=198063796981879";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivvxmSBQRAcDWB6I7oqQTxvnbp-JOo3u36psYiTAR3AIBKB2M4Li-iVvsjuiJZT4EoacV8uUc0JHoDBJoM7nrp_TIV72VagRCDJOFKTNsOtUfN3xDMYUKDnToMkFfjOKykKUJflPl3l1BU/s1600/iTunesArtwork.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivvxmSBQRAcDWB6I7oqQTxvnbp-JOo3u36psYiTAR3AIBKB2M4Li-iVvsjuiJZT4EoacV8uUc0JHoDBJoM7nrp_TIV72VagRCDJOFKTNsOtUfN3xDMYUKDnToMkFfjOKykKUJflPl3l1BU/s200/iTunesArtwork.jpg" width="200" /></a></div>
Super Tomato es un nuevo Super héroe, el cual nació en un plácido jardín, en una humilde planta tomatera... Un día, una lluvia de estrellas hizo que éste débil tomate creciera de manera distinta... Ese día... Super Tomate nació<br />
<br />
Pero esa lluvia de estrellas trajo consigo una gran invasión de máquinas del espacio, y Super Tomate no tiene otra opción que la de correr por salvar su propia vida.<br />
<br />
Corre, vuela, salta, lanza rayos. Este tomate lo tiene todo.<br />
<br />
<div class="fb-like" data-href="http://www.facebook.com/SuperTomatoGame" data-send="true" data-show-faces="true" data-width="450">
</div>
<br />
<a name='more'></a><br />
Qué encontrarás?<br />
<br />
- Excelentes gráficos 2D<br />
- Fácil de jugar, toca la pantalla para volar!<br />
- Cientos de combinaciones para vestir a tu Super Tomate!<br />
- Clasificación online a través de Game Center<br />
- Gráficos retina especialmente diseñados para iPhone 4/4S<br />
- Divertido y carismático personaje<br />
- Cámara lenta para emocionantes escenas de acción<br />
- Sube a Facebook tus mejores puntuaciones.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/BszxeCRxrdY?feature=player_embedded' frameborder='0'></iframe></div>
<br />Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-58109234407687832382011-10-14T21:32:00.000+02:002011-10-14T21:40:43.813+02:00Halloween Prank!<br />
<div class="separator" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;">
<img border="0" src="http://bluelephant.es/_Media/Halloween-Prank.png" /><strike></strike></div>
<br />
Nueva aplicación desarrollada, especialmente para el público americano, y su gran festividad de Halloween
<br />
<br />
<br />
¡La mejor aplicación para asustar a tus amigos!<br />
<br />
Con la llegada de Halloween muchas aplicaciones intentan crear una atmosfera de terror, unas tan sólo son una librería de sonidos, otras imágenes en pantalla que no interaccionan contigo. ¡"Halloween Prank" hace uso del micrófono para asustarte cuando pases por su lado haciendo ruido!<br />
<div class="fb-like-box" data-href="http://www.facebook.com/pages/Halloween-Prank/205100042897089" data-width="292" data-show-faces="false" data-stream="false" data-header="true"></div>
<br />
<a name='more'></a><div>
Imagina esta situación: </div>
<div>
Cuelgas tu iPad en la pared, y deja que cuando un amigo pase, al hacer ruido con sus pisadas o porque va hablando, ¡emita un sonido estridente que le asuste! </div>
<div>
<br /></div>
<div>
¡Pero no sólo es capaz de escuchar! ¡También te ve! Si posees un iPad 2, además esta aplicación hará uso de la cámara frontal, para que combinado con sonidos o imágenes de su alrededor, haga saltar la broma en el momento exacto. </div>
<div>
<br /></div>
<div>
*No uses esta aplicación con gente susceptible de sufrir infartos, o algún otro problema de salud. No nos hacemos responsables de los daños que pueda ocasionar.</div>
<br />
<br />
<br />Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com1tag:blogger.com,1999:blog-2852333835999291128.post-32221752450456982832011-09-20T14:23:00.000+02:002011-09-25T01:36:42.951+02:00ChipMunk, Cocos2D y Cuerpos Gelatinosos (Soft Body)He estado trabajando en un sustancioso tutorial, en el que he averiguado como hacer un cuerpo blando, gelatinoso, o simplemente del inglés "Soft Body". Viendo desde fuera un video en el que se muestran físicas de este estilo, cualquiera puede pensar que es una "BARBARIDAD" hablando en el sentido de dificultad. Pero nada más lejos... Crear un cuerpo gelatinoso no es tan dificil como lo parece.<br />
<div>
<br /></div>
<div>
En el tutorial de hoy, enseñaré como hacer un cuerpo Gelatinoso (Soft Body) bajo Cocos2D y chipmunk.</div>
<div>
<iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/7p6Vd-HAoqQ" width="560"></iframe>
<br />
<div>
<a name='more'></a>¿Cómo empezamos? </div>
<div>
Iniciaremos un proyecto con la plantilla (template) de Chipmunk en Cocos2D. Ahora, descargamos este archivo, que contiene las clases, encargadas de dibujas las formas y cuerpos de Chipmunk (Sólo para debug) http://www.megaupload.com/?d=X03R1WW6</div>
Una vez importados los dos archivos (<i>debugDraw.h</i> y <i>debugDraw.m</i> la clase helloworld layer, la borramos entera, y la dejamos tal que así.</div>
<div>
<pre class="brush:objc;">//
// HelloWorldLayer.m
// Chipmunk
//
// Created by Daniel López Sánchez on 10/09/11.
// Copyright Bluelephant 2011. All rights reserved.
//
// Import the interfaces
#import "HelloWorldLayer.h"
static void eachShape(void *ptr, void* unused)
{
cpShape *shape = (cpShape*) ptr;
cpBody *body = shape->body;
CCSprite *sprite = body->data;
if( sprite ){
//Sólo actualizo la posición, porque de la rotación, nos encargaremos nosotros "a mano"
[sprite setPosition: body->p];
}
}
// HelloWorldLayer implementation
@implementation HelloWorldLayer
+(CCScene *) scene{
CCScene *scene = [CCScene node];
HelloWorldLayer *layer = [HelloWorldLayer node];
[scene addChild: layer];
return scene;
}
-(void) addNewSpriteFlubX:(float)x y:(float)y{
Rondo *rondo = [[Rondo alloc] initWithPosition:ccp(x,y) withWorld:space];
rondo.tag = 1;
[self addChild:rondo];
}
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init] )) {
self.isTouchEnabled = YES;
glClearColor(0.5, 0.5, 0.5, 1);
CGSize wins = [[CCDirector sharedDirector] winSize];
cpInitChipmunk();
cpBody *staticBody = cpBodyNew(INFINITY, INFINITY);
space = cpSpaceNew();
cpSpaceResizeStaticHash(space, 400.0f, 40);
cpSpaceResizeActiveHash(space, 100, 600);
space->elasticIterations = space->iterations;
cpShape *shape;
// bottom
shape = cpSegmentShapeNew(staticBody, ccp(0,0), ccp(wins.width*CC_CONTENT_SCALE_FACTOR(),0), 0.0f);
shape->e = 1.0f; shape->u = 1.0f;
cpSpaceAddStaticShape(space, shape);
// top
shape = cpSegmentShapeNew(staticBody, ccp(0,wins.height*CC_CONTENT_SCALE_FACTOR()), ccp(wins.width*CC_CONTENT_SCALE_FACTOR(),wins.height*CC_CONTENT_SCALE_FACTOR()), 0.0f);
shape->e = 1.0f; shape->u = 1.0f;
cpSpaceAddStaticShape(space, shape);
// left
shape = cpSegmentShapeNew(staticBody, ccp(0,0), ccp(0,wins.height*CC_CONTENT_SCALE_FACTOR()), 0.0f);
shape->e = 1.0f; shape->u = 1.0f;
cpSpaceAddStaticShape(space, shape);
// right
shape = cpSegmentShapeNew(staticBody, ccp(wins.width*CC_CONTENT_SCALE_FACTOR(),0), ccp(wins.width*CC_CONTENT_SCALE_FACTOR(),wins.height*CC_CONTENT_SCALE_FACTOR()), 0.0f);
shape->e = 1.0f; shape->u = 1.0f;
cpSpaceAddStaticShape(space, shape);
space->gravity = ccp(0,-900);
[self schedule: @selector(step:)];
}
return self;
}
- (void) dealloc
{
cpSpaceFree(space);
space = NULL;
[super dealloc];
}
-(void) step: (ccTime) delta
{
int steps = 2;
CGFloat dt = delta/(CGFloat)steps;
for(int i=0; i<steps; i++){
cpSpaceStep(space, dt);
}
cpSpaceHashEach(space->activeShapes, &eachShape, nil);
cpSpaceHashEach(space->staticShapes, &eachShape, nil);
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for( UITouch *touch in touches ) {
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
location = ccpMult(location, CC_CONTENT_SCALE_FACTOR());
[self addNewSpriteFlubX: location.x y:location.y];
}
}
- (void)draw {
drawSpaceOptions options = {
0, // drawHash
0, // drawBBs,
1, // drawShapes
0, // collisionPointSize
0, // bodyPointSize,
0 // lineThickness
};
drawSpace(space, &options);
}
@end</pre>
¿Qué es lo que he hecho en este código? Simplemente crear un mundo con cuatro paredes (en el método init) las cuales se corresponden con los bordes de la pantalla del dispositivo. Y además he creado un delegado de TouchesEnded, en el cual, creo un nuevo esprite tipo "Rondo"</div>
El sprite es una clase heredada de CCSprite, cuya implementación, vemos aquí:
<br />
<pre class="brush:objc;">//
// Rondo.m
// DebugDraw
//
// Created by Daniel López Sánchez on 13/09/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "Rondo.h"
#define LADOS 14
@implementation Rondo
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
}
return self;
}
-(id) initWithPosition:(CGPoint) p withWorld:(cpSpace*) w{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
//[super init]
if( (self=[super initWithFile:[[NSBundle mainBundle] pathForResource:@"Ball" ofType:@"png"]])) {
RADIO = 40*CC_CONTENT_SCALE_FACTOR();
self.opacity = 0;
skin = [self texture];
world = w;
//self.position = p;
cpFloat centralMass = 1.0f/LADOS;
centralBody = cpBodyNew(centralMass, cpMomentForCircle(centralMass, 0, RADIO, cpvzero));
centralBody->p = p;
cpSpaceAddBody(world, centralBody);
centralBodyShape = cpCircleShapeNew(centralBody, RADIO, cpvzero);
centralBodyShape -> layers = 1;
cpSpaceAddShape(world, centralBodyShape);
centralBodyShape->data = self;
int i;
muelles = [[NSMutableArray alloc] initWithCapacity:LADOS];
edgeMass = 1.0/LADOS;
edgeDistance = 2.0*RADIO*cpfsin(M_PI/(cpFloat)LADOS);
_edgeRadius = edgeDistance/2.0;
cpFloat coeficienteEstrujamiento = 0.1;
cpFloat fuerzaMuelle = 60;
cpFloat retrocesoMuelle = 0.75;
for (i=0; i<LADOS; i++) {
cpVect dir = cpvforangle((cpFloat)i/(cpFloat)LADOS*2.0*M_PI);
cpVect offset = cpvmult(dir, RADIO);
cpBody* body = cpBodyNew(edgeMass, INFINITY);
body->p = cpvadd(centralBody->p, offset);
cpSpaceAddBody(world, body);
cpShape *shape = cpCircleShapeNew(body, _edgeRadius, cpvzero);
shape -> layers = 2;
shape -> u = 1;
shape -> e = 0.5;
cpSpaceAddShape(world, shape);
cpConstraint *jointDef = cpSlideJointNew(centralBody,body, offset, cpvzero, 0, RADIO*coeficienteEstrujamiento);
cpSpaceAddConstraint(world,jointDef);
cpVect springOffset = cpvmult(dir, RADIO + _edgeRadius + 4);
cpConstraint *dampedDef = cpDampedSpringNew(centralBody, body, springOffset,cpvzero, 10, fuerzaMuelle,retrocesoMuelle);
cpSpaceAddConstraint(world,dampedDef);
m_perimeterBodies[i] = body;
cpBodyApplyImpulse(body, cpv(40,40),cpv(0,0));
}
for (i=0;i<LADOS;i++){
cpBody *muelle = m_perimeterBodies[i];
cpBody *muelleB = m_perimeterBodies[(i+1)%LADOS];
cpConstraint *jointDef = cpSlideJointNew(muelle,muelleB, cpvzero, cpvzero, 0, edgeDistance);
cpSpaceAddConstraint(world,jointDef);
}
m_perimeterBodies[LADOS] = m_perimeterBodies[0];
m_perimeterBodies[LADOS+1] = m_perimeterBodies[1];
cpBodyApplyImpulse(centralBody, cpv(0,200),cpv(0,40));
}
return self;
}
@end
</pre>
Sin olvidar ambas cabeceras, helloworldlayer.h y Rondo.h
<br />
<pre class="brush:objc;">//
// Rondo.h
// DebugDraw
//
// Created by Daniel López Sánchez on 13/09/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "cocos2d.h"
#import "chipmunk.h"
@interface Rondo : CCSprite {
CCTexture2D *skin;
float PTM_RATIO;
cpSpace* world;
cpBody *centralBody;
cpShape *centralBodyShape;
NSMutableArray *muelles;
cpBody* m_perimeterBodies[16];
float RADIO;
cpFloat edgeMass;
cpFloat edgeDistance;
cpFloat _edgeRadius;
}
-(id) initWithPosition:(CGPoint) p withWorld:(cpSpace*) w;
@end</pre>
<pre class="brush:objc;">//
// HelloWorldLayer.h
// Chipmunk
//
// Created by Daniel López Sánchez on 10/09/11.
// Copyright __MyCompanyName__ 2011. All rights reserved.
//
// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
#import "drawSpace.h"
#import "Rondo.h"
// Importing Chipmunk headers
#import "chipmunk.h"
// HelloWorldLayer
@interface HelloWorldLayer : CCLayer
{
cpSpace *space;
}
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
-(void) step: (ccTime) dt;
@end</pre>
En este punto, si ejecutamos la aplicación, veremos esto:
<br />
<div>
<iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/4chDdlZZUvE" width="420"></iframe>
<br />
<div>
<br /></div>
<div>
¿Entendeis lo que sucede? He creado un cuerpo, formado por varios elementos, entre ellos existen muelles y juntas. Los muelles van desde el centro de la forma, hasta los extremos, y para mantener la forma circular, las uniones unen los extremos entre sí, dos a dos. Desde ya, parece tener cierto "peso" y cierta deformación. Pero el efecto se amplifica si le pegamos una textura, dándole como coordenadas los propios extremos del objeto.</div>
<div>
<br /></div>
<div>
¿Cómo pegar una textura a mano?</div>
<div>
Bien, todos los métodos que poseen una representación gráfica, tienen un método Draw (Si no la tiene la clase en sí, la tiene algún padre). Así que partiendo de esta base, podemos "sobreescribir" el método padre, por el nuestro. Y tenemos que añadir el siguietne método a la clase Rondo.</div>
<div>
<br /></div>
<div>
<div class="p1">
<pre class="brush:objc;">
-(float) anguloBase{
cpBody *muelle = m_perimeterBodies[0];
CGPoint e = ccp(muelle->p.x,muelle->p.y);
CGPoint c = ccp(centralBody->p.x,centralBody->p.y);
return ccpAngleSigned(e, c);
}
-( void )draw {
CGPoint segmentPos[ LADOS + 2 ];
CGPoint texturePos[ LADOS + 2 ];
CGPoint textureCenter;
float angle, baseAngle;
segmentPos[ 0 ] = CGPointZero;
for ( int count = 0; count < LADOS; count ++ ) {
//Multiplica por un factor de escala, para pegar la textura, acorde con la forma (esto se hace mejor a mano), así que para mi ejemplo, el valor 1.3f va muy bien.
segmentPos[ count + 1 ] = ccpMult( ccpSub( m_perimeterBodies[ count ]->p, centralBody->p ), 1.3f );
}
segmentPos[ LADOS + 1 ] = segmentPos[ 1 ];
// Indicamos los puntos de la textura
for ( int count = 0; count < ( LADOS + 2 ); count ++ ){
segmentPos[count] = ccpAdd(segmentPos[count], ccp((RADIO-_edgeRadius+1)*2,(RADIO-_edgeRadius+1)*2));
}
// Dibujamos la textura, en los extremos de la forma.
// Angulo base, nos devuelve el ángulo de la forma, referente a dos puntos. El extremo número 0 y el cuerpo central.
baseAngle = [self anguloBase];
texturePos[ 0 ] = CGPointZero;
for ( int count = 0; count < LADOS; count ++ ) {
angle = baseAngle + ( 2 * M_PI / LADOS * count );
texturePos[ count + 1 ].x = sinf( angle );
texturePos[ count + 1 ].y = cosf( angle );
}
texturePos[ LADOS + 1 ] = texturePos[ 1 ];
textureCenter = CGPointMake( 0.5f, 0.5f );
for ( int count = 0; count < ( LADOS + 2 ); count ++ )
texturePos[ count ] = ccpAdd( ccpMult( texturePos[ count ], 0.5f ), textureCenter );
//Rutinas OPENGL para dibujar la textura.
glColor4ub( 255, 255, 255, 255 );
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, [skin name] );
glDisableClientState( GL_COLOR_ARRAY ),
glTexCoordPointer( 2, GL_FLOAT, 0, texturePos );
glVertexPointer( 2, GL_FLOAT, 0, segmentPos );
glDrawArrays( GL_TRIANGLE_FAN, 0, LADOS + 2 );
glEnableClientState( GL_COLOR_ARRAY ) ;
}</pre>
<br /></div>
<div class="p1">
<br /></div>
<div class="p1">
Y terminado esto, lo que obtendremos es lo enseñado en el primer video.</div>
<div class="p1">
¡A disfrutar toqueteando los valores de las variables en los muelles!<br />
<br />
IMPORTANTE! La textura que selecciones DEBE ser POTENCIA de 2 (64, 128, 256, 512, 1024)</div>
</div>
</div>
Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com11tag:blogger.com,1999:blog-2852333835999291128.post-56789296115934823482011-09-15T12:11:00.000+02:002011-09-15T12:14:20.089+02:00Crazy Jungle: Review<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://a340.phobos.apple.com/us/r1000/109/Purple/72/d4/70/mzl.dhlzadsp.170x170-75.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://a340.phobos.apple.com/us/r1000/109/Purple/72/d4/70/mzl.dhlzadsp.170x170-75.jpg" /></a><span class="Apple-style-span" style="background-color: white; font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 13px; line-height: 18px;">Crazy Jungle es un juego tipo puzzle en el que se mezcla los estilos arcade, habilidad y lógica. Dando importancia a los gráficos, música y sonidos. Apto para todos los miembros de la familia, reúne tres estilos de juego. </span></div>
<span class="Apple-style-span" style="background-color: white; font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 13px; line-height: 18px;"><br style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" />Arcade: Enfrentados el bien y el mal, ayuda al bien, impulsando animales resultantes de unir un mínimo de 3 de ellos del mismo tipo. Compárate con el resto del mundo en el Ranking mundial. <br style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" /><br style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" />Endless: Juega sin límite de tiempo, sin restricciones, sin agobios. Un modo práctica o de relajación. Juega tanto como quieras. </span><br />
<a name='more'></a><span class="Apple-style-span" style="background-color: white; font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 13px; line-height: 18px;">Time Attack: Lucha en contra del tiempo, cuanto más tardes en unir tres animales, antes tus animales perecerán de frío. Evita que tus animales se congelen para poder continuar haciendo enlaces. Compárate con el resto del mundo en el Ranking mundial. </span><br />
<div>
<span class="Apple-style-span" style="background-color: white; font-family: Verdana, Helvetica, Arial, sans-serif; font-size: x-small; line-height: 18px;"><br /></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://itunes.apple.com/es/app/crazy-jungle/id463199205?mt=8&ls=1"><img border="0" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRzM72OlUTioNVFStqQ1BLgRHeU_hfjB4tX5JxT8epo6U1pB0fH3Wf1hnVc0bhX1B0EFL7HiqmCOFr346QvWcea18g8px6fDQ4QqhQtDH1bcfiEAfJnhyphenhypheny_wkWyZoQ_stUUt1X03Ln/s320/App_Store_Badge_EN+resize.png" width="320" /></a></div>
<span class="Apple-style-span" style="background-color: white; font-family: Verdana, Helvetica, Arial, sans-serif; font-size: x-small; line-height: 18px;"><br /></span><br />
<span class="Apple-style-span" style="background-color: white; font-family: Verdana, Helvetica, Arial, sans-serif; font-size: x-small; line-height: 18px;"><br /></span></div>
<div>
<span class="Apple-style-span" style="background-color: white; font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 13px; line-height: 18px;"><span class="Apple-style-span" style="font-family: Times; font-size: small; line-height: normal;"><span class="Apple-style-span" style="background-color: white; font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 13px; line-height: 18px;">Características: <br style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" />- Gráficos de alta resolución (Retina HD), tanto para iPad, como para iPhone 4 <br style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" />- Música y sonidos soberbios <br style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" />- Ranking mundial <br style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" />- 60fps de juego en todos los dispositivos <br style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" />- Integrado con Game Center </span><span class="Apple-style-span" style="background-color: white; font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 13px; line-height: 18px;">- Aplicación universal, juega dónde quieras, iPhone, iPad e iPod.</span></span></span><br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/4HogiFr9Dgs?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
</div>
Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-33101373921636314632011-09-09T15:15:00.000+02:002011-09-10T18:45:56.847+02:00Texture Packer: Facilita la creación de tus juegos<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZvUpZm_IhIjv079eP8ib92CXlSWzr2exrcXFVXSSQ27QJoQMqfpLUIo8X0WNH1HRWtNq2YmPMw8ZLlaK0h1vOgRTcOtkZjZv4bOOcFsWQIRkM3ILM2n4xjVuT4FA7K4eOevJPAePDPr_1/s1600/TP_512.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZvUpZm_IhIjv079eP8ib92CXlSWzr2exrcXFVXSSQ27QJoQMqfpLUIo8X0WNH1HRWtNq2YmPMw8ZLlaK0h1vOgRTcOtkZjZv4bOOcFsWQIRkM3ILM2n4xjVuT4FA7K4eOevJPAePDPr_1/s200/TP_512.png" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Texture Packer</td></tr>
</tbody></table>
Hoy vengo a hablaros de un magnífico programa, el cual uso para crear mis juegos más rápidamente, mejor, y que además, estén mejor optimizados. Os presento... <a href="http://www.texturepacker.com/">Texture Packer</a>!<br />
<br />
Anteriormente, cuando quería crear un juego en HD, debía de crear tanto las imagenes en alta resolución, así como las imágenes de menor resolución, indicadas para los antiguos dispositivos, tales como iPod 2g o iPhone 3gs y anteriores. Era un fastidio tener que reescalar todas las imagenes al 50% una a una. Texture Packer, te soluciona este problema, ¡creándote un SpriteSheet para cada resolución! Sinceramente, de todas las características que tiene (que son muchas, y muy útiles) concretamente esta es la que más me ha llamado la atención. ¡Te hace trabajar la mitad!
<br />
<a name='more'></a><br />
La interfaz es clara e intuitiva, tan sólo debes abrir tus imagenes y verás como aparecen automáticamente ordenadas en tu nuevo "SpriteSheet"
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVoAQ4Cnd3kiGmWSN-apd7tW5rGhPbXTNunn6k-uw3UmFF2_9qEq2fR6mvyU4AG-Nb12vdzHDhT3wqcpNfx4f-Q3pEfgSqVbD6ydxBMyuhPSavttr11GAmQ6x_dI-7RVK_4-NaAFXgWIlQ/s1600/Captura+de+pantalla+2011-09-09+a+las+15.00.40.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="428" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVoAQ4Cnd3kiGmWSN-apd7tW5rGhPbXTNunn6k-uw3UmFF2_9qEq2fR6mvyU4AG-Nb12vdzHDhT3wqcpNfx4f-Q3pEfgSqVbD6ydxBMyuhPSavttr11GAmQ6x_dI-7RVK_4-NaAFXgWIlQ/s640/Captura+de+pantalla+2011-09-09+a+las+15.00.40.png" width="640" /></a></div>
<br />
<br />
Entre más opciones tienes la posibilidad de seleccionar el espacio entre sprites, el espacio de los sprites al borde, o incluso el "Extrude" que resulta muy útil cuando trabajamos con sprites muy ajustados en tamaño, y tenemos pequeños defectos en los bordes del sprite.<br />
<br />
Además, Texture Packer permite rotar los Sprites de manera que quepan mejor en tu SpriteSheet, comunicandole a Cocos2D, que la imagen ha sido rotada para una mejor distribución, por lo que aunque tu veas que tus sprites no están derechos, Cocos2D los usará correctamente.<br />
<br />
Quizás, la más interesante que posee Texture Packer sea la de exportar tu SpriteSheet al formato PVR. Es el formato de imagen más rápido, tanto para la carga, como movimiento en pantalla de los sprites en iOS. Convertir un PNG a este formato es un quebradero de cabeza, porque rara vez damos con las opciones adecuadas para su correcta visualización en iOS. Pero ese problema se acaba con Texture Packer, porque desde mi punto de vista, es el mejor, y quizás único programa capaz de llevar a cabo esta función de manera tan rápida y eficaz.<br />
<br />
Por si fuera poco, y como colocón final, no sólo es compatible con Cocos2D, sino también con Corona SDK, Sparrow y otros tantos Frameworks.<br />
<br />
De primera mano, os puedo decir que cualquier entusiasta programador de juegos, debería tener este gran complemento para programar tus juegos. Ahorra tiempo, ahorra memoria, mejora el rendimiento de tu iDevice usando los PVR creados con Texture Packer, te ayudará a raspar esos FPS que les falta a tus juegos para ser perfectos.<br />
<br />
¡Ah! Premultiply alpha, mejorará la visualización de tus imágenes si guardas en formato PVR. Esta opción evita que salgan extraños artefactos en los bordes de las imágenes<br />
<br />
¡Ahora ya sabeis cómo los gráficos que tengo en mi blog están tan ordenados y sabeis cómo hacerlos!<br />
<br />
Visita la página oficial de <a href="http://www.texturepacker.com/">Texture Packer</a>, comprueba por tí mismo todas sus posibilidades.<br />
<br />
Mi más sincera enhorabuena p<span class="Apple-style-span" style="font-family: 'Droid Sans','Myriad Pro',Helvetica,Arial,sans-serif;"><span class="Apple-style-span" style="font-size: 14px; line-height: 22px;">ara Andreas Löw, creador de esta magnífica utilidad.</span></span>Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com1tag:blogger.com,1999:blog-2852333835999291128.post-56245580451874303612011-09-08T22:57:00.001+02:002011-09-08T22:58:11.424+02:00Crazy JunglePor fin he acabado el juego que tenía entre manos (uno de ellos) y puedo escribir con satisfacción, que mi juego saldrá a la venta pronto, en cuanto Apple confirme que mi aplicación es válida.
<iframe width="560" height="345" src="http://www.youtube.com/embed/4HogiFr9Dgs" frameborder="0" allowfullscreen></iframe>
<a name='more'></a>
¿Qué os ofrece este juego?
- Aplicación universal, soporta gráficos HD para iPhone 4 e iPad. Así como gráficos SD para los demás dispositivos.
- Experiencia de juego a 60 fps en todos los dispositivos
- Buena Música
- Ranking mundial
- Sonidos cómicos
- Horas de juego
Y todo ello por sólo 0.79€ (0.99$)Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-82694513316999281052011-08-30T22:55:00.006+02:002011-09-01T15:07:27.552+02:00Herencia y clases abstractas en Objective C<div class="separator" style="clear: both; text-align: center;"><a href="http://mcrispino.files.wordpress.com/2009/11/xcode.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"><img border="0" height="256" width="256" src="http://mcrispino.files.wordpress.com/2009/11/xcode.png" /></a></div>¿Qué es la herencia?<br />
<br />
Supongamos que queremos tener un array al que llamaremos "Zoologico". Y que este array sólo puede contener "Animales".<br />
<br />
Ahora supongamos que queremos implementar un mono, un elefante y un pájaro, como clases. Todos ellos son animales, por tanto, tienen propiedades comunes a todos los animales, pero cada animal, tiene sus "diferencias". Por ejemplo, el pájaro tiene alas, y el mono no. O el elefante tiene trompa, y el pájaro un pico. Imaginaos que queremos tener una representación gráfica distinta para cada animal, por tanto debemos crear una clase Animal, del tipo CCSprite.<br />
<br />
<pre class="brush:objc;">@interface Animal: CCSprite {
int numeroDePatas;
bool tienePlumas;
bool tienePelo;
bool tienePico;
bool tieneTrompa;
bool tieneAlas;
}
-(void) saluda;
@protocol Especializacion
-(void) desplazate;
@end
</pre><br />
<a name='more'></a><br />
<br />
<pre class="brush:objc;">@implementation Animal
-(id) init
{
if( (self=[super init])) {
}
return self;
}
-(void) saluda{
NSLog(@"%@",@"Soy un animal");
}
-(void) dealloc{
[super dealloc];
}
</pre><br />
A grandes rasgos, hemos creado un animal (a falta de poner otras muchas caracteristicas). La cual tiene informacion general sobre un animal.<br />
<br />
Ahora queremos implementar, una clase Mono, que herede de Animal.<br />
<br />
<pre class="brush:objc;">@interface Mono : Animal Especializacion{
// Aquí pondríamos variables que nos interesen
}
-(void) cuelgaDeArboles;
@end
</pre><br />
Y en el módulo de la clase, deberemos implementar los procedimientos de "Protocol" y si queremos, "sobreescribir" los métodos de la clase Animal, desde una clase hijo.<br />
<br />
<pre class="brush:objc;">@implementation Mono
-(id) init
{
if( (self=[super init])) {
//Cargo la imagen del mono
numeroDePatas = 4;
tienePlumas = false;
tienePelo = true;
tienePico = false;
tieneTrompa = false;
tieneAlas = false;
}
return self;
}
-(void) saluda{ //He sobreescrito el método saluda de Animal.
NSLog(@"%@",@"¡Soy un mono!");
}
-(void) dealloc{
[super dealloc];
}
-(void) desplazate{
//Me desplazo como un mono
}
-(void) cuelgaDeArboles{
//Bla bla bla...
}
</pre><br />
Ahora voy a implementar lo que sería un elefante.<br />
<br />
<pre class="brush:objc;">@interface Elefante : Animal Especializacion{
// Aquí pondríamos variables que nos interesen
}
@end
</pre><br />
Y en el módulo de la clase, deberemos implementar los procedimientos de "Protocol" y si queremos, "sobreescribir" los métodos de la clase Animal, desde una clase hijo.<br />
<br />
<pre class="brush:objc;">@implementation Elefante
-(id) init
{
if( (self=[super init])) {
//Cargo la imagen del elefante
numeroDePatas = 4;
tienePlumas = false;
tienePelo = false;
tienePico = false;
tieneTrompa = true;
tieneAlas = false;
}
return self;
}
-(void) dealloc{
[super dealloc];
}
-(void) desplazate{
//Me desplazo como un elefante
}
</pre><br />
Si observas, el método Saluda de Animal, ha sido reemplazado por el método saluda de Mono, pero no en la clase elefante. ¿Que significa eso?<br />
<br />
<pre class="brush:objc;">Mono *mono = [[Mono alloc] init];
Elefante* elefante = [[Elefante alloc] init];
[elefante saluda]; //La salida en pantalla será, "Soy un animal"
[mono saluda]; //La salida en pantalla será, "¡Soy un mono!"
</pre><br />
¿Qué tiene esto de bueno? Pues que por ejemplo, si tuvieramos alguna restricción de tipos a la hora de guardar un elemento en un array, y no pudieramos guardar clases de distinto tipo en el array... Pues esto sería solución. Ya que la clase "padre" de todo mono, elefante, o pájaro, es "Animal". Por lo que el array que tuviesemos que definir, lo definiriamos para "Animal".<br />
<br />
PD: Cada vez que he escrito Especializacion en @implementation, va entre <>. <especializacion><br />
<br />
Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com1tag:blogger.com,1999:blog-2852333835999291128.post-62409512445932071762011-08-17T16:39:00.001+02:002011-08-26T21:58:21.812+02:00Preparando otro tutorialAgosto está en su punto medio y las vacaciones llegando a su fin... Mi tiempo libre se agota y empezaré con las clases y exámenes... Espero poder acabar este tutorial que tengo entre manos, que será un intento de "Match 3" estilo Columns, o Bejeweled, aún no lo tengo claro. Los gráficos serán muy "bonitos" y estarán mucho más animados que el anterior tutorial, ya que serán montados por piezas. Un animalito estará formado por 2 ojos, algunos por dos orejas, otros con colas, otros con alas, cuernos, etc...<br />
<br />
Gracias a esto, podré explicar un poco la herencia en Objective C. Puesto que cada uno es un "Monstruo" tendrán "habilidades" y "propiedades" comunes, independientemente de cuantas patas, ojos, colas, alas, tengan.<br />
<br />
Para prueba, un video:<br />
<iframe width="425" height="349" src="http://www.youtube.com/embed/9jAzC2yqrUg" frameborder="0" allowfullscreen></iframe><br />
<br />
¡Permaneced atentos!<br />
<br />
Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-56360214961447441512011-08-16T01:21:00.002+02:002011-08-16T01:23:22.853+02:00Ya está a la venta "Smash Them!"<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKAReoWAYG0quXGrlLAS7qHrfkfVE0SgDaAygCWelOxEsyOcxAYHpNgkkSRZuzuybIOwxXR5XUlk-4tOZla3-f8qGj-Mh8MjgCfwqzrnt6lSVp4SjlruwIbrB4vn48kLykmsMnpsVKfVuM/s1600/app-store-400x400.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"><img border="0" height="240" width="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKAReoWAYG0quXGrlLAS7qHrfkfVE0SgDaAygCWelOxEsyOcxAYHpNgkkSRZuzuybIOwxXR5XUlk-4tOZla3-f8qGj-Mh8MjgCfwqzrnt6lSVp4SjlruwIbrB4vn48kLykmsMnpsVKfVuM/s320/app-store-400x400.png" /></a></div>Smash Them! ha sido aprobado por Apple para ser vendido en la App Store. El juego no es más que un ejemplo de cómo desarrollar para iPhone y además, una manera de hacer un pequeño donativo por la publicación del código fuente. El proceso de verificación de esta aplicación ha llevado en total 5 días. Cada uno de los procesos por los que ha pasado la aplicación han sido: Waiting for review, In review, Processing for App Store, y finalmente, Ready for sale. El tiempo que ha tardado cada uno ha sido:<br />
<br />
Waiting for review: <i>5 días</i><br />
In review: <i>5 horas</i><br />
Preparing for App Store: <i>20 minutos</i><br />
<br />
<a name='more'></a><br />
El tiempo de In review, depende mucho de tu aplicación, una aplicación simple como la mía no toma más de 5 horas, pero sin embargo, si la aplicación es compleja, puede llegar a prolongarse hasta varias semanas. Una vez aprobada la aplicación, estará disponible para vender en menos de una hora.<br />
<br />
Las razones por las que Apple no autorice la venta de tu aplicación en su tienda, son varias, desde el uso de API's no autorizadas, hasta temáticas de la aplicación poco "elegantes"<br />
<br />
Si quieres ayudarme a seguir creando tutoriales, o si te sientes agradecido, compra el juego, aunque lo puedas compilar tu mismo desde este blog: <a href="http://itunes.apple.com/us/app/smash-them-all/id455967497?l=es&ls=1">Descarga Smash Them!</a>Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com2tag:blogger.com,1999:blog-2852333835999291128.post-91553958385886945492011-08-11T12:50:00.001+02:002011-08-15T19:19:14.819+02:00Tutorial Cocos2D: Juego completo (parte 4)Hoy escribo la última entrada referente al primer juego completo publicado en el blog. De momento lo hemos tocado todo, desde la lógica principal del juego, hasta las animaciones. Pero aún queda una cosa esencial en todo juego... <b>El menú</b><br />
<br />
Para ello he creado un menú totalmente simple, con una imagen de fondo y tan sólo dos opciones. Jugar y visitar el código fuente del juego (ya que tengo pensado publicar el juego a modo de "donación" por un precio de 79 céntimos).<br />
<br />
Las letras del menú estarán animadas para dar un poco más de vida, y además, mostraremos la puntuación más alta obtenida en nuestras partidas anteriores.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyjM7amg5ykC15ROFkRvU0AK1hdkzx1SfhHz7cFD7sZPJ8dp5G802srsBGuCAAd1WkFJMo-a8iOhMbYz3sLm9lHhk-JjcI7OdL49g-YoFJ5D98Dycv20Xtc5v6UmhHDSHCa0VpbhqnGMQb/s1600/Inicio.png" imageanchor="1" style=""><img border="0" height="214" width="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyjM7amg5ykC15ROFkRvU0AK1hdkzx1SfhHz7cFD7sZPJ8dp5G802srsBGuCAAd1WkFJMo-a8iOhMbYz3sLm9lHhk-JjcI7OdL49g-YoFJ5D98Dycv20Xtc5v6UmhHDSHCa0VpbhqnGMQb/s320/Inicio.png" /></a></div><br />
<a name='more'></a><br />
<br />
En la definición de la cabecera de la clase "MenuScene" no debemos definir nada, salvo importar la clase "JuegoLayer" ya que necesitamos esta clase, para iniciar el juego.<br />
<br />
<pre class="brush:objc;">#import <Foundation/Foundation.h>
#import "JuegoLayer.h"
#import "cocos2d.h"
@interface MenuScene : CCLayer {}
+(CCScene *) scene;
@end
</pre><br />
Y ahora, toca implementar el menú. Como ya he dicho, necesitaremos poca cosa, un CCMenu, dos CCMenuItemLabel, un CCSprite como fondo, un CCShaky3D (para la animación del menú), y selectores para cada pulsación de los elementos del menú.<br />
<br />
<pre class="brush:objc;">#import "MenuScene.h"
@implementation MenuScene
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
MenuScene *layer = [MenuScene node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// Aquí montaremos el menú, encajando cada pieza en su sitio
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if((self=[super init])) {
CCSprite *fondo = [CCSprite spriteWithFile: [[NSBundle mainBundle] pathForResource:@"Inicio" ofType:@"png"]];
fondo.position = ccp(240,160);
[self addChild:fondo];
//Defino el botón Jugar, poniendole como texto "Play", fuente "Arial", tamaño 30 y además,
//El selector que indicará que acción realizará al ser pulsado
CCMenuItemLabel *jugar = [CCMenuItemLabel itemWithLabel:[CCLabelTTF labelWithString:@"Play" fontName:@"Arial" fontSize:30] target:self selector:@selector(jugarPulsado)];
//Igual que he definido el botón Jugar, defino el de ir a esta página web.
CCMenuItemLabel *informacion = [CCMenuItemLabel itemWithLabel:[CCLabelTTF labelWithString:@"Source Code" fontName:@"Arial" fontSize:24] target:self selector:@selector(website)];
//Cambiando el anchorPoint de esta manera, obtenemos un texto alineado a la derecha
jugar.anchorPoint = ccp(jugar.anchorPoint.x*2,0);
informacion.anchorPoint = ccp(informacion.anchorPoint.x*2,0);
//Una vez definidos los botones, los añadimos a un "CCMenu" y lo posicionamos
CCMenu *menu = [CCMenu menuWithItems:jugar, informacion, nil];
[menu alignItemsVertically];
menu.position = ccp(465,25);
[self addChild:menu];
//Este es el efecto que tendrán las letras del menú
CCShaky3D *efecto = [CCShaky3D actionWithRange:5 shakeZ:NO grid: ccg(4,4) duration:5];
[menu runAction:[CCRepeatForever actionWithAction:efecto]];
self.isTouchEnabled = YES;
int puntuacionAnt;
if([[NSUserDefaults standardUserDefaults] valueForKey:@"Score"] != nil) {
// The key existed...
puntuacionAnt = [[NSUserDefaults standardUserDefaults] integerForKey:@"Score"];
}
else {
puntuacionAnt = 0;
}
CCLabelTTF *puntuacionAnterior = [CCLabelTTF labelWithString:[NSString stringWithFormat:@"Puntuación: %d",puntuacionAnt] fontName:@"Arial" fontSize:24];
puntuacionAnterior.anchorPoint = ccp(0,0);
[self addChild:puntuacionAnterior];
puntuacionAnterior.position = ccp(100,150);
}
return self;
}
-(void) jugarPulsado{
CCTransitionFlipY *action = [CCTransitionFlipY transitionWithDuration:1 scene:[HelloWorldLayer scene]];
[[CCDirector sharedDirector] replaceScene:action];
}
-(void) website{
//¡Así se abre una página web desde un juego!
NSString *link = @"http://indie-veloper.blogspot.com/2011/08/tutorial-cocos2d-juego-completo-parte-1.html";
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:link]];
}
@end</pre><br />
Y con esta última entrada, se acaba el tutorial de un juego completo bajo Cocos2D<br />
<br />
<a href="http://indie-veloper.blogspot.com/p/codigo-fuente-y-proyecto-xcode-de-smash.html">Código Fuente</a><br />
Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com5tag:blogger.com,1999:blog-2852333835999291128.post-40590090307571471962011-08-10T16:46:00.002+02:002011-08-11T12:52:31.041+02:00Tutorial Cocos2D: Juego completo (parte 3)Y ahora, el tutorial está llegando a su fin, esta es la tercera parte de un total de cuatro partes, y en esta en concreto, explicaré la lógica de juego, la detección de toques en la pantalla, el "montaje" del escenario, barras de progreso (CCProgressTimer) y como crear unas partículas en Cocos2D.<br />
<br />
Como de costumbre, escribiré aquí la cabecera de la clase "JuegoLayer" la cual contendrá información sobre todo lo relativo al juego, como monstruos, animaciones y puntuaciones.<br />
<br />
<iframe width="560" height="349" src="http://www.youtube.com/embed/roj_tAPA_Ec" frameborder="0" allowfullscreen></iframe><br />
<br />
<a name='more'></a><br />
<br />
<pre class="brush:objc;">#import "cocos2d.h"
#import "Monstruo.h"
#import "MenuScene.h"
#import "Mazo.h"
@interface JuegoLayer : CCLayer{
CCSpriteBatchNode *graficos; //Este el el spriteBatchNode donde está todo el escenario y monstruos
CCSpriteBatchNode *graficosMazo; //Con este spriteBatchNode, tenemos el mazo
CCSprite *fondo; //El sprite que contiene el fondo
NSMutableArray *hoyos; //Array que guarda los 6 hoyos
NSMutableArray *monstruos; //Array que guarda los 6 monstruos
Mazo *mazo; //Array que guarda el mazo creado
CCProgressTimer *tiempo; //Barra de progreso que muestra cuando acabará el juego
int entrePuntuaciones; //
int multiplicador; // Variables relacionadas
int score; // con las puntuaciones
CCLabelTTF *etiquetaPuntuacion; //
}
+(CCScene *) scene;
@end
</pre><br />
Definida la cabecera, debemos crear ahora los métodos y procedimientos que harán uso de estas variables. Metodos que debemos implementar... Podrían ser algunos para crear el escenario, otro para controlar los toques, otros para la puntuación, otros para detectar la "colisión", etc..<br />
<br />
Aquí va la implementación:<br />
<br />
<pre class="brush:objc;">#import "JuegoLayer.h"
@implementation JuegoLayer
//Aquí creamos un monstruo en cierta posición con cierta escala, para facilitar la tarea de crear 6 monstruos
-(void) creaMonstruo:(NSString*) tipo Posicion: (CGPoint) pos Escala: (float) escala{
Monstruo *monstruo = [Monstruo spriteWithSpriteFrameName:tipo];
monstruo.position = pos;
monstruo.scale = escala;
[monstruos addObject:monstruo];
[graficos addChild:monstruo];
[monstruo marcaOrigen];
}
//Creamos la porción de tierra que va a tapar al monstruo cuando se esconde en el hoyo
-(void) creaHoyo:(NSString*) tipo Posicion: (CGPoint) pos {
CCSprite *hoyo = [CCSprite spriteWithSpriteFrameName:tipo];
hoyo.position = pos;
[hoyos addObject:hoyo];
[graficos addChild:hoyo];
}
//Montaje del escenario
-(void) montaEscenario{
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[[NSBundle mainBundle] pathForResource:@"Sprites" ofType:@"plist"]];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[[NSBundle mainBundle] pathForResource:@"Mazo" ofType:@"plist"]];
graficos = [CCSpriteBatchNode batchNodeWithFile:[[NSBundle mainBundle] pathForResource:@"Sprites" ofType:@"png"]];
graficosMazo = [CCSpriteBatchNode batchNodeWithFile:[[NSBundle mainBundle] pathForResource:@"Mazo" ofType:@"png"]];
[self addChild: graficos];
[self addChild: graficosMazo];
fondo = [CCSprite spriteWithSpriteFrameName:@"Escenario.png"];
fondo.position = ccp(240,160);
[graficos addChild:fondo];
[self schedule:@selector(update:) interval:1.0f/30.0f];
[self creaMonstruo:@"Naranja.png" Posicion:ccp(97.8,145) Escala:0.8f];
[self creaMonstruo:@"Azul.png" Posicion:ccp(249,145) Escala:0.8f];
[self creaMonstruo:@"Verde.png" Posicion:ccp(397.0,145) Escala:0.8f];
[self creaHoyo:@"Hoyo-1.png" Posicion:ccp(97.8,150)];
[self creaHoyo:@"Hoyo-2.png" Posicion:ccp(249,150)];
[self creaHoyo:@"Hoyo-3.png" Posicion:ccp(397.0,150)];
[self creaMonstruo:@"Rosa.png" Posicion:ccp(97.25,30) Escala:0.9f];
[self creaMonstruo:@"Naranja.png" Posicion:ccp(248,30) Escala:0.9f];
[self creaMonstruo:@"Azul.png" Posicion:ccp(398.8,30) Escala:0.9f];
[self creaHoyo:@"Hoyo-4.png" Posicion:ccp(97.25,43.5)];
[self creaHoyo:@"Hoyo-5.png" Posicion:ccp(248,43)];
[self creaHoyo:@"Hoyo-6.png" Posicion:ccp(398.8,43)];
CCParticleSnow *particulas = [[CCParticleSnow alloc] initWithTotalParticles:150];
particulas.tag = 1337;
particulas.autoRemoveOnFinish = YES;
particulas.position = ccp(240,330);
particulas.speed = 55;
particulas.life = 8;
particulas.lifeVar = 0;
particulas.texture = [[CCTextureCache sharedTextureCache] addImage:[[NSBundle mainBundle] pathForResource:@"particle-stars" ofType:@"png"]];
[self addChild:particulas];
tiempo = [CCProgressTimer progressWithFile:[[NSBundle mainBundle] pathForResource:@"Progreso" ofType:@"png"]];
tiempo.position = ccp(240, 300);
tiempo.type = kCCProgressTimerTypeHorizontalBarLR;
tiempo.percentage = 100;
[self addChild:tiempo];
etiquetaPuntuacion = [CCLabelTTF labelWithString:@"0" fontName:@"Arial" fontSize:32];
etiquetaPuntuacion.position = ccp(450,30);
etiquetaPuntuacion.anchorPoint = ccp(etiquetaPuntuacion.anchorPoint.x*2,etiquetaPuntuacion.anchorPoint.y);
[self addChild:etiquetaPuntuacion];
}
//Actualizar puntuacion tras acabar la partida, creando un valor en el diccionario
-(void) actualizaPuntuacion{
int puntuacionAnterior=0;
if([[NSUserDefaults standardUserDefaults] valueForKey:@"Score"] != nil) {
// Si la entrada existe, entonces actualizamos el valor
puntuacionAnterior = [[NSUserDefaults standardUserDefaults] integerForKey:@"Score"];
if (puntuacionAnterior < score){
[[NSUserDefaults standardUserDefaults] setInteger:score forKey:@"Score"];
//Tells the userDefaults to update the data base.
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
else {
[[NSUserDefaults standardUserDefaults] setInteger:score forKey:@"Score"];
//Tells the userDefaults to update the data base.
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
//En este procedimiento, actualizamos la barra de progreso.
-(void)update:(ccTime)dt
{
tiempo.percentage = tiempo.percentage - 0.085f;
entrePuntuaciones = entrePuntuaciones + 1;
if (entrePuntuaciones > 30){
entrePuntuaciones = 30;
multiplicador = 1;
}
if (tiempo.percentage <= 0){
[self actualizaPuntuacion];
[self unscheduleAllSelectors];
for (CCNode* nodo in [self children]) {
[nodo unscheduleAllSelectors];
[nodo removeFromParentAndCleanup:YES];
}
CCTransitionFade *action = [CCTransitionFade transitionWithDuration:2 scene:[MenuScene scene]];
[[CCDirector sharedDirector] replaceScene:action];
}
}
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
- (void) cleanupSprite:(CCSprite*)inSprite
{
[self removeChild:inSprite cleanup:YES];
}
-(void) puntuacion:(int) p Posicion: (CGPoint) pos{
CCLabelTTF *punt = [CCLabelTTF labelWithString:[NSString stringWithFormat:@"%d",p] fontName:@"Marker Felt" fontSize:22];
[punt setOpacity:0];
[punt setPosition:pos];
// start the destroy process
id action1 = [CCFadeTo actionWithDuration:0.425 opacity:255];
id action2 = [CCFadeTo actionWithDuration:0.425 opacity:0];
id action3 = [CCMoveBy actionWithDuration:0.425 position:ccp(0,40)];
id action5 = [CCMoveBy actionWithDuration:0.425 position:ccp(0,40)];
id action4 = [CCSpawn actions:action1, action3, nil];
id action6 = [CCSpawn actions:action2, action5, nil];
id cleanupAction = [CCCallFuncND actionWithTarget:self selector:@selector(cleanupSprite:) data:punt];
id seq = [CCSequence actions:action4, action6, cleanupAction, nil];
[self addChild:punt];
[punt runAction:seq];
score = score + p;
[etiquetaPuntuacion setString:[NSString stringWithFormat:@"%d",score]];
tiempo.percentage = tiempo.percentage + (p/25);
}
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
//Habilitamos la pantalla táctil
self.isTouchEnabled = YES;
monstruos = [[NSMutableArray alloc] init];
hoyos = [[NSMutableArray alloc] init];
[self montaEscenario];
}
return self;
}
-(bool) compruebaGolpe:(CGPoint) golpe{
Monstruo* m;
bool golpeado = false;
int i;
if (golpe.y>192){
for (i = 0; i < 3; i++){
m = [monstruos objectAtIndex:i];
if (CGRectContainsPoint([m boundingBox], golpe) && ![m estaMuerto]) {
[m recibeGolpe];
golpeado = true;
[self puntuacion:100*multiplicador-(ccpDistance(golpe, m.position)) Posicion:golpe];
[m aumentaNivel:score];
}
}
}else if(golpe.y>72){
for (i = 3; i < 6; i++){
m = [monstruos objectAtIndex:i];
if (CGRectContainsPoint([m boundingBox], golpe) && ![m estaMuerto]) {
[m recibeGolpe];
golpeado = true;
[self puntuacion:100*multiplicador-(ccpDistance(golpe, m.position)) Posicion:golpe];
[m aumentaNivel:score];
}
}
}
return golpeado;
}
//Comprobamos los toques que se realizan en la pantalla.
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSSet *allTouches = [event allTouches];
for (UITouch *touch in allTouches) {
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
mazo = [Mazo animacion];
[graficosMazo addChild: mazo];
mazo.position = ccpAdd(location,ccp(-15,50));
if ([self compruebaGolpe: location]){
multiplicador++;
entrePuntuaciones = 0;
}
else{
tiempo.percentage = tiempo.percentage - 3;
multiplicador = 0;
entrePuntuaciones = 30;
}
}
[self reorderChild:[self getChildByTag:1337] z:1000];
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[[self getChildByTag:1337] dealloc];
[monstruos dealloc];
[hoyos dealloc];
[super dealloc];
}
@end
</pre>
Continúa leyendo la cuarta parte del <a href="http://indie-veloper.blogspot.com/2011/08/tutorial-cocos2d-juego-completo-parte-4.html">Tutorial Cocos2D en Español</a>Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-2781240187034130352011-08-09T19:33:00.001+02:002011-08-10T16:48:03.644+02:00Tutorial 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 <b><i>@selector</i></b> y reproducir efectos de sonido.<br />
<br />
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.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidlZK44yzhxnRVPTgfAZaebLsXHZZrb8MjpGRxexNzlvBaW3RekUmwWXPPOExxxsHEdkTVuTyHY9gxXVqST4GFhezOzoe_QKZT2TJ3a6B6BGAICdn_mYzsUKmalz-eLrE8xLAehm8ssQQA/s1600/Sprites.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="160" width="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidlZK44yzhxnRVPTgfAZaebLsXHZZrb8MjpGRxexNzlvBaW3RekUmwWXPPOExxxsHEdkTVuTyHY9gxXVqST4GFhezOzoe_QKZT2TJ3a6B6BGAICdn_mYzsUKmalz-eLrE8xLAehm8ssQQA/s320/Sprites.png" /></a></div><br />
<a name='more'></a><br />
<br />
<pre class="brush:objc;">#import <Foundation/Foundation.h>
#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
</pre><br />
Ahora hagamos la implementación de la clase Monstruo.<br />
<br />
<pre class="brush:objc;">#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</pre><br />
Me gustaría aclarar de todo el código que significa y qué valores dar a pitch, pan, y gain<br />
Gain es el volumen, y un valor de 1 lo mantiene igual, 2 lo duplica y 0.5 lo reduce a la mitad.<br />
Pitch es el tono, un valor de 2 lo hará más agudo, y por de bajo de 1 más grave<br />
Pan es el "estéreo" 0 quiere decir que saldrá por ambos auriculares, -1 por el izquierdo totalmente y 1 por el derecho.<br />
<br />
Y para realizar esta clase, necesitáis el archivo de imagenes y propiedades, que como es habitual, subo para vosotros.<br />
<br />
<a href="http://www.megaupload.com/?d=NE182NPA">Imágenes gratis</a> / <a href="http://www.megaupload.com/?d=NE182NPA">Free Sprites</a><br />
<br />
Continua leyendo la parte 3 del <a href="http://indie-veloper.blogspot.com/2011/08/tutorial-cocos2d-juego-completo-parte-3.html">Tutorial de cocos2D</a><br />
Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-8999421783831175002011-08-07T15:45:00.005+02:002011-08-10T01:07:35.302+02:00Tutorial Cocos2D: Juego completo (parte 1)El ansiado tutorial está llegando a su fin, para no demorarlo ni crear una entrada tan larga, voy a dividir el tutorial en varias partes (creo que 3 o 4). Esta primera parte estará compuesta por la clase "Mazo" la cual os enseñará como crear animaciones con sprites.<br />
<br />
Para empezar una clase, debemos tener claro como estructurarla y que función debe desempeñar. Esta clase que he diseñado, lo único que hace es crear un mazo. Un mazo no es otra cosa que un Sprite animado, por lo que es lógico que deba heredar de la clase CCSprite. Para ello debemos indicarlo en el <b><i>@interface</i></b><br />
<br />
<br />
<pre class="brush:objc;">#import <Foundation/Foundation.h>
#import "cocos2d.h"
@interface Mazo : CCSprite //Aquí nuestro mazo, hereda las propiedades de un sprite
{}
+(id) animacion; //Con este procedimiento, creamos un mazo (en lugar de init), que tendrá la propiedad "autorelease"
@end
</pre><br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgItAzmjeVBKpoK7c51zjC3iiHW66GISDHbL_GJjlWrjKNzUtiTPbrwyXAH-dt48Ip2f_JHPjS1spIKSoYGq_0YI5OMEX96b9u5QSBdw6jYIo-Wey2wy1gjXUjoB_N1LzG5n0Cy-7mxfqOs/s1600/Mazo.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="320" width="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgItAzmjeVBKpoK7c51zjC3iiHW66GISDHbL_GJjlWrjKNzUtiTPbrwyXAH-dt48Ip2f_JHPjS1spIKSoYGq_0YI5OMEX96b9u5QSBdw6jYIo-Wey2wy1gjXUjoB_N1LzG5n0Cy-7mxfqOs/s320/Mazo.png" /></a></div><a href="http://www.megaupload.com/?d=V69RVZ3G">Animación Mazo</a><br />
<a name='more'></a><br />
<br />
<pre class="brush:objc;">#import "Mazo.h"
@implementation Mazo
+(id) animacion
{
return [[[self alloc] init] autorelease];
}
-(void) creaAnimacion{
NSMutableArray *arrayAnimacion = [NSMutableArray array];
for(int i = 1; i <= 9; ++i) {
[arrayAnimacion addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:@"Mazo00%d.png", i]]];
}
for(int i = 10; i <= 20; ++i) {
[arrayAnimacion addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:@"Mazo0%d.png", i]]];
}
CCAnimation *animacion = [CCAnimation animationWithFrames:arrayAnimacion delay:1.0f/60.0f];
CCAnimate *accionAnimacion = [CCAnimate actionWithAnimation:animacion restoreOriginalFrame:NO];
CCSequence *secuencia = [CCSequence actions:accionAnimacion, [CCCallFunc actionWithTarget:self selector:@selector(elimina)],nil];
[self runAction:secuencia];
}
-(void) elimina{
[self removeFromParentAndCleanup:YES];
}
// on "init" you need to initialize your instance
-(id) init
{
if( (self=[super init])) {
[self setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"Mazo001.png"]];
[self creaAnimacion];
}
return self;
}
-(void) dealloc{
[super dealloc];
}
@end
</pre>
Como véis, el Mazo se crea con "Autorelease" lo cual hace que no tengamos que preocuparnos de desalojar la memoria que ocupa. Teóricamente él sólo se "destruirá" después de completar la animación, gracias a esta línea:
<pre class="brush:objc;">CCSequence *secuencia = [CCSequence actions:accionAnimacion, [CCCallFunc actionWithTarget:self selector:@selector(elimina)],nil];
[self runAction:secuencia];</pre>Y será eliminado porque ningún puntero está apuntando a esa dirección de memoria.
Para que la clase al cargar las imágenes no de error, antes debemos de "cachear" las imágenes en la clase padre (esta clase padre será donde se desarolle todo el juego, y la mostraré en la tercera parte del tutorial):
<pre class="brush:objc;">[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[[NSBundle mainBundle] pathForResource:@"Mazo" ofType:@"plist"]];
graficosMazo = [CCSpriteBatchNode batchNodeWithFile:[[NSBundle mainBundle] pathForResource:@"Mazo" ofType:@"png"]];
[self addChild: graficosMazo];
</pre>
Debemos hacerlo en el metodo init, para así asegurarnos de que la carga de las imagenes (cacheo) se hace una vez por inicio de nivel.
Continuar leyendo la segunda parte del <a href="http://indie-veloper.blogspot.com/2011/08/tutorial-cocos2d-juego-completo-parte-2.html">Tutorial Cocos2D</a>Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com7tag:blogger.com,1999:blog-2852333835999291128.post-90353102789891618102011-08-06T02:00:00.000+02:002011-08-06T02:00:45.768+02:00El tutorial está llegando a su fínEste fin de semana me he puesto manos a la obra para empezar (y casi acabar) el tutorial que quería publicar desde hace un par de días. El tiempo sigue siendo escaso y el esfuerzo en crear los gráficos y buscar sonidos y música es grande en comparación con la programación que debemos realizar. Cocos2D es muy simple, pero a la vez muy eficaz. <br />
<br />
Tengo pensado publicar TODO lo realizado para el tutorial, es decir, tanto las imágenes como el código fuente y el proyecto. Por sí a alguien no le queda bien claro lo descrito en la entrada del blog.<br />
<br />
Para ir dejando un buen sabor de boca os comentaré que es lo que llevo implementado.<br />
<br />
Música: usaré la librería Cocos Dension.<br />
HD y SD: Para retina y sin retina.<br />
Sprite Batch: un png de 2048 x 1024 para almacenar todos los sprites.<br />
Controles táctiles: detectaremos uno o varios toques.<br />
Animaciones: las usaremos para mover sprites (y darles vida con CCAnimate).<br />
Partículas: Todavía por implementar<br />
Menu: Todavía por implementar<br />
Nombre: Aún no está claro :D<br />
<br />
Para los impacientes, además dejo un video.<br />
<br />
<iframe width="560" height="349" src="http://www.youtube.com/embed/Is5xjmBLdm0" frameborder="0" allowfullscreen></iframe>Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-48586321940069711362011-08-05T00:24:00.002+02:002011-08-09T19:37:33.645+02:00Preparando algo especialHoy y mañana estaré bastante ocupado, pero estoy preparando un tutorial para hacer un juego completo hecho en Cocos2D. Estoy haciendo en los pocos ratos libres que tengo los gráficos que usaré. Tengo pensado tocar los siguientes temas en el tutorial:<br />
<br />
Música: usaré la librería Cocos Dension.<br />
Sprite Batch: un png de 1024 x 1024 para almacenar todos los sprites.<br />
Controles táctiles: detectaremos uno o varios toques.<br />
Animaciones: las usaremos para mover sprites.<br />
Partículas: usaremos las partículas predefinidas de Cocos2D<br />
<br />
Con todos estos elementos ya podremos crear un juego bastante simple y educativo, ¡Permaneced atentos!Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-40640051742130633842011-08-03T16:49:00.003+02:002011-08-03T16:58:30.203+02:00Multiples "dedos" o "toques" o "touches"Hoy voy a mostrar un simple ejemplo de como detectar cuantos dedos hay en la pantalla. Es un poco más "complejo" que obtener información del sensor del acelerómetro, pero sigue siendo sencillo para todos.<br />
<br />
Comenzando desde un proyecto nuevo en COCOS2D y eliminando el contenido del hello world (como ya hicimos en el <a href="http://indie-veloper.blogspot.com/2011/08/acelerometro-y-cocos2d.html">tutoria acelerómetro</a>) tenemos que añadir las siguientes líneas de código:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVijfzraDT4_lRQV6BC03klUFKE0tNd8rYGk0gBDplo239hyphenhyphena8b369S6edo7iWqNY7zFTrqkzfNYMlARvXq7CPZOWp8-K5KBkrCS1aGdH3guies26VgrIXJxVw0SgbslQUHwTzyTwOMLgZ/s1600/Captura+de+pantalla+2011-08-03+a+las+16.54.21.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"><img border="0" height="170" width="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVijfzraDT4_lRQV6BC03klUFKE0tNd8rYGk0gBDplo239hyphenhyphena8b369S6edo7iWqNY7zFTrqkzfNYMlARvXq7CPZOWp8-K5KBkrCS1aGdH3guies26VgrIXJxVw0SgbslQUHwTzyTwOMLgZ/s320/Captura+de+pantalla+2011-08-03+a+las+16.54.21.png" /></a></div><br />
<a name='more'></a><br />
<pre class="brush:objc;">// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
//Habilitamos la pantalla táctil
self.isTouchEnabled = YES;
//Creamos un label para mostrar información
etiqueta = [CCLabelTTF labelWithString:@"No se detecta ningún dedo" fontName:@"Papyrus" fontSize:34];
etiqueta.position = ccp(240,160);
[self addChild:etiqueta];
}
return self;
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}
</pre><br />
Con esto hemos creado la etiqueta (label) que nos indicará cuantos dedos tenemos en la pantalla, y además hemos habilitado la pantalla para "poder tocarla" y reconocer los "toques". Justo después he creado los 3 procedimientos necesarios (deben tener el mismo nombre que yo os pongo). Como el nombre indica, cada uno es lanzado cuando se produce un inicio, un movimiento, y un fin en cada detección.<br />
<br />
Por otro lado, queremos que detecte MULTIPLES dedos, por lo que debemos ir nuestro AppDelegate.m y añadir la siguiente linea, justo después de inicializar el glview<i></i><br />
<br />
<pre class="brush:objc;"><[glView setMultipleTouchEnabled:YES];</pre>
Por último sólo queda "rellenar" los procedimientos de ccTouches:
<pre class="brush:objc;">-(void)actualizaEtiquetaConTexto:(NSString *) txt Dedos:(int) dedos{
[etiqueta setString:[NSString stringWithFormat:@"%d dedo(s) detectado(s): %@",dedos, txt]];
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self actualizaEtiquetaConTexto:@"Inicio" Dedos:[touches count]];
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[self actualizaEtiquetaConTexto:@"Movimiento" Dedos:[touches count]];
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self actualizaEtiquetaConTexto:@"Fin" Dedos:[touches count]];
}</pre><br />
Y eso es todo por hoy, otro tutorial simple. Poco a poco iré profundizando e incluso haré un pequeño juegoDaniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-14746290143071918472011-08-02T15:22:00.006+02:002011-08-02T16:52:39.457+02:00Acelerómetro y Cocos2DHoy enseñaré como usar el acelerómetro y además, a elegir una imagen de un sprite, acorde con el ángulo del iPhone.<br />
<br />
Para ello, necesitaremos esta flecha que he diseñado yo mismo (para vuestro uso y tutorial)<br />
<a href="http://www.megaupload.com/?d=TORNMUMJ">Descarga flecha animada</a> / <a href="http://www.megaupload.com/?d=TORNMUMJ">Download animated arrow</a><br />
<br />
será algo tal que así:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBvwG734fXumkddvuIAxDAuDOS0PnLmkQmPCXdvQhXXDaClJ-_jIKr58irTwEp8I8jbgIJv2K8ZK6yid0x1zYycuspf4BtU_bXhuVU4zx73aIJaNWTguDo24jSefJDY50gTm0YI_pLF9xn/s1600/Flecha.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"><img border="0" height="160" width="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBvwG734fXumkddvuIAxDAuDOS0PnLmkQmPCXdvQhXXDaClJ-_jIKr58irTwEp8I8jbgIJv2K8ZK6yid0x1zYycuspf4BtU_bXhuVU4zx73aIJaNWTguDo24jSefJDY50gTm0YI_pLF9xn/s320/Flecha.png" /></a></div>Cómo veis es una imagen parecida a la que subí anteriormente, por lo que el ZIP de Megaupload contiene las imágenes en HD y SD así como los archivos correspondientes "plist"<br />
<br />
<br />
<br />
<br />
<br />
Y este será el resultado final del tutorial:<br />
<iframe width="425" height="349" src="http://www.youtube.com/embed/QXOJVCTdpZo" frameborder="0" allowfullscreen></iframe><br />
<a name='more'></a><br />
<br />
Empecemos con el tutorial. Primero bajad la imagen mencionada y a continuación, cread un proyecto Cocos2D.<br />
<br />
Una vez creado, arrastrar la carpeta que se encuentra dentro del zip a la carpeta resources de tu proyecto. Cuando te pregunte <i>"Copy items into destination group's folder (if needed)"</i> marca el casillero.<br />
<br />
Si todo ha salido bien, ahora tendremos un HelloWorldLayer.m. De esta clase no necesitamos el init tal y como está, así que lo eliminamos y lo reemplazamos por el siguiente.<br />
<pre class="brush:objc;">// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
//Habilitamos el uso del acelerómetro
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[[NSBundle mainBundle] pathForResource:@"Flecha" ofType:@"plist"]]; /Carga del fichero plist, que contiene información relativa a la posición de los sprites
flecha = [CCSprite spriteWithSpriteFrameName:@"Flecha090.png"];
flecha.position = ccp(240,160);
self.isAccelerometerEnabled = YES;
[self addChild:flecha];
}
return self;
}
- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration{}</pre><br />
Si os fijáis he añadido aquí la carga del plist (al cargar el plist, automáticamente se cargan todos los sprites del PNG, clasificados por nombre con el siguiente formato: FlechaXXX.png), he añadido además un procedimiento "accelerometer". Aquí en este procedimiento escribiremos la lógica para rotar el sprite de la flecha, así como asignar la imagen que debemos mostrar.<br />
<br />
Como es de esperar, este procedimiento es el que maneja los eventos del acelerómetro. Cómo queremos añadir movimiento a nuestro sprite, en relación a la inclinación del iPhone, debemos realizar aquí la mayor parte (o toda) de la lógica del código.<br />
<br />
Así que... Vamos a rellenar ese espacio vacío.<br />
<pre class="brush:objc;">- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration{
[flecha setRotation: (atan2f(acceleration.x, acceleration.y)*360.0f)/(2.0f*PI)];
int imagen = [self normaliza:acceleration.z];
if (imagen < 10){
[flecha setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:@"Flecha00%d.png",imagen]]];
}else if(imagen<100){
[flecha setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:@"Flecha0%d.png",imagen]]];
}else{
[flecha setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:@"Flecha%d.png",imagen]]];
}
}</pre>
La función normalizar, no hace otra cosa que convertir la gravedad, en un ángulo (por así decirlo) y como sólo tenemos 180 imagenes (una imágen para cada grado) la función retorna un ángulo entre 180 y 1.
<pre class="brush:objc;">-(float) normaliza:(float) ang{
int tmp = (ang + 1.0f) * 90;
tmp = MIN(180,tmp);
tmp = MAX(1,tmp);
return tmp;
}</pre>Y por último, añadimos al header la siguiente línea
<pre class="brush:objc;">// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
// HelloWorldLayer
@interface HelloWorldLayer : CCLayer
{
CCSprite * flecha; //Esta línea debemos añadir
}
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
@end</pre><br />
Y eso es todo por hoy. Seguiré mis labores y la creación de mi juego.<br />
<br />
¡Que lo disfrutéis!<br />
<br />
PD: Se puede mejorar usando filtros matemáticos para "suavizar" el movimiento de la flecha, pero eso ya lo dejo a vuestro cargo ;)Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com1tag:blogger.com,1999:blog-2852333835999291128.post-44640158899836852732011-08-01T17:54:00.004+02:002011-08-02T12:45:47.416+02:00¿Synthesize? Objective-C te ayuda con getters y settersMucho he tardado en entender de una manera clara que es @synthesize en Objective C. Y por fin sé que es, y puedo explicarlo de una manera clara. No sé si me topé con una mala ayuda, o simplemente tuve varios días malos, pero por fín lo tengo claro.<br />
<br />
Aquí os dejo la "ayudita" de hoy.<br />
<br />
Supongamos que tenemos una clase "Persona". La cual tendrá Nombre, Apellidos y edad como variables de clase. Tendremos pues, un header definido de la siguiente manera:<br />
<br />
<pre class="brush:objc;">@interface Persona{
NSString *nombre;
NSString *apellidos;
int edad;
}
</pre><br />
<a name='more'></a><br />
<br />
Ahora, tendríamos que definir en nuestro modulo (el Persona.m) un getter y un setter para cada una de las variables. Es decir, deberíamos de tener algo así:<br />
<br />
<pre class="brush:objc;">#import "Persona.h"
@implementation Persona
-(id) init
{
if( (self=[super init])) {
//Inicialización
}
return self;
}
-(NSString*) getNombre{
return nombre;
}
-(void) setNombre:(NSString*) nuevoNombre{
nombre = nuevoNombre;
}
//... Y así sucesivamente para las demás propiedades
@end
</pre><br />
Lo cual, si tenemos muchas variables que deben de ser accesibles, puede ser un poco tedioso implementar cada uno de los procedimientos y funciones.<br />
<br />
Para ellos Objective-C te ofrece las directivas @property y @synthesize. Para hacerlo funcionar tan sólo debemos añadir al header lo siguiente:<br />
<br />
<pre class="brush:objc;">@interface Persona{
NSString *_nombre;
NSString *_apellidos;
int _edad;
}
@property (nonatomic, getter=getNombre, setter=setNombre) NSString* nombre;
@property (nonatomic, getter=getApellidos, setter=setApellidos) NSString* apellidos;
@property (nonatomic, getter=getEdad, setter=setEdad) int edad;
</pre><br />
Y en el módulo de la clase lo siguiente:<br />
<br />
<pre class="brush:objc;">#import "Persona.h"
@implementation Persona
@synthesize nombre = _nombre;
@synthesize apellidos = _apellidos;
@synthesize edad = _edad;
-(id) init
{
if( (self=[super init])) {
//Inicialización
}
return self;
}
//... ¡Y no hay que implementar nada más!
@end
</pre><br />
Ya tan sólo deberías de acceder a las variables de la siguiente manera:<br />
<pre class="brush:objc;">Persona * persona = [[Persona alloc] init];
[persona setNombre: @"Daniel"];
[persona setApellidos: @"López Sánchez"];
[persona setEdad: 25];
//Y para recibir información:
int edad = [persona getEdad];
</pre><br />
Y eso es todo por hoy.Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com6tag:blogger.com,1999:blog-2852333835999291128.post-72822678671898393622011-07-31T22:29:00.001+02:002011-07-31T22:31:05.293+02:00¡Animación gratis para tu juego!Hoy no he podido actualizar el blog ni daros nada interesante a lo largo del día, puesto que es un domingo y no lo he trabajado :D<br />
<br />
Hoy os dejo aquí un pequeño apaño, arreglo o ayuda para vuestro juego. No es gran cosa, no soy un grafista profesional, pero algo es algo.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWB1leJsSoDlOePkH3H36x0kXmk3rGfLhFyCGW9XRBidRkJY9k4TF9HSLPcnhEZAoTjsbga8oOMSnhVoGr316KrZjLvvKM0y7jqcaVMqnd_-Kt8B5r1gRQj4PBCQHuZ5ooeGYSl5RM-BA-/s1600/Estrella.png" imageanchor="1" style=""><img border="0" height="128" width="128" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWB1leJsSoDlOePkH3H36x0kXmk3rGfLhFyCGW9XRBidRkJY9k4TF9HSLPcnhEZAoTjsbga8oOMSnhVoGr316KrZjLvvKM0y7jqcaVMqnd_-Kt8B5r1gRQj4PBCQHuZ5ooeGYSl5RM-BA-/s320/Estrella.png" /></a></div><br />
Cómo veréis, no es más que una imagen PNG (con transparencias) acompañado de un <i>plist</i>. El archivo podéis conseguirlo desde uno de los servidores gratuitos que ocasionalmente usamos para subir nuestros pinitos, y en este caso lo alojaré en <i>Megaupload</i><br />
<br />
<a href="http://www.megaupload.com/?d=HNX405DA">Descarga animación gratis</a> / <a href="http://www.megaupload.com/?d=HNX405DA">Download free animation</a><br />
<br />
La razón por la que os "regalo" esta animación es porque la tengo para mi juego y aún no sé si usarlo como bonus, no usarlo, o tirarlo a la papelera :P Así que, por si acaso he hecho trabajo en vano, os lo doy. Casi lo olvido... Tiene la versión HD y la versión SD (iPhone 4 para HD y resto de dispositivos sin Retina display para la versión SD). ¡Disfrutadlo!Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-78137751986408497142011-07-30T12:56:00.001+02:002011-08-01T18:16:11.519+02:00Distancia de un punto a una recta<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKMuFMfRoY8j7R9GRXyE31RZ7CXFOzJYAy458IiJNdXC4KV5TrL3RnooQ2tAaZgXJG5REW898bMhaUvL8_T3ThYRJBGfv_IyNQeWvbffSFoXWybgOf4wOV1NTLOHh4ZajK4cNTyoaJGoQU/s1600/graph3a.gif" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="219" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKMuFMfRoY8j7R9GRXyE31RZ7CXFOzJYAy458IiJNdXC4KV5TrL3RnooQ2tAaZgXJG5REW898bMhaUvL8_T3ThYRJBGfv_IyNQeWvbffSFoXWybgOf4wOV1NTLOHh4ZajK4cNTyoaJGoQU/s320/graph3a.gif" width="255" /></a></div><div style="text-align: justify;">Bueno, en mis desarrollos de juegos/aplicaciones/chorradas muchísimas veces he tenido que hallar la distancia entre un punto y una recta. Siempre he acabado solucionándolo de una forma u otra. Pero siempre tenia las tediosas divisiones por cero... Tenía que comprobar siempre que el denominador fuera distinto de cero. O si era próximo a cero la precisión en la distancia se veía un poco comprometida... Debido a la precisión de los Float.</div><br />
<br />
<div style="text-align: justify;">La fórmula es la llamada "Fórmula de Heron" y se basa en la obtención del área de un triángulo.</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;"><br />
</div><div style="text-align: justify;">x1, y1, x2, y2: Definen la línea.</div><div style="text-align: justify;">px, py: Definen el punto.</div><br />
<pre class="brush:objc;">-(float) distanciaPuntoRectaConPuntoX: (float) px PuntoY:(float) py LineaX1: (float) x1 LineaY1: (float) y1 LineaX2: (float) x2 LineaY2: (float) y2{
float d1,d2,d3,s,area;
d1 = sqrtf(pow(x2-x1,2) + pow(y2-y1,2));
d2 = sqrtf(pow(x2-px,2) + pow(y2-py,2));
d3 = sqrtf(pow(px-x1,2) + pow(py-y1,2));
s = (d1 + d2 + d3) / 2;
area = sqrtf(s*(s-d1)*(s-d2)*(s-d3));
return (2*area)/d1;
}
</pre><br />
<div style="text-align: justify;">Y eso es todo por hoy, simple pero efectiva fórmula de Heron. ¡Se acabaron los problemas!</div>Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-11825429681831623602011-07-30T12:33:00.000+02:002011-07-30T12:33:07.739+02:00Finalmente será un blog en EspañolAconsejado por una mayoría de compañeros, he decidido que será un blog en español. Siempre se puede traducir un blog con Google Translator (Que horror :D)<br />
<br />
Las razones son obvias, el inglés y el español están muy reñidos en cuestión de hablantes (aunque predomine el inglés) y existen muchos blogs en inglés sobre programación, y la verdad, que pocos he visto en español. Quizás pueda ofrecer algo más de ayuda a mis compañeros de lenguaje.<br />
<br />
Por petición, si algún tutorial genuino resulta atractivo para otros hablantes, me haré una traducción a mi "estilo".<br />
<br />
A lo largo de hoy, publicaré algo un poco tonto, pero que siempre me trajo de cabeza... ¡Distancia de un punto a una recta!Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0tag:blogger.com,1999:blog-2852333835999291128.post-78450311263803484772011-07-30T02:06:00.000+02:002011-07-30T02:15:13.187+02:00First entry? Primera entrada?Well actually I don't know which language should I use for my blog, I think this a very basic and simple English that everybody can understand... I will think this problem and discuss it with my pillow... Who knows... Maybe tomorrow I'm writing in Spanish...<br />
<br />
Btw. This blog is aimed to be about programming skills for iPhone (I think, better to say iOS) and nice tricks and code snippets from my developments in my own games. Maybe some tutorials... Maybe some reviews and quizás alguna que otra tontería mas... (Spanish language!)<br />
<br />
See you next time folks!<br />
<br />
PS: I've done this blog and this first entry with my iPhone 4!Daniel Lópezhttp://www.blogger.com/profile/04336414682278797146noreply@blogger.com0