0

愚蠢的问题。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];
}
4

2 回答 2

3

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

循环引用将发生的情况是,当 Director 在完成运行游戏场景后释放您的游戏场景时,您的孩子仍将保留它,而您的游戏场景仍将保留该孩子。这个圈子会飘起来,永远无法释放,因为它被遗弃了。

于 2012-09-17T22:52:19.387 回答
1

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

即使您使用强引用,也有一种方法可以确保在 ARC 下保持循环安全。覆盖 cleanup 方法并在此处清除引用(在 MRC 下,如果您保留了引用,您还应该在此处调用 release):

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

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

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

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

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

于 2012-09-18T08:33:47.993 回答