2012-09-17 1 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];  
} 

그리고 IT는 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]; 
} 

작동하지 않습니다 할당 해제에 이렇게. 자식 노드가 부모에 대한 강력한 참조를 가지고있는 한, 할당 해제되지 않습니다. 따라서이를 정리에서 수행해야하며 정리 플래그를 설정할 수있는 모든 메소드 호출에서 해당 매개 변수를 YES로 설정해야합니다.

하지만 이니셜 라이저에 부모 노드를 전달하는 것보다 나은 솔루션이 있다고 생각합니다. 예를 들어, 부모의 공통 태그를 통해 공유 배치 노드를 가져올 수 있으며, 필요할 때마다 (작은 함수로 묶음) 또는 weak (비 보유) 인스턴스에 저장할 수 있습니다. 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 약한 포인터를해야한다는 loadLevelData 방법 중 하나입니다. 그렇지 않다면 순환 참조으로 끝날 수 있습니다.

순환 참조는 게임 장면 실행이 끝난 후에 감독이 게임 장면을 출시 할 때 자녀가 계속 게임 장면을 유지하고 게임 장면이 계속 그 아동을 유지할 때 발생합니다. 이 서클은 떠 다니고 버려지기 때문에 결코 풀어 낼 수 없습니다.

+0

감사합니다. Ben, 재미있는 대답입니다. 나는 이것을 미래에 고려할 것이다. 나는 그것을 업 그레 이드했지만 LearnCocos2D 하나를 내 문제에 대한 "혁신적인"솔루션을 제공하지만 공평하게 답변을 받아 들여야하며 둘 모두 고려해야합니다. – mm24

+1

항상 도움이되는 대답을 받아 들여야합니다. 나는 동의한다, Steffen의 대답은 나의 것보다 낫다. 그리고 그는 그것을받을 가치가있다! –

+1

저는 이렇게 넓고 도움이되는 커뮤니티에 물어볼 수있어서 너무 감사하고 있습니다. 지식을 공유해 주셔서 감사합니다. 나는 첫 번째 게임을 발표하고 나면 당신이하는만큼 돌아올 수 있도록 충분히 알고 싶습니다. :) – mm24

관련 문제