PageRenderTime 57ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/genePool099d/libs/cocos2d/CCTMXLayer.m

https://github.com/gregkdunn/iphone-game-demos
Objective C | 483 lines | 315 code | 105 blank | 63 comment | 55 complexity | 3df34ee151658385a5cf3717eaafe030 MD5 | raw file
  1. /* cocos2d for iPhone
  2. *
  3. * http://www.cocos2d-iphone.org
  4. *
  5. * Copyright (C) 2009 Ricardo Quesada
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the 'cocos2d for iPhone' license.
  9. *
  10. * You will find a copy of this license within the cocos2d for iPhone
  11. * distribution inside the "LICENSE" file.
  12. *
  13. * TMX Tiled Map support:
  14. * http://www.mapeditor.org
  15. *
  16. */
  17. #import "CCTMXLayer.h"
  18. #import "CCTMXTiledMap.h"
  19. #import "CCTMXXMLParser.h"
  20. #import "CCSprite.h"
  21. #import "CCSpriteSheet.h"
  22. #import "CCTextureCache.h"
  23. #import "Support/CGPointExtension.h"
  24. #pragma mark -
  25. #pragma mark CCSpriteSheet Extension
  26. /* IMPORTANT XXX IMPORTNAT:
  27. * These 2 methods can't be part of CCTMXLayer since they call [super add...], and CCSpriteSheet#add SHALL not be called
  28. */
  29. @implementation CCSpriteSheet (TMXTiledMapExtension)
  30. /* Adds a quad into the texture atlas but it won't be added into the children array.
  31. This method should be called only when you are dealing with very big AtlasSrite and when most of the CCSprite won't be updated.
  32. For example: a tile map (CCTMXMap) or a label with lots of characgers (BitmapFontAtlas)
  33. */
  34. -(void) addQuadFromSprite:(CCSprite*)sprite quadIndex:(unsigned int)index
  35. {
  36. NSAssert( sprite != nil, @"Argument must be non-nil");
  37. NSAssert( [sprite isKindOfClass:[CCSprite class]], @"CCSpriteSheet only supports CCSprites as children");
  38. while(index >= textureAtlas_.capacity || textureAtlas_.capacity == textureAtlas_.totalQuads )
  39. [self increaseAtlasCapacity];
  40. //
  41. // update the quad directly. Don't add the sprite to the scene graph
  42. //
  43. [sprite useSpriteSheetRender:self];
  44. [sprite setAtlasIndex:index];
  45. ccV3F_C4B_T2F_Quad quad = [sprite quad];
  46. [textureAtlas_ insertQuad:&quad atIndex:index];
  47. // XXX: updateTransform will update the textureAtlas too using updateQuad.
  48. // XXX: so, it should be AFTER the insertQuad
  49. [sprite updateTransform];
  50. }
  51. /* This is the opposite of "addQuadFromSprite.
  52. It add the sprite to the children and descendants array, but it doesn't update add it to the texture atlas
  53. */
  54. -(id) addSpriteWithoutQuad:(CCSprite*)child z:(unsigned int)z tag:(int)aTag
  55. {
  56. NSAssert( child != nil, @"Argument must be non-nil");
  57. NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteSheet only supports CCSprites as children");
  58. // quad index is Z
  59. [child setAtlasIndex:z];
  60. // XXX: optimize with a binary search
  61. int i=0;
  62. for( CCSprite *c in descendants_ ) {
  63. if( c.atlasIndex >= z )
  64. break;
  65. i++;
  66. }
  67. [descendants_ insertObject:child atIndex:i];
  68. // IMPORTANT: Call super, and not self. Avoid adding it to the texture atlas array
  69. [super addChild:child z:z tag:aTag];
  70. return self;
  71. }
  72. @end
  73. #pragma mark -
  74. #pragma mark CCTMXLayer
  75. @interface CCTMXLayer (Private)
  76. -(CGPoint) positionForIsoAt:(CGPoint)pos;
  77. -(CGPoint) positionForOrthoAt:(CGPoint)pos;
  78. -(CGPoint) positionForHexAt:(CGPoint)pos;
  79. // adding quad from sprite
  80. -(void)addQuadFromSprite:(CCSprite*)sprite quadIndex:(unsigned int)index;
  81. // adds an sprite without the quad
  82. -(id)addSpriteWithoutQuad:(CCSprite*)child z:(int)z tag:(int)aTag;
  83. // index
  84. -(unsigned int) atlasIndexForExistantZ:(unsigned int)z;
  85. -(unsigned int) atlasIndexForNewZ:(int)z;
  86. @end
  87. @implementation CCTMXLayer
  88. @synthesize layerSize = layerSize_, layerName = layerName_, tiles=tiles_;
  89. @synthesize tileset=tileset_;
  90. @synthesize layerOrientation=layerOrientation_;
  91. @synthesize mapTileSize=mapTileSize_;
  92. @synthesize properties=properties_;
  93. #pragma mark CCTMXLayer - init & alloc & dealloc
  94. +(id) layerWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo
  95. {
  96. return [[[self alloc] initWithTilesetInfo:tilesetInfo layerInfo:layerInfo mapInfo:mapInfo] autorelease];
  97. }
  98. -(id) initWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo
  99. {
  100. // XXX: is 35% a good estimate ?
  101. CGSize size = layerInfo.layerSize;
  102. float totalNumberOfTiles = size.width * size.height;
  103. float capacity = totalNumberOfTiles * 0.35f + 1; // 35 percent is occupied ?
  104. CCTexture2D *tex = nil;
  105. if( tilesetInfo )
  106. tex = [[CCTextureCache sharedTextureCache] addImage:tilesetInfo.sourceImage];
  107. if((self=[super initWithTexture:tex capacity:capacity])) {
  108. self.layerName = layerInfo.name;
  109. self.layerSize = layerInfo.layerSize;
  110. self.tiles = layerInfo.tiles;
  111. self.tileset = tilesetInfo;
  112. self.mapTileSize = mapInfo.tileSize;
  113. self.layerOrientation = mapInfo.orientation;
  114. self.properties = [NSMutableDictionary dictionaryWithDictionary:layerInfo.properties];
  115. atlasIndexArray_ = ccCArrayNew(totalNumberOfTiles);
  116. [self setContentSize: CGSizeMake( layerSize_.width * mapTileSize_.width, layerSize_.height * mapTileSize_.height )];
  117. }
  118. return self;
  119. }
  120. - (void) dealloc
  121. {
  122. [layerName_ release];
  123. [tileset_ release];
  124. [reusedTile_ release];
  125. [properties_ release];
  126. if( atlasIndexArray_ ) {
  127. ccCArrayFree(atlasIndexArray_);
  128. atlasIndexArray_ = NULL;
  129. }
  130. if( tiles_ ) {
  131. free(tiles_);
  132. tiles_ = NULL;
  133. }
  134. [super dealloc];
  135. }
  136. -(void) releaseMap
  137. {
  138. if( tiles_) {
  139. free( tiles_);
  140. tiles_ = NULL;
  141. }
  142. if( atlasIndexArray_ ) {
  143. ccCArrayFree(atlasIndexArray_);
  144. atlasIndexArray_ = NULL;
  145. }
  146. }
  147. #pragma mark CCTMXLayer - obtaining tiles/gids
  148. -(CCSprite*) tileAt:(CGPoint)pos
  149. {
  150. NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position");
  151. NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released");
  152. CCSprite *tile = nil;
  153. unsigned int gid = [self tileGIDAt:pos];
  154. // if GID == 0, then no tile is present
  155. if( gid ) {
  156. int z = pos.x + pos.y * layerSize_.width;
  157. tile = (CCSprite*) [self getChildByTag:z];
  158. // tile not created yet. create it
  159. if( ! tile ) {
  160. CGRect rect = [tileset_ rectForGID:gid];
  161. tile = [[CCSprite alloc] initWithSpriteSheet:self rect:rect];
  162. [tile setPosition: [self positionAt:pos]];
  163. tile.anchorPoint = CGPointZero;
  164. unsigned int indexForZ = [self atlasIndexForExistantZ:z];
  165. [self addSpriteWithoutQuad:tile z:indexForZ tag:z];
  166. [tile release];
  167. }
  168. }
  169. return tile;
  170. }
  171. -(unsigned int) tileGIDAt:(CGPoint)pos
  172. {
  173. NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position");
  174. NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released");
  175. int idx = pos.x + pos.y * layerSize_.width;
  176. return tiles_[ idx ];
  177. }
  178. #pragma mark CCTMXLayer - adding helper methods
  179. -(CCSprite*) insertTileForGID:(unsigned int)gid at:(CGPoint)pos
  180. {
  181. CGRect rect = [tileset_ rectForGID:gid];
  182. int z = pos.x + pos.y * layerSize_.width;
  183. if( ! reusedTile_ )
  184. reusedTile_ = [[CCSprite alloc] initWithSpriteSheet:self rect:rect];
  185. else
  186. [reusedTile_ initWithSpriteSheet:self rect:rect];
  187. [reusedTile_ setPosition: [self positionAt:pos]];
  188. reusedTile_.anchorPoint = CGPointZero;
  189. // get atlas index
  190. unsigned int indexForZ = [self atlasIndexForNewZ:z];
  191. // Optimization: add the quad without adding a child
  192. [self addQuadFromSprite:reusedTile_ quadIndex:indexForZ];
  193. // insert it into the local atlasindex array
  194. ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ);
  195. // update possible children
  196. for( CCSprite *sprite in children_) {
  197. unsigned int ai = [sprite atlasIndex];
  198. if( ai >= indexForZ)
  199. [sprite setAtlasIndex: ai+1];
  200. }
  201. tiles_[z] = gid;
  202. return reusedTile_;
  203. }
  204. -(CCSprite*) updateTileForGID:(unsigned int)gid at:(CGPoint)pos
  205. {
  206. CGRect rect = [tileset_ rectForGID:gid];
  207. int z = pos.x + pos.y * layerSize_.width;
  208. if( ! reusedTile_ )
  209. reusedTile_ = [[CCSprite alloc] initWithSpriteSheet:self rect:rect];
  210. else
  211. [reusedTile_ initWithSpriteSheet:self rect:rect];
  212. [reusedTile_ setPosition: [self positionAt:pos]];
  213. reusedTile_.anchorPoint = CGPointZero;
  214. // get atlas index
  215. unsigned int indexForZ = [self atlasIndexForExistantZ:z];
  216. [reusedTile_ setAtlasIndex:indexForZ];
  217. [reusedTile_ updateTransform];
  218. tiles_[z] = gid;
  219. return reusedTile_;
  220. }
  221. // used only when parsing the map. useless after the map was parsed
  222. // since lot's of assumptions are no longer true
  223. -(CCSprite*) appendTileForGID:(unsigned int)gid at:(CGPoint)pos
  224. {
  225. CGRect rect = [tileset_ rectForGID:gid];
  226. int z = pos.x + pos.y * layerSize_.width;
  227. if( ! reusedTile_ )
  228. reusedTile_ = [[CCSprite alloc] initWithSpriteSheet:self rect:rect];
  229. else
  230. [reusedTile_ initWithSpriteSheet:self rect:rect];
  231. [reusedTile_ setPosition: [self positionAt:pos]];
  232. reusedTile_.anchorPoint = CGPointZero;
  233. // optimization:
  234. // The difference between appendTileForGID and insertTileforGID is that append is faster, since
  235. // it appends the tile at the end of the texture atlas
  236. unsigned int indexForZ = atlasIndexArray_->num;
  237. // don't add it using the "standard" way.
  238. [self addQuadFromSprite:reusedTile_ quadIndex:indexForZ];
  239. // append should be after addQuadFromSprite since it modifies the quantity values
  240. ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ);
  241. return reusedTile_;
  242. }
  243. #pragma mark CCTMXLayer - atlasIndex and Z
  244. int compareInts (const void * a, const void * b)
  245. {
  246. return ( *(int*)a - *(int*)b );
  247. }
  248. -(unsigned int) atlasIndexForExistantZ:(unsigned int)z
  249. {
  250. int key=z;
  251. int *item = bsearch((void*)&key, (void*)&atlasIndexArray_->arr[0], atlasIndexArray_->num, sizeof(void*), compareInts);
  252. NSAssert( item, @"TMX atlas index not found. Shall not happen");
  253. int index = ((int)item - (int)atlasIndexArray_->arr) / sizeof(void*);
  254. return index;
  255. }
  256. -(unsigned int)atlasIndexForNewZ:(int)z
  257. {
  258. // XXX: This can be improved with a sort of binary search
  259. unsigned int i=0;
  260. for( i=0; i< atlasIndexArray_->num ; i++) {
  261. int val = (int) atlasIndexArray_->arr[i];
  262. if( z < val )
  263. break;
  264. }
  265. return i;
  266. }
  267. #pragma mark CCTMXLayer - adding / remove tiles
  268. -(void) setTileGID:(unsigned int)gid at:(CGPoint)pos
  269. {
  270. NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position");
  271. NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released");
  272. unsigned int currentGID = [self tileGIDAt:pos];
  273. if( currentGID != gid ) {
  274. // setting gid=0 is equal to remove the tile
  275. if( gid == 0 )
  276. [self removeTileAt:pos];
  277. // empty tile. create a new one
  278. else if( currentGID == 0 )
  279. [self insertTileForGID:gid at:pos];
  280. // modifying an existing tile with a non-empty tile
  281. else {
  282. unsigned int z = pos.x + pos.y * layerSize_.width;
  283. id sprite = [self getChildByTag:z];
  284. if( sprite ) {
  285. CGRect rect = [tileset_ rectForGID:gid];
  286. [sprite setTextureRect:rect];
  287. tiles_[z] = gid;
  288. } else {
  289. [self updateTileForGID:gid at:pos];
  290. }
  291. }
  292. }
  293. }
  294. -(void) removeChild:(CCSprite*)sprite cleanup:(BOOL)cleanup
  295. {
  296. // allows removing nil objects
  297. if( ! sprite )
  298. return;
  299. NSAssert( [children_ containsObject:sprite], @"Tile does not belong to TMXLayer");
  300. unsigned int atlasIndex = [sprite atlasIndex];
  301. unsigned int zz = (unsigned int) atlasIndexArray_->arr[atlasIndex];
  302. tiles_[zz] = 0;
  303. ccCArrayRemoveValueAtIndex(atlasIndexArray_, atlasIndex);
  304. [super removeChild:sprite cleanup:cleanup];
  305. }
  306. -(void) removeTileAt:(CGPoint)pos
  307. {
  308. NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position");
  309. NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released");
  310. unsigned int gid = [self tileGIDAt:pos];
  311. if( gid ) {
  312. unsigned int z = pos.x + pos.y * layerSize_.width;
  313. unsigned atlasIndex = [self atlasIndexForExistantZ:z];
  314. // remove tile from GID map
  315. tiles_[z] = 0;
  316. // remove tile from atlas position array
  317. ccCArrayRemoveValueAtIndex(atlasIndexArray_, atlasIndex);
  318. // remove it from sprites and/or texture atlas
  319. id sprite = [self getChildByTag:z];
  320. if( sprite )
  321. [super removeChild:sprite cleanup:YES];
  322. else {
  323. [textureAtlas_ removeQuadAtIndex:atlasIndex];
  324. // update possible children
  325. for( CCSprite *sprite in children_) {
  326. unsigned int ai = [sprite atlasIndex];
  327. if( ai >= atlasIndex) {
  328. [sprite setAtlasIndex: ai-1];
  329. }
  330. }
  331. }
  332. }
  333. }
  334. #pragma mark CCTMXLayer - obtaining positions
  335. -(CGPoint) positionAt:(CGPoint)pos
  336. {
  337. CGPoint ret = CGPointZero;
  338. switch( layerOrientation_ ) {
  339. case CCTMXOrientationOrtho:
  340. ret = [self positionForOrthoAt:pos];
  341. break;
  342. case CCTMXOrientationIso:
  343. ret = [self positionForIsoAt:pos];
  344. break;
  345. case CCTMXOrientationHex:
  346. ret = [self positionForHexAt:pos];
  347. break;
  348. }
  349. return ret;
  350. }
  351. -(CGPoint) positionForOrthoAt:(CGPoint)pos
  352. {
  353. int x = pos.x * mapTileSize_.width + 0.49f;
  354. int y = (layerSize_.height - pos.y - 1) * mapTileSize_.height + 0.49f;
  355. return ccp(x,y);
  356. }
  357. -(CGPoint) positionForIsoAt:(CGPoint)pos
  358. {
  359. int x = mapTileSize_.width /2 * ( layerSize_.width + pos.x - pos.y - 1) + 0.49f;
  360. int y = mapTileSize_.height /2 * (( layerSize_.height * 2 - pos.x - pos.y) - 2) + 0.49f;
  361. // int x = mapTileSize_.width /2 *(pos.x - pos.y) + 0.49f;
  362. // int y = (layerSize_.height - (pos.x + pos.y) - 1) * mapTileSize_.height/2 + 0.49f;
  363. return ccp(x, y);
  364. }
  365. -(CGPoint) positionForHexAt:(CGPoint)pos
  366. {
  367. float diffY = 0;
  368. if( (int)pos.x % 2 == 1 )
  369. diffY = -mapTileSize_.height/2 ;
  370. int x = pos.x * mapTileSize_.width*3/4 + 0.49f;
  371. int y = (layerSize_.height - pos.y - 1) * mapTileSize_.height + diffY + 0.49f;
  372. return ccp(x,y);
  373. }
  374. -(id) propertyNamed:(NSString *)propertyName
  375. {
  376. return [properties_ valueForKey:propertyName];
  377. }
  378. @end