Archivi categoria: Objective C

I miei esperimenti e appunti su Objective C , mac osx e iOS programming

Kingdom:Undead sketch e idee

Iniziamo oggi una serie di articoli/appunti/idee sullo sviluppo di un prototipo di gioco per mac osx, iphone e ipad.

Il gioco è un incrocio tra un tower defence e un rpg, in cui si deve gestire il Kingdom, il “regno” di un manipolo di sopravvissuti all’infezione zombie.

Il tutto sarà incentrato nella gestione e nella crescita di questo Kingdom, un insieme di bunker e container che vengono assemblati man mano che si acquisiscono le materie prime.

Lo sviluppo per ora è su XCode 4.2 utilizzando obiective-c e la libreria Cocos2d .

Per semplificare il disegno, sto iniziando a definire tutti i modelli grafici abbozzandoli a matita, e poi scannerizzandoli e scontornandoli a mano.
Consiglio l’uso di PixelMator per questo tipo di attività. E’ un software dal costo moderato e dalle grandi potenzialità. Quando il gioco si troverà ad uno stadio abbastanza maturo, e avrò finito di definire con esattezza la dimensione di ogni singolo asset di gioco, allora passerò il lavoro a uno o più grafici 2d per ridisegnare il tutto.

Non conoscendo bene objective-c e cocos2d, inizio per gradi facendo direttamente training on the job, molte cose quindi magari ovvie a chi conosce meglio questo linguaggio, a primo acchitto potrebbero sfuggirmi. Già nella situazione attuale, iniziata oramai da 2 settimane, mi sono accorto di carenze nel disegno delle classi e ho iniziato a utilizzare funzionalità non previste inizialmente, come CoreData, il NotificationCenter , i Protocolli, e tante altre cose.

Alcuni ignobili schizzi da me eseguiti:

 

Che trasformati, diventeranno la base dei nostri spritesheet dove mettere tutte le animazioni per i sopravvissuti e per gli zombie, e i tileset per la nostra mappa.

Per la creazione del Kingdom invece sto utilizzando una CCTMXTiledMap composta da due righe, una per il piano base e una per il piano superiore, composta da 3 strati di layer, il primo che contiene lo sfondo di ogni cella, il secondo che contiene il centro della cella, con i mobili e soprammobili, il terzo strato che contiene le immagini frontali, le cornici dei bunker. i personaggi si troveranno tra lo sfondo e il layer centrale. Questo permetterà di avere una mappa modulare in cui andare a mischiare diversi background, foreground e middle per ottenere un numero maggiore di personalizzazioni della mappa.

Una prima bozza dopo una settimana di lavoro inizia a prendere forma:

Ci sono già diverse scene per l’implementazione dei credits iniziali, del menu di gioco e della schermata principale, basata su uno sfondo con scrolling in parallasse e la nostra mappa di bunker al centro.

Per poter lavorare al meglio, ho esteso la CCTXMTiledMap, per aggiungere tutte quelle proprietà e metodi che dovranno snellire e semplificare lo sviluppo e la gestione della mappa.

ecco un esempio della sua interfaccia:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#import "cocos2d.h"
#import "CCTMXTiledMap.h"

@interface BunkerMap : CCTMXTiledMap
{
CCTMXLayer *_baseLayer;
int _homeBunkerPosition;
int _lastSelectedBunkerId;
CCSprite* _lastSelectedBunker;
}

@property(nonatomic,retain)CCTMXLayer *baseLayer;
@property(nonatomic,readwrite)int homeBunkerPosition;
@property(nonatomic,readonly)int lastSelectedBunkerId;
//TODO: convertire il lastSelectedBunker da sprite a oggetto Bunker
@property(nonatomic,readonly)CCSprite *lastSelectedBunker;
-(void)selectBunker:(int)bunkerNumber;

-(void)centerToBunkerNumber:(int)bunkerNumber select:(BOOL)flag;
-(int)centerToBunkerPosition:(CGPoint)location select:(BOOL)flag;

-(int) findBunkerByPosition:(CGPoint)location;
@end

ed un esempio di implementazione della funzione per capire su che bunker ho selezionato (per poterlo centrare ad esempio o visualizzarne la sua scheda):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-(int) findBunkerByPosition:(CGPoint)location{
   
    CGSize s = [self.baseLayer layerSize];
    for( int x=0; x<s.width;x++) {
        for( int y=0; y< s.height; y++ ) {
            CCTMXLayer *l;
            CCARRAY_FOREACH(self.children, l) {
                if([l isKindOfClass:[CCTMXLayer class]]){
                    CCSprite *tile = [l tileAt:ccp(x,y)];
                   
                    if (CGRectContainsPoint([tile boundingBox],[self convertToNodeSpace:location])){
                        // NSLog(@"TILE:%@",[tile position]);
                        return x;
                    }
                }
               
            }
           
           
            //  unsigned int tmpgid =[layer tileGIDAt:ccp(x,y)];
           
           
        }
    }
   
    return -1;
}

Leggere configurazione plist di default

Su mac è possibile leggere il file di default contenente la configurazione dell’applicativo in maniera molto semplice.
basta usare gli NSBundle

per poter accedere al bundle di default, il file info.plist generalmente contenuto delle Resources dell’applicativo:

1
NSBundle* mainBundle = [NSBundle mainBundle];

per poter accedere alle info contenute, basta usare il dictionary.

1
2
BOOL stats=[[[mainBundle infoDictionary] valueForKey:@"ShowStats"]boolValue];
NSString* version= [[mainBundle infoDictionary] valueForKey:@"Version"];

e’ possibile mettere nel bundle anche dictionary :

1
2
3
4
5
6
7
    NSDictionary* list = [[mainBundle infoDictionary ]valueForKey:@"Resolution"];
    if (list!=nil){
       
        NSNumber* width =[list valueForKey:@"Width"];
        NSNumber* height =[list valueForKey:@"Height"];
        return CGSizeMake([width doubleValue],[height doubleValue]);
    }

Creare Classi Statiche e Singleton in Objective C

Per creare una classe che sia raggiungibile da ogni parte del nostro codice, abbiamo due alternative: realizzare una classe statica con metodi e attributi statici, oppure implementare il pattern del Singleton.
Preferisco generalmente la seconda scelta più orientata ad un design pulito del nostro codice e rimanendo legato alla programmazione ad oggetti.

Per creare una classe statica, con attributi e metodi statici definiamo l’interfaccia:

1
2
3
4
@interface MyStaticClass : NSObject
+ (int)getIntMethod;
+ (void)setIntMethod:(int)value;
@end

e poi implementiamola:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#import "MyStaticClass.h"

@implementation MyStaticClass

static int staticValue = 1;

+ (int)getIntMethod {
  return staticValue;
}

+ (void)setIntMethod:(int)value {
  staticValue = value;
}

+ (id)alloc {
  [NSException raise:@"Non può essere istanziata format:@"Static class 'ClassName' non può essere instanziato"];
  return nil;
}

@end

importante l’override dell’alloc per non permettere alla classe di essere istanziata.

Per un approccio più elegante e sicuramente più riusabile, implementiamo invece il Design Pattern del Singleton:

1
2
3
4
5
6
7
8
9
@interface MySingleton : NSObject
{
}
-(int)getIntMethod;
-(void)setIntMethod:(int)value;

+ (MySingleton *) m_MySingleton;
 
@end

Implementiamo la nostra interfaccia:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#import "MySingleton.h"
 
@implementation MySingleton
 
static MySingleton *m_MySingleton =nil;
static int staticValue = 1;


+ (MySingleton *) m_MySingleton
{
  if (m_MySingleton == nil)
  {
    //creo l'istanza
    m_MySingleton = [[super allocWithZone:NULL] init];
  }
  return m_MySingleton;
}
 
- (int)getIntMethod {
  return staticValue;
}

- (void)setIntMethod:(int)value {
  staticValue = value;
}
+ (id)allocWithZone:(NSZone *)zone
{
 //sincronizzo in modo da rendere la fase di allocazione ThreadSafe
 //in modo da gestire più thread che richiedono contemporaneamente il Singleton
 @synchronized(self)
 {
   if (m_MySingleton == nil)
   {
     m_MySingleton = [super allocWithZone:zone];
     return m_MySingleton;
   }
 }
 return nil;
}
 
- (id)copyWithZone:(NSZone *)zone
{
 //non e' possibile clonarlo e quindi ritorna sempre la stessa istanza.
 return self;
}
 
- (id)retain
{
 return self;
}
 
- (NSUInteger)retainCount
{
 //in questo modo il suo refCount non scenderà mai a zero.
 return NSUIntegerMax;
}
 
- (void) release
{
  //non fa nulla.
}
 
- (id)autorelease
{
 return self;
}
 
@end

Piccola correzione. I metodi nel singleton ovviamente non devono essere statici (tranne il metodo che permette l’accesso all’istanza).