2012-09-17 75 views
0

愚蠢的问题。 Cocos2d围绕父子层次结构进行构建。我想知道是否可以有一个父类(例如GameScene)并用一个指向父类成员的指针(例如CCSpriteBatchNode *)初始化一个子类(例如SpriteHandler)。Cocos2d:可以有指向父级类指针的指针吗?

我正在尝试这样做来优化CCSpriteBatchNodes的数量。这是我的主类(GameScene风格)的代码片段。

#import <Foundation/Foundation.h> 
#import "cocos2d.h" 

enum ShooterSceneLayerTags { 
    HudLayerTag = 0, 
    }; 

@interface ShooterScene : CCLayer { 
    CCSpriteBatchNode* sharedSpriteBatchNode; 
} 


-(id) initWithSharedBatchNodeReference:(CCSpriteBatchNode*) sharedSpriteBatchNode; 
+ (id) sceneWithId:(int)sceneId; 
@end 


#import "ShooterScene.h" 
#import "MainMenuScene.h" 

//Layers 
#import "LevelSpritesLayer.h" 
#import "HudLayer.h" 



@interface ShooterScene (PrivateMethods) 
-(void) addLayers:(int)sceneId; 
-(void) loadGameArtFile; 
-(BOOL) verifyAndHandlePause; 
@end 

@implementation ShooterScene 

+ (id) sceneWithId:(int)sceneId 
{ 
    CCScene *scene = [CCScene node]; 

    ShooterScene * shooterLayer = [[self alloc] initWithId:sceneId]; 
    [scene addChild:shooterLayer]; 

    return scene;  
} 

-(id) initWithId:(int)sceneId 
{ 
    if ((self = [super init])) 
    { 
     //Load game art before adding layers - This will initialize the batch node 
     [self loadGameArtFile:sceneId]; 

     //Will add sprites to shared batch node for performance 
     [self addLayers:sceneId]; 
     [self addChild:sharedSpriteBatchNode]; 

     //Do other stuff.. 
     [self scheduleUpdate]; 

    } 
    return self; 

} 

-(void) addLayers:(int)sceneId 
{ 
    LevelSpritesLayer * levelData = [LevelSpritesLayer node]; 
    [levelData initWithSharedBatchNodeReference:sharedSpriteBatchNode]; 

    [self addChild:levelData]; 

    switch (sceneId) { 
     case 1: 
      [levelData loadLevelOneSprites]; 
      break; 
     case 2: 
      [levelData loadLevelTwoSprites]; 
      break;    
     default: 
      break; 
    } 

    HudLayer * hud = [HudLayer node]; 
    [hud setUpPauseMenu]; 
    [self addChild:hud z:1 tag:HudLayerTag]; 
} 

-(BOOL) verifyAndHandlePause 
{ 
    HudLayer * hud = [self getChildByTag:HudLayerTag]; 
    if(hud.pauseRequested){ 
     [[CCDirector sharedDirector] replaceScene:[MainMenuScene scene]]; 

     return true; 
    } 
    else { 
     return false; 
    } 

} 
-(void) update:(ccTime)delta 
{ 
    if([self verifyAndHandlePause]==false) 
    { 
     //Continue with animation etc.. 


    } 
} 

/** 
This is tricky. Could have loaded this in LevelData but as I am expecting to use the same SpriteSheet for HudLayer as well then 
I prefer to have the control here of this. Also, the same sheet could be used for more level, hence specific function is not bad 
**/ 
-(void) loadGameArtFile:(int) sceneId 
{ 
    CCSpriteFrameCache* frameCache = [CCSpriteFrameCache sharedSpriteFrameCache]; 
    [frameCache addSpriteFramesWithFile:@"game-art-hd.plist"]; 

    sharedSpriteBatchNode = [CCSpriteBatchNode batchNodeWithFile:@"game-art-hd.png"]; 
} 

//As dealloc is deprecated, I prefer to remove unused sprites and texture on cleanup 
-(void) cleanup 
{ 
    [[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];  
} 

这里是的loadLevelData方法之一,它采用了sharedSpriteBAtchNodeReference

-(void) loadLevelOneSprites 
{ 
    //Just a proof of concept example 
    testSprite = [CCSprite spriteWithSpriteFrameName:@"File0.png"]; 
    testSprite.anchorPoint = CGPointMake(0.5f, 0.5f); 
    testSprite.position = CGPointMake(160.0f, 240.0f); 
    [sharedSpriteBatchNodeReference addChild:testSprite]; 
} 

回答

1

什么[本]说。它不应该是一个保留或强大的参考,后者是ARC实例变量的默认值。

有一种方法可以确保在ARC下您即使使用强参考也能保持周期安全。覆盖清理方法和零有参考(MRC下,你也应该在这里呼吁版本中,如果保留了参考):

-(void) cleanup 
{ 
    sharedSpriteBatchNode = nil; 
    [super cleanup]; 
} 

中的dealloc将无法正常工作这样做。只要一个子节点对父节点有很强的引用,它就不会被释放。因此,您需要在清理过程中执行此操作,并确保可以在其中设置清理标志的所有方法调用都将该参数设置为YES。

但还有其他的,我认为更好的解决方案,比在初始化器中传递父节点。例如,您可以通过父级的公共标签获取共享批处理节点,并且每次需要时都可以这样做(将其包装为小函数)或将其存储在弱(非保留)实例var中:

// onEnter is typically called right after init (during addChild) 
// parent is already set here 
-(void) onEnter 
{ 
    [super onEnter]; 

    CCSpriteBatchNode* sharedBatchNode = [parent getChildByTag:kSharedBatchNodeTag]; 
} 

或者让家长和投它,假设sharedBatchNode是父类的属性:建议

-(void) whereEver 
{ 
    ShooterScene* scene = (ShooterScene*)parent; 
    CCSpriteBatchNode* sharedBatchNode = scene.sharedSpriteBatchNode; 
    … 

    // you can also reduce the above to a single line: 
    CCSpriteBatchNode* batch = ((ShooterScene*)parent).sharedSpriteBatchNode; 
} 

尤其是后者的解决方案。即使您需要经常这样做,速度也很快。铸造是免费的,财产访问不超过消息发送。只要确定父母实际上是你正在投射的课程的一个对象。

+0

谢谢Steffen,好的和明确的答案。丰富的想法我没有考虑过。 – mm24

3

你可以这样做,但如果你正在使用ARC你应该让你sharedSpriteBatchNode一个弱指针。如果你不这样做,你最终可能会得到通函

循环参考将会发生什么情况:当导演完成游戏场景运行后释放游戏场景时,仍然会让您的孩子保留游戏场景,并且您的游戏场景仍然会保留该孩子。这个圈子会浮起来,永远不会被放弃,因为它被放弃了。

+0

谢谢本,有趣的答案。我将来会考虑这个。我积极参与其中,但接受LearnCocos2D之一,因为它为我的问题提供了一种“创新”解决方案,但要公平,您的答案也应该被接受,并且都应该被考虑。 – mm24

+1

您应该始终接受最有帮助的答案。我同意,Steffen的回答比我的好,他应得的! –

+1

我很感激能够向SO这样广泛而有用的社区提出问题。感谢您分享您的知识。我希望,一旦我发布了我的第一场比赛,就足够了解和你们一样回复:)。 – mm24