SpriteKit problems with SKScene - ios

I have performance problems using iOS Sprite kit.
I did the main scene of the game and every thing run nice and smoothly, then I inserted a splash windows in the beginning and when I change the scene to the main scene the game seems laggy with frame drop, but fps, cpu and memory parameters are Ok.
I also have a game over screen and the behaviour is the same, when the game return to the main scene looks very laggy.
I have tried with [self removeFromParents] and removing children, and nothing happens.
Code:
#implementation Splash
- (id) initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
SKSpriteNode *sprite= [SKSpriteNode spriteNodeWithImageNamed:#"main"];
sprite.position = CGPointMake(160,284);
[self addChild:sprite];
}
return self;
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// Transition back to the Game
SKScene *myScene = [[MyScene alloc] initWithSize:self.size];
SKTransition *reveal = [SKTransition fadeWithDuration:0.5];
[self.view presentScene:myScene transition:reveal];
[self removeAllChildren];
[self removeAllActions];
[self removeFromParent];
}
#end

Related

Scene not changing in SpriteKit

I've created a game and now I want to add a start screen in which I want to run a video and after that there will be a screen with start button, I've searched over internet but couldn't find out the way to do it, have tried many things, the latest code is:
#import "FirstScreen.h"
#import "GameScene.h"
#import <MediaPlayer/MediaPlayer.h>
#implementation FirstScreen
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
NSURL *url =[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Intro" ofType:#"mp4"]];
_player = [[MPMoviePlayerController alloc] initWithContentURL:url];
_player.view.frame = CGRectMake(0,0, 1024, 768);
[self.view addSubview:_player.view];
_player.shouldAutoplay = YES;
[_player prepareToPlay];
NSString *nextSceneButton;
nextSceneButton = #"Start";
SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
myLabel.text = nextSceneButton;
myLabel.fontSize = 30;
myLabel.fontColor = [SKColor blackColor];
myLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
myLabel.name = #"Start";
[self addChild:myLabel];
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"Start"]) {
SKTransition *reveal = [SKTransition fadeWithDuration:3];
GameScene *scene = [GameScene sceneWithSize:self.view.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[self.view presentScene:scene transition:reveal];
}
}
#end
App just starts and give the SIGABRT error. Can you tell me how to do it?
You should check few things to make sure is everything okay there:
Check if everything is okay with file name, look for spaces or typos etc...
See what pathForResource returns (println it)
See what fileURLWithPath returns
Go to : Target -> Build Phases -> Copy Bundle Resources and add Intro.mp4 file there. If that file is already added , delete it, clean your project Project->Clean, and add it again. Build project and run it.

Using SpriteKit template, Swipe gesture lag between Scenes

I'm building an app using Spritekit, I use UISwipeGestureRecognizer to swipe between scenes.
Now, my app is going to have a lot of scenes based on the same code, and the swipe gesture works well, except once I add a third or fourth scene I get a lot of swipe lag.
I'm not sure if it's because I'm using too many scenes or if it's because each scene uses a background image.
The swipe works but it suffers from a significant lag.
Am I the only one getting this issue? Has anyone found a work around for this kind of lag?
Any help would be appreciated.
Here's an example of the code used for 1 scene.
Thank you
#import "Ep1Slide1.h"
#import "Ep1Slide2.h"
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.backgroundColor = [SKColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1.0];
CGImageRef backgroundCGImage = [UIImage imageNamed:#"backgroundImage1"].CGImage;
SKTexture *backgroundTexture = [SKTexture textureWithCGImage:backgroundCGImage];
SKSpriteNode *someNode = [SKSpriteNode spriteNodeWithTexture:backgroundTexture];
someNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[someNode setScale:0.5f];
[self addChild:someNode];
}
return self;
}
- (void)didMoveToView:(SKView *)view{
UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action: #selector(leftFlip:)];
[leftSwipe setDirection:UISwipeGestureRecognizerDirectionLeft];
[self.view addGestureRecognizer:leftSwipe];
}
- (void)leftFlip:(id)sender{
SKView * skView = (SKView *)self.view;
SKScene * scene = [Ep1Slide2 sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:scene transition:[SKTransition pushWithDirection:SKTransitionDirectionLeft duration:1.2]];
}

CCButton not working completely when pressed in Cocos2D v3 iOS

I have a gameplay scene, over which I add a CCNode as a child. My Game Over node has a replay CCButton in it.
The button is supposed to restart the game play scene. The problem is, when I press the "Restart" button, it goes through the lines but it doesn't perfrom replaceScene. Also it doesn't highlight when pressed. Here's my relevant code:
The code where I add the Game Over Node in my GamePlay Class (.m):
CCNode GameOver = [[GameOverNode alloc] init];
[self unscheduleAllSelectors];
[self stopAllActions];
[[OALSimpleAudio sharedInstance] stopBg];
[[CCDirector sharedDirector] stopAnimation];
[[CCDirector sharedDirector] pause];
[self addChild:GameOver z:5];
and here's the code for GameOver Class (.h):
#interface GameOverNode:CCNode {
CCButton *_aButton;
}
#property (nonatomic, retain) CCButton *aButton;
- (id)init;
- (void)ButtonPressed:(id)sender;
and Game Over (.m):
-(id)init {
if ( self = [super init] ){
CCSpriteFrame *replayFrame = [CCSpriteFrame frameWithImageNamed:#"Replay.png"];
_aButton = [CCButton buttonWithTitle:#"" spriteFrame:replayFrame];
_aButton.position = ccp(200,200);
[_aButton setTarget:self selector:#selector(ButtonPressed:)];
[self addChild:_aButton z:2];
}
return self;
}
- (void)ButtonPressed:(id)sender
{
NSLog(#"Button pressed");
CCTransition* t = [CCTransition transitionFadeWithDuration:0.4f];
t.outgoingSceneAnimated = YES;
t.incomingSceneAnimated = YES;
[[CCDirector sharedDirector] replaceScene:[GamePlayScene scene] withTransition:t];
}
The thing is, it prints out "Button pressed", also goes through the rest of the code of the method, but nothing happens.
I'll appreciate if you can let me know what I am doing wrong.
Thanks!
It does not work because you have paused the CCDirector. Remove the following line:
[[CCDirector sharedDirector] pause];
Alternatively if you really need that, resume the director before you attempt to replace the scene.
[[CCDirector sharedDirector] resume];
[[CCDirector sharedDirector] replaceScene:[GamePlayScene scene] withTransition:t];

Restarting game/scene from another layer

I’m having a real headache trying to diagnose this problem for quiet awhile now.
Details:
I have the following nodes:
GameLayer; the main game layer, also a semi-singleton.
HUDLayer; a simple HUD layer (for score and pause button), added to the scene along with GameLayer of course.
PauseLayer; a CCLayerColor subclass which holds a simple menu (resume, restart, and main menu).
The PauseLayer is added to the GameLayer as a child whenever the pause button is pressed.
Problem:
Restarting the game/scene works fine by: [self restartGame] from within GameLayer itself.
But invoking the same method from PauseLayer by using: [[GameLayer sharedGameLayer] restartGame] does not completely replace the GameLayer scene with a new instance; old scene is still there with no childs, same old score, etc, albeit GameLayer’s dealloc is being called (Only if no transition is used).
Code:
GameLayer
static GameLayer *sharedGameLayer;
#implementation GameLayer
- (id)init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super's" return value
if(self = [super initWithColor:ccc4(255, 255, 255, 255) width:[CCDirector sharedDirector].winSize.width height:[CCDirector sharedDirector].winSize.height])
{
NSLog(#"%s", __PRETTY_FUNCTION__);
sharedGameLayer = self;
.
.
}
+ (GameLayer *)sharedGameLayer {
#synchronized(self) {
if (sharedGameLayer == nil) {
sharedGameLayer = [[self alloc] init];
}
}
return sharedGameLayer;
}
- (void)pauseGame
{
NSLog(#"%s", __PRETTY_FUNCTION__);
if (![[CCDirector sharedDirector] isPaused])
{
// Disable the top menu.
self.hud.topMenu.enabled = NO;
// Pause game.
[[CCDirector sharedDirector] pause];
// Pause the background music.
[[SimpleAudioEngine sharedEngine] pauseBackgroundMusic];
// Disable touch detection.
[[CCDirector sharedDirector].touchDispatcher removeDelegate:self];
// Add the Pause Layer.
PauseLayer *pauseLayer = [[PauseLayer alloc] init];
pauseLayer.tag = 2;
pauseLayer.zOrder = 10;
[self addChild:pauseLayer z:10 tag:2];
}
}
- (void)restartGame
{
NSLog(#"%s", __PRETTY_FUNCTION__);
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[CCDirector sharedDirector].touchDispatcher removeAllDelegates];
[self stopAllActions];
[self unscheduleAllSelectors];
[self unscheduleUpdate];
[self removeAllChildrenWithCleanup:YES];
[[CCDirector sharedDirector] replaceScene:[GameLayer scene]];
//[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:[GameLayer scene]]];
}
- (void)dealloc
{
NSLog(#"%s", __PRETTY_FUNCTION__);
sharedGameLayer = nil;
}
#end
PauseLayer
- (id)init
{
if (self = [super initWithColor:ccc4(0, 0, 0, 128) width:[CCDirector sharedDirector].winSize.width height:[CCDirector sharedDirector].winSize.height])
{
// Create a menu.
// Resume button.
CCLabelTTF *resumeLabel = [CCLabelTTF labelWithString:#"Resume" fontName:#"Marker Felt" fontSize:32];
resumeLabel.color = ccc3(240, 240, 240);
CCMenuItemLabel *resumeButton = [CCMenuItemLabel itemWithLabel:resumeLabel block:^(id sender) {
[[GameLayer sharedGameLayer] resumeGame];
}];
// Restart Game button.
CCLabelTTF *restartLabel = [CCLabelTTF labelWithString:#"Restart" fontName:#"Marker Felt" fontSize:32];
restartLabel.color = ccc3(240, 240, 240);
CCMenuItemLabel *restartButton = [CCMenuItemLabel itemWithLabel:restartLabel block:^(id sender) {
[[GameLayer sharedGameLayer] restartGame];
}];
// Main Menu button.
CCLabelTTF *mainMenuLabel = [CCLabelTTF labelWithString:#"Main Menu" fontName:#"Marker Felt" fontSize:32];
mainMenuLabel.color = ccc3(240, 240, 240);
CCMenuItemLabel *mainMenuButton = [CCMenuItemLabel itemWithLabel:mainMenuLabel block:^(id sender) {
[[GameLayer sharedGameLayer] gotoMainMenu];
}];
CCMenu *menu = [CCMenu menuWithItems:resumeButton, restartButton, mainMenuButton, nil];
[menu alignItemsVerticallyWithPadding:10.0];
menu.position = ccp([CCDirector sharedDirector].winSize.width / 2, [CCDirector sharedDirector].winSize.height / 2);
[self addChild:menu];
}
return self;
}
EDIT:
If I add the Pause layer (as a child to GameLayer, and always visible) in the GameLayer's init method, everything works perfectly.. which is a bit weird.
Any input is highly appreciated.
Thanks in advance!
from the line
static GameLayer *sharedGameLayer;
You needed to write this line before #implementation class line to make it isn't class member variable. and at sharedGameLayer method you should change it to
+ (GameLayer *)sharedGameLayer {
#synchronized(self) {
if (sharedGameLayer == nil) {
sharedGameLayer = [[self alloc] init];
}
}
return sharedGameLayer;
}
And, when you define game layer class you don't call init method but you should call sharedGameLayer to make game layer class be singleton.
I would not suggest using singleton pattern on CCLayers, however did you override / edit the CCScene wrapper so it doesn't call init.
For instance
+(id)scene
{
CCScene* scene = [CCScene node];
[scene addChild:[GameLayer sharedGameLayer]];
return scene;
}
Then when you call
[[CCDirector sharedDirector] replaceScene:[GameLayer scene]];
Remember to restart your game right after
[[GameLayer sharedInstance] restart];
Or you could do that in the scene wrapper method.
Hope I got your point.
Atleast this will save you from reallocating space for the game layer all the time hehe..
The culprit of the problem was calling the CCDirector's ReplaceScene: method after pausing the game using the CCDirector's Pause method. What I did was implementing my own "Pause" functionality mainly using: [self pauseSchedulerAndActions]; and also in my case: [[self.spidersBatchNode children] makeObjectsPerformSelector:#selector(pauseSchedulerAndActions)]; for all the children inside of the spriteBatchNode. Another problem arised for which I'll make another question.Thanks for all the replies.

My Cocos2d CCMenu won't responding to touch

Hi there when my game reaches it's Game Over Scene i have a button to return to the Main menu and for some reason this has stopped working
Here is the code that leads to my Game Over Scene:
[[CCDirector sharedDirector] pause];
[[CCDirector sharedDirector] replaceScene:[ResultsScreen node]];
In my Init function for Game Over
....
mainMenu = [CCMenuItemImage itemFromNormalImage:#"MainMenu.png" selectedImage:#"MainMenuClick.png" target:self selector:#selector(Menu:)];
[mainMenu setAnchorPoint:ccp(0.0f, 0.0f)];
menu = [[CCMenu menuWithItems:mainMenu, nil] retain];
menu.isTouchEnabled = YES;
[menu setPosition:ccp(75, 80)];
[menu setAnchorPoint:ccp(0, 0)];
[self addChild:menu z:Z_INTERACTION];
....
My Selector Function for the button
- (void) Menu:(id)sender
{
NSLog(#"Exit to MainMenu");
[[CCDirector sharedDirector] replaceScene:[MainMenu node]];
}
Now I've compared this with my other code, all my other buttons are working, the only difference is that is that i am specifying a 'z' value.
Any help would be very much appreciated
Update:
Ran a quick check with Leaks and Allocations and nothing seems to be an issue
I found my Issue in my - (void) onEnter {}
I wasn't calling [super onEnter];

Resources