Cocos2d:指向父类的指针是否可以?

时间:2012-09-17 16:59:01

标签: pointers opengl-es cocos2d-iphone ccsprite

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

我正在尝试这样做以优化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];
}

2 个答案:

答案 0 :(得分:3)

你可以这样做但是如果你使用ARC,你应该让你的sharedSpriteBatchNode成为弱指针。如果不这样做,您最终可能会得到循环引用

循环参考会发生什么事情,当导演在完成游戏场景运行后释放你的游戏场景时,仍然会让你的孩子保留它,你的游戏场景仍将保留那个孩子。这个圆圈将浮出水面,因为它被抛弃而永远无法被释放。

答案 1 :(得分:1)

[Ben]说的是什么。它不应该是保留或强引用,后者是ARC中的默认实例变量。

有一种方法可以确保在ARC下,即使您使用强引用,您也可以保持周期安全。覆盖清理方法并在那里没有引用(在MRC下你也应该在这里调用release,如果你保留了引用):

-(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;
}

特别推荐使用后一种解决方案。即使您需要经常这样做,也很快。投射是免费的,财产访问不超过发送消息。请确保父实际上是您要将其投射到的类的对象。