Wednesday, August 26, 2009

iPhone Coding Part3

Σε αυτό το post θα ρίξουμε μια ματιά στο Core Animation Framework του iPhone αλλά και του MacOS μιας και είναι σχεδόν ίδια. Βασικά το ένα είναι υποσύνολο του άλλου.
Το Core Animation μας λύνει τα χέρια, είναι μια πολύ εύκολη βιβλιοθήκη σχεδιασμένη για διανοητικά καθυστερημένους προγραμματιστές σαν και και τον γράφοντα.
Αυτό μου αρέσει με το MacOS η απλότητα του, όλα γίνονται σχεδόν με μαγικό τρόπο. Για όσους έχουν δοκιμάσει να γράψουν παιχνίδι σε άλλες πλατφόρμες (win32, SDL, gtk) θα είδαν ότι δεν όλα τόσο ρόδινα, εδώ όμως είναι!
Η Core Animation έχει το concept των Layers τα οποία είναι βασικά ορθογώνια παραλληλεπίπεδα υποστηριζόμενα από την OpenGL.
Θα μπορούσαμε να το κάνουμε και εμείς εύκολα με την OpenGL (βασικά θα το δούμε πως γίνεται σε άλλο tutorial) άλλα οι άνθρωποι προσπαθούν να μας κάνουν τη ζωή ακόμα ποιο εύκολη γιατί να τους γειώσουμε :)
Ο κώδικας που χειρίζεται τα Layers είναι πολύ απλός. το μόνο που πρέπει να θυμάστε είναι ότι οι συντεταγμένες τους είναι το κέντρο του Layer εκτός και αν το αλλάξουμε που δεν θα το κάνουμε εδώ.
Και δεν θα το κάνουμε γιατί σε λίγο που θα αρχίσουμε να περιστρέφουμε τα Layers θα έχουν σαν pivot point το κέντρο τους.
Με τη μέθοδο position τοποθετούμε το Layer στον χώρο μας σε ένα συγκεκριμένο σημείο (CGPoint). Όπου CG = Core Graphics, μια άλλη βιβλιοθήκη.
Με τη μέθοδο bounds ορίζουμε το μέγεθος και εδώ χρησιμοποιούμε την CGRectMake όπου φτιάχνουμε το τετράγωνο μας, αφού έχουμε ίδιες πλευρές μεγέθους objectSize.
Με την μέθοδο contents ορίζουμε το τι θα περιλαμβάνει αυτό το Layer. Εδώ έχουμε πολυ κουβέντα αλλά προς το παρόν θα βάλουμε μια εικόνα PNG.
H zPosition δε νομίζω να χρειάζεται επεξήγηση, το λέει το όνομα της. Είναι η θέση που έχει το Layer στο zBuffer των Layers του View στο οποίο παίζουμε.
Όσον αφορά το business logic του παιχνιδιού μας, τα tileLayer arrays είναι arrays από CALayers
Tα layerX και layerΥ έχουν τις συντεταγμένες τους, τα tileAngle έχουν τις γωνίες περιστροφής και πάει λέγοντας. Θεωρώ ότι ο κώδικας όσο κακογραμμένος και να είναι έχει εύκολα ονόματα μεταβλητών για να καταλάβουμε τι παίζει.
Λοίπουν κάποιες ρουτίνες θα τις ανεβάζω σιγά-σιγά για να μη χαθούμε




-(void)setupTIle:(int)_tileID:(int)_x:(int)_y:(id)_imageRef:(float)_z:(int)_posInMapStruct:(BOOL)_storeAngleInArray:(BOOL)isTeleport
{
tileLayer[_tileID] = [CALayer layer];
tileLayer[_tileID].position=CGPointMake(_x+objectHalfSize,_y+objectHalfSize);
tileLayer[_tileID].bounds=CGRectMake(0,0,objectSize,objectSize);

tileLayer[_tileID].contents = _imageRef;
tileLayer[_tileID].zPosition=_z;
layerX[_tileID]=_x;
layerY[_tileID]=_y;
initialTileCoordX[tileID] = _x;
initialTileCoordY[tileID] = _y;
if (_storeAngleInArray) {
tileAngle[_tileID]=level[0].rot[_posInMapStruct];
if (tileAngle[_tileID] !=0)
[self rotateTile:tileID:tileAngle[_tileID]:YES];
}
if (isTeleport)
tileAngle[tileID]=level[0].rot[_posInMapStruct];
[rootLayer addSublayer:tileLayer[_tileID]];

}
-(void)setupTileLayers{
tileID = -1;
int posInMapStruct = 0;
for (int rowNum=0; rowNum < level[0].rowsInMap ; rowNum++){
for (int columnNum=0 ; columnNum < level[0].objectsPerRow ; columnNum++){
tileID++;
x = (columnNum * objectSize);
y = (rowNum * objectSize) ;

posInMapStruct = (rowNum * level[0].objectsPerRow) + columnNum;

if (level[0].map[posInMapStruct] == 0) {
layerX[tileID]=x;
layerY[tileID]=y;
initialTileCoordX[tileID] = x;
initialTileCoordY[tileID] = y;
}
else
{
switch ( level[0].map[posInMapStruct] )
{
case 0: break;
case MIRROR:
[self setupTIle:tileID:x:y:(id)mirrorImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
case DOUBLE_MIRROR:
[self setupTIle:tileID:x:y:(id)doubleMirrorImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
case GATES:
[self setupTIle:tileID:x:y:(id)gatesImageRef:.6f:posInMapStruct:DO_NOT_STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
[self animateGate:tileID];
break;
case TELEPORT:
[self setupTIle:tileID:x:y:(id)teleportImageRef:.6f:posInMapStruct:DO_NOT_STORE_ANGLE_IN_ARRAY:TELEPORTATION];
break;
case Y_TILE:
[self setupTIle:tileID:x:y:(id)YImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
case PRISM:
[self setupTIle:tileID:x:y:(id)prismImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
case RED_FILM:
[self setupTIle:tileID:x:y:(id)redImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
case GREEN_FILM:
[self setupTIle:tileID:x:y:(id)greenImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
case LAZER:
[self setupTIle:tileID:x:y:(id)lazerImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];

//Calc Lazer Beam begining
[self addNewLazerBeam:tileID:x:y:0:YES];
break;
//Blocks
case BLOCK_1_FULL:
[self setupTIle:tileID:x:y:(id)block1FullImageRef:.6f:posInMapStruct:STORE_ANGLE_IN_ARRAY:NO_TELEPORTATION];
break;
}
}
}
}
}


-(void)showLazer{
[tileLayer[LAZER_LAYER_ID] setNeedsDisplay];
}

-(void)setupLevelIndicatorLayer{
}
-(void)calcScoreLayerCoordinates{
visibleRect = tileLayer[0].visibleRect;
scoreLayerCoordinates = CGPointMake( visibleRect.size.width, visibleRect.size.height+300); //FIX HARD VALUE LATER
}
-(void)setupScoreIndicatorLayer{
tileLayer[SCORE_LAYER_ID]= [CALayer layer];
self.calcScoreLayerCoordinates;
tileLayer[SCORE_LAYER_ID].bounds = CGRectMake(scoreLayerCoordinates.x, scoreLayerCoordinates.y, screenWidth, 20); //FIX HARD VALUE LATER
tileLayer[SCORE_LAYER_ID].position=CGPointMake(scoreLayerCoordinates.x+(screenWidth/2), scoreLayerCoordinates.y+310);//FIX HARD VALUE LATER
tileLayer[SCORE_LAYER_ID].zPosition=4.;
[rootLayer addSublayer:tileLayer[SCORE_LAYER_ID]];
}

-(void)setupLevel {
self.backgroundColor = [UIColor colorWithPatternImage:image];
rootLayer = [self layer];
[self setupTileLayers];
[self setupRotorLayer];
[self setupLazerBeams];
[self showLazer];
}



0 σχόλια:

Post a Comment