Cocos2d - Initial app layer displays incorrectly on app load, displays appropriately after reload

513 views Asked by At

I'm using a GameManager singleton to handle some of the shared tasks required by my game. One of those tasks is loading individual game scenes. When the game starts up, whichever scene/layer combo I have displayed appears incorrectly; it looks like maybe the coordinates that items are positioned in relation to are wrong.

It doesn't matter which layer I choose–they all appear incorrectly. If you click on a button and load another scene/layer, then go back to the scene/layer in question, everything displays correctly. This only happens on retina display devices, so I think it might be related to the scaling I set for the different display types in the game manager. However, changing the scaling for Retina displays breaks things even more (the layers are too small).

Here's my AppDelegate.h

#import <UIKit/UIKit.h>

@class RootViewController;

@interface AppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow            *window;
    RootViewController  *viewController;
}

@property (nonatomic, retain) UIWindow *window;

@end

AppDelegat.m

#import "cocos2d.h"

#import "AppDelegate.h"
#import "GameConfig.h"
#import "RootViewController.h"
#import "GameplayScene.h"
#import "GameManager.h"

@implementation AppDelegate

@synthesize window;

- (void) removeStartupFlicker
{
#if GAME_AUTOROTATION == kGameAutorotationUIViewController
#endif
}


- (void) applicationDidFinishLaunching:(UIApplication*)application
{
    // Init the window
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // Try to use CADisplayLink director
    // if it fails (SDK < 3.1) use the default director
    if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
        [CCDirector setDirectorType:kCCDirectorTypeDefault];


    CCDirector *director = [CCDirector sharedDirector];

    // Init the View Controller
    viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
    viewController.wantsFullScreenLayout = YES;

    EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
                                   pixelFormat:kEAGLColorFormatRGB565   // kEAGLColorFormatRGBA8
                                   depthFormat:0                        // GL_DEPTH_COMPONENT16_OES
                        ];

    // attach the openglView to the director
    [director setOpenGLView:glView];
    [director setDeviceOrientation:kCCDeviceOrientationPortrait];

    [director setAnimationInterval:1.0/60];
    [director setDisplayFPS:YES];


    // make the OpenGLView a child of the view controller
    [viewController setView:glView];

    // make the View Controller a child of the main window
    [window addSubview: viewController.view];

    [window makeKeyAndVisible];

    [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];


    // Removes the startup flicker
    [self removeStartupFlicker];

    // Run the intro Scene
    //[[CCDirector sharedDirector] runWithScene:[GameplayScene node]];
    [[GameManager sharedGameManager] runSceneWithID:kMainMenuScene];
}

- (void)dealloc {
    [[CCDirector sharedDirector] end];
    [window release];
    [super dealloc];
}

GameManager.h

#import <Foundation/Foundation.h>
#import "Constants.h"
#import "CommonProtocols.h"

@interface GameManager : NSObject {
    BOOL isMusicON;
    BOOL isSoundEffectsON;
    BOOL hasPlayerDied;
    BOOL newHighScore;
    BOOL newBestTime;
    BOOL isUiTextLeft;
    int currentScore;
    int highScore;
    int lengthPlayed;
    int bestTime;
    int randomPurple;
    int randomGreen;
    int timeBonus;
    int timeTillDeath;
    int uiBackgroundHeight;
    CharacterStates previousPurpleState;
    CharacterStates previousGreenState;
    SceneTypes currentScene;
}

@property (readwrite) BOOL isMusicON;
@property (readwrite) BOOL isSoundEffectsON;
@property (readwrite) BOOL hasPlayerDied;
@property (readwrite) BOOL newHighScore;
@property (readwrite) BOOL newBestTime;
@property (readwrite) BOOL isUiTextLeft;
@property (readwrite) int currentScore;
@property (readwrite) int highScore;
@property (readwrite) int lengthPlayed;
@property (readwrite) int bestTime;
@property (readwrite) int randomPurple;
@property (readwrite) int randomGreen;
@property (readwrite) int uiBackgroundHeight;
@property (readwrite) CharacterStates previousPurpleState;
@property (readwrite) CharacterStates previousGreenState;
@property (readwrite) int timeBonus;
@property (readwrite) int timeTillDeath;

+(GameManager*)sharedGameManager;
-(void)runSceneWithID:(SceneTypes)sceneID;
-(void)openSiteWithLinkType:(LinkTypes)linkTypeToOpen ;

@end

GameManager.m

#import "GameManager.h"
#import "GameplayScene.h"
#import "MainMenuScene.h"
#import "OptionsScene.h"
#import "CreditsScene.h"
#import "IntroScene.h"
#import "LevelCompleteScene.h"

@implementation GameManager

static GameManager* _sharedGameManager = nil; 
@synthesize isMusicON;
@synthesize isSoundEffectsON;
@synthesize hasPlayerDied;
@synthesize newHighScore;
@synthesize newBestTime;
@synthesize isUiTextLeft;
@synthesize currentScore;
@synthesize highScore;
@synthesize lengthPlayed;
@synthesize bestTime;
@synthesize previousPurpleState;
@synthesize previousGreenState;
@synthesize randomPurple;
@synthesize randomGreen;
@synthesize timeBonus;
@synthesize timeTillDeath;
@synthesize uiBackgroundHeight;

+(GameManager*)sharedGameManager {
    @synchronized([GameManager class])
    {
        if(!_sharedGameManager)
            [[self alloc] init]; 
        return _sharedGameManager;
    }
    return nil; 
}

+(id)alloc {
    @synchronized([GameManager class]){
        NSAssert(_sharedGameManager == nil, @"Attempted to allocate a second instance of the Game Manager singleton");
        _sharedGameManager = [super alloc];
        return _sharedGameManager;
    }
    return nil;
}

-(id) init {
    self = [super init];
    if (self != nil) {
        // Game manager initialized
        CCLOG(@"Game manager singleton, init");
        hasPlayerDied = NO;
        // DECODING INFO FROM DEFAULTS
        NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [dirPaths objectAtIndex:0];

        NSMutableData *gameData;
        NSKeyedUnarchiver *decoder;

        NSString *documentPath = [documentsDirectory stringByAppendingPathComponent:@"gameState.dat"];
        gameData = [NSData dataWithContentsOfFile:documentPath];

        if (gameData) {
            decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:gameData];

            highScore = [decoder decodeIntForKey:@"highScore"];
            bestTime = [decoder decodeIntForKey:@"bestTime"];
            isMusicON = [decoder decodeBoolForKey:@"isMusicON"];
            isSoundEffectsON = [decoder decodeBoolForKey:@"isSoundEffectsON"];
            isUiTextLeft = [decoder decodeBoolForKey:@"isUiTextLeft"];
            //currentScore = [decoder decodeIntForKey:@"currentScore"];

            [decoder release];
        } else {
            highScore = 0;
            bestTime = 0;
            isMusicON = TRUE;
            isSoundEffectsON = TRUE;
            isUiTextLeft = TRUE;
            //currentScore = 0;
        }

        CCLOG(@"Music - %s", isMusicON ? "true" : "false");
        CCLOG(@"Sound - %s", isSoundEffectsON ? "true" : "false");
        //****************************
        currentScore = 0;
        timeBonus = 0;
        timeTillDeath = 0;
        uiBackgroundHeight = 0;
        currentScene = kNoSceneUninitialized;
    }

    return self;
}

-(void)runSceneWithID:(SceneTypes)sceneID {

    SceneTypes oldScene = currentScene;
    currentScene = sceneID;
    id sceneToRun = nil;

    switch (sceneID) {
        case kMainMenuScene:
            sceneToRun = [MainMenuScene node];
            break;
        case kOptionsScene:
            sceneToRun = [OptionsScene node];
            break;
        case kCreditsScene:
            sceneToRun = [CreditsScene node];
            break;
        case kIntroScene:
            sceneToRun = [IntroScene node];
            break;
        case kLevelCompleteScene:
            sceneToRun = [LevelCompleteScene node];
            break;
        case kGameplayScene:
            sceneToRun = [GameplayScene node];
            break;
        default:
            CCLOG(@"Unknown ID, cannot switch scenes");
            return;
            break;
    }

    if (sceneToRun == nil) {
        // Revert back, since no new scene was found
        currentScene = oldScene;
        return;
    }

    // Menu Scenes have a value of < 100
    if (sceneID < 100) {

        if ([[CCDirector sharedDirector] enableRetinaDisplay:YES]) {
            // iPhone 4 Retina
            [sceneToRun setScaleX:1.0f];
            [sceneToRun setScaleY:1.0f];
            CCLOG(@"GM:Scaling for iPhone 4 (retina)");
        } else {
            [sceneToRun setScaleX:1.0f];
            [sceneToRun setScaleY:1.0f];
            CCLOG(@"GM:Scaling for iPhone 3G(non-retina)");
        }
    }

    if ([[CCDirector sharedDirector] runningScene] == nil) {
        [[CCDirector sharedDirector] runWithScene:sceneToRun];
    } else {
        [[CCDirector sharedDirector] replaceScene:sceneToRun];
    }
}
2

There are 2 answers

3
Haroon On

You are calling node with the scene objects call [MainMenuScene scene];and try

and make this method with return type CCScene* and use to get return the scene and write

[[CCDirector sharedDirector]runWithScene:[obj runSceneWithID:1]];

Edit --

-(CCScene*)runSceneWithID:(SceneTypes)sceneID {

    SceneTypes oldScene = currentScene;
    currentScene = sceneID;
    id sceneToRun = nil;

    switch (sceneID) {
        case kMainMenuScene:
            sceneToRun = [MainMenuScene node];
            break;
        case kOptionsScene:
            sceneToRun = [OptionsScene node];
            break;
        case kCreditsScene:
            sceneToRun = [CreditsScene node];
            break;
        case kIntroScene:
            sceneToRun = [IntroScene node];
            break;
        case kLevelCompleteScene:
            sceneToRun = [LevelCompleteScene node];
            break;
        case kGameplayScene:
            sceneToRun = [GameplayScene node];
            break;
        default:
            CCLOG(@"Unknown ID, cannot switch scenes");
            return nil;
            break;
    }

    if (sceneToRun == nil) {
        // Revert back, since no new scene was found
        currentScene = oldScene;

    }
    return sceneToRun;
}
0
sergio On

I ran into a similar problem once, but I can't seem to relate it directly to your code.

In short, the issue is that while I am building my layer/scene in the init method, the nodeToWorldTransformation (at least, possibly others as well) is not set. So, if you are trying to calculate the world coordinates (or possibly the size in world coordinates) of something, it does not work.

Now, in may case, I used that transform to calculate a scale factor at init time, and it was wrong. If I delayed the calculation to after the transformation was set, then everything was ok. The transformation becomes ready only after the layer you are creating has been added to its parent, but I am not exactly sure when it does. Finally, in my case I simply hardcoded the transformation, knowing all dimensions at init time.

It seems that your case could be analogous to this, but, as I said, I cannot relate this to your code.