Совместное использование общих верхних и нижних колонтитулов CCLayers между несколькими CCScenes и делает его обновляемым

В моей игре cocos2d мне нужно использовать общий верхний и нижний колонтитулы, которые будут отображаться на каждом игровом слое.

Заголовок содержит такие вещи, как деньги и спрайты, тогда как нижний колонтитул содержит кнопки CCMenuItem для перемещения между различными игровыми сценами.

Верхний / нижний колонтитулы отображаются в Z-индексе немного выше, чем текущая сцена / слой, так что он появляется поверх всего отображаемого.

Заголовок может, например, выглядеть так:

Наличные деньги: 5000 долларов

Нижний колонтитул может, например, выглядеть как CCMenu со спрайтовыми кнопками, например так:

Главная | базарная площадь

Это будет считаться системой HUD.

Теперь я попытался добавить HUD к каждой сцене способом, подобным этому;

HudLayer *hud = [HudLayer node]
[self addChild:hud z:1];

Но это вызывает ошибку утверждения.

* Ошибка подтверждения в -[CCMenuItemSprite addChild:z:tag:] ...

Это вызвано тем, что HUD не может быть добавлен снова в разных сценах. Это настоящее разочарование у меня с cocos2d.

Единственный способ решить эту проблему до сих пор - это иметь BaseScene который имеет HUD как CCLayer, и я загружаю все последующие CCLayers, используя NSNotifications.

Код теперь следует;

#define HudBase 1
#define kHUDTag 9000

#import "cocos2d.h"

@interface BaseScene : CCScene

@end

@class HeaderHUDLayer;
@interface BaseLayer : CCLayer
{
    CCNode *currentNode;
    CCLayer *thisLayer;
    HeaderHUDLayer *hud;
}

@property (nonatomic, retain) CCNode *currentNode;
@property (nonatomic, retain) CCLayer *thisLayer;
@property (nonatomic, retain) HeaderHUDLayer *hud;

-(void) changeNodeTo:(CCNode *)thisNode;
-(void) changeHUDSceneObserver:(NSNotification *)notification;

@end

@implementation BaseScene

- (id)init
{
    self = [super init];
    if (self) {
        [self addChild:[BaseLayer node] z:0 tag:800];
    }
    return self;
}
@end

@implementation BaseLayer
@synthesize currentNode, thisLayer;
@synthesize hud;

-(id) init
{
    if ((self =[super init]))
    {
        NSLog(@"HUD Scene");

        self.hud = [HeaderHUDLayer node];

        if (self.currentNode == nil)
        {
            self.currentNode = [MyDashboardScene node];
            [self changeNodeTo:self.currentNode];
        }

        [self addChild:self.hud z:TopZLayer];

        [[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(changeHUDSceneObserver:) name: @"changeHUDScene" object: nil];
    } // end if
    return self;
}


- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super dealloc];
}


-(void) changeNodeTo:(CCNode *)thisNode
{
    NSLog(@"changeNodeTo");
    [self removeChild:self.currentNode cleanup:YES];
    [self addChild:thisNode z:0];
    self.currentNode = thisNode;
}

-(void) changeHUDSceneObserver:(NSNotification *)notification
{
    NSLog(@"changeHUDSceneObserver = %@", notification.name);

    if ([notification.name isEqualToString:@"changeHUDScene"]==YES) {
        if ([notification object]!=nil) {
            [self changeNodeTo:[notification object]];
        }
    }
}

@end

Сам заголовок очень сложный, но в простых сроках у меня есть CCLabel, например:

NSString *txtCash = [NSString stringWithFormat:@"Cash: %d", [player.cash intValue]];
            self.lblCash = [CCLabelBMFont labelWithString:txtCash fntFile:@"font_minipixi_16.fnt"];
            [lblCash setTag:kCashLabelTag];
            [lblCash setAnchorPoint:CGPointMake(1, 0)];
            [lblCash setPosition:CGPointMake(100, 8)];
[self addChild:lblCash];

Это прекрасно работает, я могу перемещаться между различными частями игры, а верхний / нижний колонтитулы являются общими для всех сцен.

Однако использование NSNotifications для изменения узла вызывает у меня настоящую головную боль, а именно, я не могу изменить метки (lblCash) внутри HUD, и я не знаю, как решить эту проблему.

Например, если я нажимаю на CCMenuItem, который уменьшает мои деньги, метка HUD никогда не обновляется.

Чтобы обойти эту проблему, я использую другое NSNotification для решения этой проблемы. За исключением случаев, когда мне нужно иметь несколько NSNotifications (чтобы изменить заголовок, обновить страницу или что-то еще), и я в конечном итоге с ошибками утверждения, потому что я не могу повторно отправить вещи.

Бывают случаи, когда у меня есть NSNotification для обновления заголовка, а затем сразу же другой NSNotification, который перенаправляет узел на другой узел, который я хочу представить; опять это вызывает ошибку утверждения.

Я чувствую, что все это превращается в беспорядок NSNotifications повсюду и делает меня очень разочарованным cosos2d.

Я пытался:

Создание базовой сцены удаляет HUD (и всех ее детей) и повторно добавляет его. Это вызывает ошибку утверждения и не представляется возможным сделать. Я, вероятно, кодирую это неправильно, хотя

Попытка разыграть BaseScene/Layer и захватить HUD с помощью тега и таким образом обновить метку.

т.е.

    BaseLayer *baseLayer = (BaseLayer *) [self.parent getChildByTag:800];
    [baseLayer.hud updateCashAmount];

Ничего не делает (Базовые слои / сцены и слой HUD имеют теги).

Ни один не делает,

    HeaderHUDLayer *hud = (HeaderHUDLayer *)[base getChildByTag:kHUDTag];
    [hud updateCashAmount];

Я думаю, что причина, по которой я не могу выполнить кастинг, заключается в том, что NSNotifications изменили контекст.

Все это сделало меня очень расстроенным из-за cosos2d.

Все, что я хочу, это общий верхний / нижний колонтитул во многих сценах, я хочу иметь возможность обновлять метки заголовка без лишней суеты.

Учитывая это, как мне использовать общий CCLayer верхнего / нижнего колонтитула и сделать элементы внутри верхнего / нижнего колонтитула обновляемыми?

Есть ли более простой способ сделать это без необходимости загружать NSNotifications повсюду?

С благодарностью.

3 ответа

Решение

Я думаю, что у меня есть другое решение этой проблемы.

Напомним,

Я хочу иметь или совместно использовать общий верхний / нижний колонтитул для нескольких сцен / слоев.

Я использовал это как BaseScene (CCScene) с одним ребенком, который был моим HD, а затем я использовал наблюдателя NSNotification, чтобы изменить нижележащего ребенка (CCLayer).

Это стало очень грязным, и такие вещи, как HUD, перекрывали любые модальные диалоги.

Я пока не могу решить проблему модального диалога, но с точки зрения совместного использования общего колонтитула, я думаю, у меня может быть решение.

В книге Штеффена Иттерхейма "Изучите разработку игр cocos2d с iOS5" в главе 5, в ней говорится об использовании "полу-синглтона".

Далее следует цитата;

static MultiLayerScene* multiLayerSceneInstance;
+(MultiLayerScene*) sharedLayer {
NSAssert(multiLayerSceneInstance != nil, @"MultiLayerScene not available!");
return multiLayerSceneInstance; }

-(void) dealloc {
// MultiLayerScene will be deallocated now, you must set it to nil multiLayerSceneInstance = nil;
// don't forget to call "super dealloc"
[super dealloc]; }

Проще говоря, multiLayerSceneInstance - это статическая глобальная переменная, которая будет содержать текущий объект MultiLayerScene в течение его срока службы. Ключевое слово static обозначает, что переменная multiLayerSceneInstance доступна только в файле реализации, в котором она определена. В то же время она не является переменной экземпляра; он живет за пределами любого класса. Вот почему он определен вне любого метода, и к нему можно получить доступ в методах класса, таких как sharedLayer.

Доступ к GameLayer и UserInterfaceLayer предоставляется через методы получения свойств для простоты использования.

Это облегчает доступ к различным слоям из любого узла MultiLayerScene.

В игре с открытым исходным кодом, CastleHassle, автор использует похожую технику, которую я смог рассмотреть и "воспроизвести" таким образом, чтобы я мог перемещаться от сцены к сцене (они на самом деле являются CCLayers) и совместно использовать один и тот же общий заголовок без таковых. досадные ошибки "... ребенок уже добавил".

Я исследую это немного дальше, но CastleHassle очень помог мне с этой проблемой, и если я поэкспериментирую с кодом книги, у меня может быть дальнейшее решение.

Таким образом, я сделал так, чтобы использовать концепцию экземпляра согласно CastleHassle; но я посмотрю как книга справилась с ее концепцией общего экземпляра.

В проекте с общим слоем меню я использую этот метод для переноса слоя из одной сцены в другую (в Kobold2D этот метод уже доступен):

-(void) transferToNode:(CCNode*)targetNode
{
    NSAssert(self.parent != nil, @"self hasn't been added as child. Use addChild in this case, transferToNode is only for reassigning child nodes to another node");
    CCNode* selfNode = [self retain];
    [self removeFromParentAndCleanup:NO];
    [targetNode addChild:selfNode z:selfNode.zOrder tag:selfNode.tag];
    [selfNode release];
}

Это работает так:

CCScene* newScene = [MyNewScene node];
[menuLayer transferToNode:newScene];
[[CCDirector sharedDirector] replaceScene:newScene];

Теперь слой меню живет в следующей сцене. Вот и все.

PS: если вы используете ARC, пропустите строки сохранения и выпуска.

Я считаю, что я решил проблему.

Я решил это следующим образом:

  1. Используйте NSNotifications для перемещения между сценами / слоями, чтобы cocos2d не жаловался на уже добавленные узлы или другие ресурсы.
  2. В обозревателе NSNotification я обновляю заголовки или другие элементы на уровне HUD, которые мне нужно обновить.
  3. Используйте единый userData (словарь), который является общим для всей игры, и поэтому я могу передавать данные в / из заголовка через NSNotification.

Это в значительной степени хак, но я думаю, что теперь это работает нормально.

Например, в моей сцене / слое HUD я создаю NSNotification

[[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(changeHUDObserver:) name:@"hudStatus" object:nil];

Я также создаю наблюдателя

-(void) changeHUDObserver:(NSNotification *)notification
{
    NSLog(@"changeHUD.status = %@", notification.name);

// Получаем данные из синглтона состояния // Есть и другие способы сделать это, но это только пример Player *player = [state.userData objectForKey:@"player"];

// Update header cash label
    // Cash formatter
    NSLocale *usLocale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    [formatter setLocale:usLocale];
    [formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
    [formatter setMaximumFractionDigits:0];
    NSString *txtCash = [formatter stringFromNumber:player.cash];
    [formatter release];

    // Cash Label
    self.lblCash.string = txtCash;
} // end method

Это всего лишь пример, но теперь я могу отправлять и обновлять содержимое в шапке.

Хотя мой ответ взломан, я чувствую, что у @LearnCocos2d есть ответ, который следует рассматривать как возможную альтернативу.

Спасибо

Другие вопросы по тегам