比较完美版本的GameScene,借助于userData,可以比较彻底的将超出界限外的BYShape 对象回收掉
参见前面写过的一篇文章。。。。
// // GameScene.mm // GameFrameWork // // Created by Eric Zhu on 6/12/11. // Copyright 2011 Home. All rights reserved. // #import "GameScene.h" // enums that will be used as tags enum { kTagDemoNode = 1, kTagEleBallNode = 2, kTagEleBoxNode = 3, kTagBgGame = 4, }; @implementation GameScene +(CCScene *) scene { CCScene *scene = [CCScene node]; // 'scene' is an autorelease object. CCLayer *bgLayer = [GameBgLayerA node]; [scene addChild:bgLayer z:0]; CCLayer *gameLayer = [GameScene node]; [scene addChild:gameLayer z:2]; return scene; } -(void) createBox2dWorld { // 创建 box2d 世界~ b2Vec2 gravity; gravity.Set(0.0f, -10.0f); bool doSleep = true; _world = new b2World(gravity, doSleep); _world->SetContinuousPhysics(true); _m_debugDraw = new GLESDebugDraw(PTM_RATIO); _world->SetDebugDraw(_m_debugDraw); // Add contact listener MyContactListener *_contactListener = new MyContactListener(); _world->SetContactListener(_contactListener); // 如果 DEBUG_DRAW 的开关打开了的话,还可以对 DEBUG_DRAW 具体要画出些什么内容进行详细配置~ uint32 flags = 1; if(DEBUG_DRAW) { flags = 0; } flags += b2DebugDraw::e_shapeBit; // flags += b2DebugDraw::e_jointBit; // flags += b2DebugDraw::e_aabbBit; // flags += b2DebugDraw::e_pairBit; // flags += b2DebugDraw::e_centerOfMassBit; _m_debugDraw->SetFlags(flags); } -(void) addToolBar { _toolBar = [ToolBar node]; [_toolBar setShapes:_shapes]; [self addChild:_toolBar]; } -(void) loadTmxTiledMap { // 加载 TMX 地图~ _gameMap = [CCTMXTiledMap tiledMapWithTMXFile:[NSString stringWithFormat:@"level_%@.tmx", @"demo"]]; [self addChild:_gameMap]; // Element Group 中的 Table 类表示的是地图中的拦路方块儿~ CCTMXLayer *ground = [_gameMap layerNamed:@"Ground"]; for (int x = 0; x < GAME_WIDTH; x ++) { for (int y = 0; y < GAME_HEIGH; y ++) { int tileGID = [ground tileGIDAt:CGPointMake(x, y)]; if (tileGID) { NSDictionary *properties = [_gameMap propertiesForGID:tileGID]; if (properties) { NSString *collision = [properties valueForKey:@"collidable"]; if (collision && [collision compare:@"hori"] == NSOrderedSame) { [[Table alloc] init:_world type:1 x:[ground tileAt:CGPointMake(x, y)].position.x/PTM_RATIO y:[ground tileAt:CGPointMake(x, y)].position.y/PTM_RATIO]; } else if (collision && [collision compare:@"left"] == NSOrderedSame) { [[Table alloc] init:_world type:2 x:[ground tileAt:CGPointMake(x, y)].position.x/PTM_RATIO y:[ground tileAt:CGPointMake(x, y)].position.y/PTM_RATIO]; } else if (collision && [collision compare:@"right"] == NSOrderedSame) { [[Table alloc] init:_world type:3 x:[ground tileAt:CGPointMake(x, y)].position.x/PTM_RATIO y:[ground tileAt:CGPointMake(x, y)].position.y/PTM_RATIO]; } else if (collision && [collision compare:@"full"] == NSOrderedSame) { [[Table alloc] init:_world type:4 x:[ground tileAt:CGPointMake(x, y)].position.x/PTM_RATIO y:[ground tileAt:CGPointMake(x, y)].position.y/PTM_RATIO]; } } } } } NSLog(@"Enter the loadTmxTiledMap method2"); // 读取对象层中配置的数据(将所有形状均保存至1个统一的数组 _shapes,便于在越界的时候做清除遍历)~ CCTMXObjectGroup *objects = [_gameMap objectGroupNamed:@"Objects"]; NSAssert(objects != nil, @"'Objects' object group not found"); for( NSMutableDictionary *child in [objects objects] ) { CGPoint p = CGPointMake([[child valueForKey:@"x"] intValue], [[child valueForKey:@"y"] intValue]); CGSize sz = CGSizeMake([[child valueForKey:@"w"] intValue], [[child valueForKey:@"h"] intValue]); float rotateDegree = [[child valueForKey:@"r"] floatValue]; if( [[child valueForKey:@"name"] isEqual: @"Triangle"] ) { // 三角形~ BYTriangle *triangle = [[BYTriangle alloc] init:_world size:sz rotate:rotateDegree textureImgName:@"darkWoodenTexture.png"]; [_shapes addObject:triangle]; } else if ( [[child valueForKey:@"name"] isEqual:@"Rectangle"] ) { // 矩形~ BYRectangle *rectangle = [[BYRectangle alloc] init:_world size:sz rotate:rotateDegree insideColor:(ccColor4F){0, 0.0f, 0, 0.3f} outsideColor:(ccColor4F){1.0f, 1.0f, 1.0f, 0.7f} textureImgName:@"redWoodFloor.png" noiseImgName:@"paperNoise_compact512.png"]; [_shapes addObject:rectangle]; } else if ( [[child valueForKey:@"name"] isEqual:@"Trapezium"] ) { // 梯形~ BYTrapezium *trapezium = [[BYTrapezium alloc] init:_world size:sz rotate:rotateDegree insideColor:(ccColor4F){0, 0.0f, 0, 0.3f} outsideColor:(ccColor4F){1.0f, 1.0f, 1.0f, 0.7f} textureImgName:@"colorMixed_compact256.png"]; [_shapes addObject:trapezium]; } else if ( [[child valueForKey:@"name"] isEqual:@"Pentagon"] ) { // 五边形~ BYPentagon *pentagon = [[BYPentagon alloc] init:_world size:sz rotate:rotateDegree]; [_shapes addObject:pentagon]; } else if ( [[child valueForKey:@"name"] isEqual:@"Sexangle"] ) { // 六边形~ BYSexangle *sexangle = [[BYSexangle alloc] init:_world size:sz rotate:rotateDegree insideColor:(ccColor4F){0, 0.0f, 0, 0.3f} outsideColor:(ccColor4F){1.0f, 1.0f, 1.0f, 0.7f} textureImgName:@"greenStoneTexture.png"]; [_shapes addObject:sexangle]; } else if ( [[child valueForKey:@"name"] isEqual:@"Circle"] ) { // 圆~ BYCircle *circle = [[BYCircle alloc] init:_world size:sz rotate:rotateDegree]; [_shapes addObject:circle]; } } // for(int i = 0; i < [_shapes count]; i ++) { // NSLog(@"shape%d 的角度为: %0.2f", i, CC_RADIANS_TO_DEGREES([[_shapes objectAtIndex:i] getSprite].rotation)); // } } -(void) initMemberVariable { _spriteContainer = [[NSMutableArray alloc] init]; // init sprite container _shapes = [[NSMutableArray alloc] init]; // 真bug,_shapes没有初始化竟然编译通过运行时也不报错,nnd~ // Set up sprite, you could get this batchNode by tag anywhere in this class _testBatch = [CCSpriteBatchNode batchNodeWithFile:@"blocks.png" capacity:150]; [self addChild:_testBatch z:0 tag:kTagDemoNode]; _eleBallBatch = [CCSpriteBatchNode batchNodeWithFile:@"ele-ball.png" capacity:1]; [self addChild:_eleBallBatch z:0 tag:kTagEleBallNode]; _eleBoxBatch = [CCSpriteBatchNode batchNodeWithFile:@"ele-box.png" capacity:150]; [self addChild:_eleBoxBatch z:0 tag:kTagEleBoxNode]; } -(id) init { if( (self=[super init])) { self.isTouchEnabled = YES; // 1.启用屏幕的触摸,三轴陀螺仪特性~ // self.isAccelerometerEnabled = YES; //enable accelerometer [self createBox2dWorld]; // 2.创建 box2d 物理模拟世界~ [self initMemberVariable]; // 3.初始化该类的一些成员变量~ [self loadTmxTiledMap]; // 4.加载 TiledMap 地图 [self addToolBar]; // 5.添加形状条~ [self schedule: @selector(tick:)]; // 6.指定计划任务方法(每隔一段固定的时间就调用1次)~ } return self; } -(void) draw { // draw will be called fps CC_DISABLE_DEFAULT_GL_STATES(); glEnableClientState(GL_VERTEX_ARRAY); _world->DrawDebugData(); // restore default GL states glEnable(GL_TEXTURE_2D); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); } -(BOOL) isPositionOutOfBounds:(CGPoint)p { // 判断 BYShape 对象是否越界,越界即将之销毁(可用来判定游戏失败)~ BOOL flag = NO; if(p.x < -320.0f || p.x > 640.0f || p.y < -480.0f) { // 将范围适当扩大,避免失真~ flag = YES; } return flag; } -(void) tick: (ccTime) dt { int32 velocityIterations = 8; int32 positionIterations = 1; _world->Step(dt, velocityIterations, positionIterations); b2Body *node = _world->GetBodyList(); while(node) { b2Body *b = node; if(b->GetUserData() != NULL) { //Synchronize(同步) the AtlasSprites position and rotation with the corresponding(相应的) body // 根据 box2d 对物理世界的现实模拟得到的结果同步更新 精灵的位置以及旋转角度~ BYShape *shape = (BYShape*)b->GetUserData(); CCSprite *myActor = [shape getSprite]; CGPoint position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO ); if([self isPositionOutOfBounds:position] == NO) { // 如果没有越界的话,实时更新~ myActor.position = position; myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()); node = node->GetNext(); } else { // 1.在1个物体摧毁之前一切都很顺利。1旦1个物体摧毁了,它的next指针就变得非法,所以 b2Body::GetNext()就会返回垃圾。 // 解决方法是在摧毁之前拷贝 next 指针。 node = node->GetNext(); [_shapes removeObject:shape]; // 将 body 所属的 BYShape 对象从 _shapes 中移除~ [shape dealloc]; // 释放 shape 对象所占用的内存(包括销毁 b2Body,b2Fixture,CCSprite 对象)~ } } else { node = node->GetNext(); } } } -(void) addNewSpriteWithCoords:(CGPoint)p { CCSpriteBatchNode *batch = (CCSpriteBatchNode*) [self getChildByTag:kTagDemoNode]; //We have a 64 x 64 sprite sheet with 4 different 32 x 32 images. //The following code is just randomly picking one of the images int idx = (CCRANDOM_0_1() > 0.5 ? 0 : 1); int idy = (CCRANDOM_0_1() > 0.5 ? 0 : 1); CCSprite *sprite = [CCSprite spriteWithBatchNode:batch rect:CGRectMake(32*idx, 32*idy, 32, 32)]; [batch addChild:sprite]; sprite.position = ccp( p.x, p.y); //add sprite into container [_spriteContainer addObject:sprite]; NSLog(@"new sprite added at x:%d y:%d", p.x, p.y); } - (void) addBallElementWithCoords:(CGPoint)p { // 保留这个方法是为实验销毁BYShape对象是否符合要求~ // ****************************** 多边形 *********************************** CGPoint *input = new CGPoint[4]; input[0] = ccp(0, 0); input[1] = ccp(10, 50); input[2] = ccp(50, 50); input[3] = ccp(40, 0); BYTextureDef *textureDef = [[BYTextureDef alloc] init:input vertexCount:4 insideColor:(ccColor4F){0, 0.0f, 0, 0.3f} outsideColor:(ccColor4F){1.0f, 1.0f, 1.0f, 0.7f} textureImgName:@"greenWaveTexture.png" noiseImgName:@"Noise_compact512.png" rotateDegree:0]; BYPolygon *elePolygon = [[BYPolygon alloc] init:_world textureDef:textureDef]; [self addChild:[elePolygon getSprite]]; [elePolygon addSprite2b2World:p]; // ******************************* 矩形 ******************************* BYRectangle *rectangle = [[BYRectangle alloc] init:_world size:CGSizeMake(80.0f, 15.0f) insideColor:(ccColor4F){0, 0.0f, 0, 0.3f} outsideColor:(ccColor4F){1.0f, 1.0f, 1.0f, 0.7f} textureImgName:@"redWoodFloor.png" noiseImgName:@"paperNoise_compact512.png"]; [self addChild:[rectangle getSprite]]; [rectangle addSprite2b2World:p]; // ******************************* 三角形 ******************************* BYTriangle *triangle = [[BYTriangle alloc] init:_world size:CGSizeMake(60.0f, 30.0f) textureImgName:@"darkWoodenTexture.png"]; [self addChild:[triangle getSprite]]; [triangle addSprite2b2World:p]; // ******************************* 梯形 ******************************* BYTrapezium *trapezium = [[BYTrapezium alloc] init:_world size:CGSizeMake(60.0f, 30.0f) insideColor:(ccColor4F){0, 0.0f, 0, 0.3f} outsideColor:(ccColor4F){1.0f, 1.0f, 1.0f, 0.7f} textureImgName:@"colorMixed_compact256.png"]; [self addChild:[trapezium getSprite]]; [trapezium addSprite2b2World:p]; // ******************************* 五边形 ******************************* BYPentagon *pentagon = [[BYPentagon alloc] init:_world size:CGSizeMake(80.0f, 80.0f)]; [self addChild:[pentagon getSprite]]; [pentagon addSprite2b2World:p]; // *********************** 六边形(宽高比 115.4:100 ) ************************** BYSexangle *sexangle = [[BYSexangle alloc] init:_world size:CGSizeMake(57.0f, 50.0f) insideColor:(ccColor4F){0, 0.0f, 0, 0.3f} outsideColor:(ccColor4F){1.0f, 1.0f, 1.0f, 0.7f} textureImgName:@"greenStoneTexture.png"]; [self addChild:[sexangle getSprite]]; [sexangle addSprite2b2World:p]; // ******************** 圆形 ************************ BYCircle *circle = [[BYCircle alloc] init:_world size:CGSizeMake(30.0f, 180.0f)]; [self addChild:[circle getSprite]]; [circle addSprite2b2World:p]; } - (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { for( UITouch *touch in touches ) { CGPoint location = [touch locationInView: [touch view]]; location = [[CCDirector sharedDirector] convertToGL: location]; [self addBallElementWithCoords:location]; } } - (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { for( UITouch *touch in touches ) { CGPoint location = [touch locationInView: [touch view]]; location = [[CCDirector sharedDirector] convertToGL: location]; CCSprite *shapeSprite = [[_toolBar getFirstShape] getSprite]; shapeSprite.position = ccp(location.x, location.y); } // NSLog(@"ccTouchesMoved"); } - (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { for( UITouch *touch in touches ) { CGPoint location = [touch locationInView: [touch view]]; location = [[CCDirector sharedDirector] convertToGL: location]; CCSprite *shapeSprite = [[_toolBar getFirstShape] getSprite]; [(BYShape*)shapeSprite.userData addSprite2b2World:ccp(location.x, location.y)]; [_toolBar removeFirstShape]; } NSLog(@"ccTouchesEnded"); } - (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"ccTouchesCancelled"); } // on "dealloc" you need to release all your retained objects - (void) dealloc { // 1.if the object extends CCNode, you needn't care how to free the heap space(dynamic allocated memmery-space) // 2.if the object is contained by objective-c_baseclass_framework, free heapspace by "[*** dealloc];" // 3.if the object comes from box2d(writing in C++), free heapspace by "delete ****;" delete _world; _world = NULL; delete _m_debugDraw; _m_debugDraw = NULL; // below is the second case! since the CCSpriteBatchNode extends CCNode, // there is no need to dealloc it! so, below is no neccessary! // [_testBatch dealloc]; // [_eleBallBatch dealloc]; // [_eleBoxBatch dealloc]; // below is the third case! [_spriteContainer dealloc]; [_eleBallContainer dealloc]; [_shapes dealloc]; // don't forget to call "super dealloc" [super dealloc]; } @end