A SpriteKit game presents MainMenuScene then LevelSelectionScene and finally the GamePlayScene. Once the game is over, in GamePlayScene, the user is transferred back to MainMenuScene like so:
MainMenuScene *newScene = [MainMenuScene sceneWithSize:self.view.bounds.size];
[self.view presentScene:newScene transition:[SKTransition fadeWithDuration:0.5]];
It takes me to the scene but the buttons, that worked previously, don't work anymore. I press them, they seem to do their little animation as if they're being pressed but they don't present me appropriate scenes they're supposed to.
I have a very specific bundle of code activated every time a scene is about to be left and I don't actually know if it influences this whole conundrum:
-(void)willMoveFromView:(SKView *)view
{
/* Remove anything stuck in memory. */
[self removeAllActions];
[self removeAllChildren];
[self removeFromParent];
[self.scene removeAllActions];
[self.scene removeAllChildren];
[self.scene removeFromParent];
//[self.scene.view removeFromSuperview]; <--TOTALLY DESTROYS ALLS SCENES IN THE GAME. Don't use it.
}
Has any had previously working scenes presented as partially working duds?
From what you said looks like the button is triggering the animation but not the action, still not much information to be sure how to exactly fix it, probably the creation of the actions of the button is working. Double check if the part of the code that creates the SKActions is actually running and active.
So I poked around and found out the culprit is in the class I used to create buttons. It's called AGSpriteButton and I highly recommend it (You can find it on github). It has built-in SELECTORS, ACTIONS and BLOCKS functionality offered by default that is very easy to tap into. I'm not sure as to why, but SELECTORS stopped functioning correctly. I just switched to ACTIONS calling my methods & everything works perfectly.
Related
I have a CCB file with a timeline animation in it. I load the file like this.
CCSprite *spriteAnimation = (CCSprite*)[CCBReader load:#"MyGreatAnimation"];
spriteAnimation.paused = TRUE;
At some point later, I add it to the scene and run the animation
[MyScene addChild:spriteAnimation];
CCAnimationManager* animationManager = _deletionAnimaion.userObject;
[animationManager runAnimationsForSequenceNamed:#"Default Timeline"];
This is great. My animation runs. I then remove spriteAnimation from the scene until I need it again.
[spriteAnimation removeFromParent];
.
The problem
I can't figure out how to get the animation to run the next time I add it to the scene.
I've tried:
[animationManager jumpToSequenceNamed:#"Default Timeline" time:0];
And also..
[animationManager runAnimationsForSequenceNamed:#"Default Timeline"];
But the animation doesn't seem to run. If at this point I call:
spriteAnimation.userObject.runningSequenceName
to see the running sequence, it returns NULL.
.
My Question
How do I arbitrarily run a timeline animation repeatedly?
To be clear, I'm not asking about looping the animation. I want to start it from frame 1 whenever I need.
If you need it repeatedly, you shouldn't remove it from the scene:
[spriteAnimation removeFromParent];
Instead, just make the animation invisible for the time being:
spriteAnimation.visible = NO;
Later just make it visible again.
After all when you remove a node it is gone from the scene hierarchy unless you addChild: it back in which I can't see in your code above.
I have a scene that is calling the next one using a transition like this:
SKTransition *transition = [SKTransition revealWithDirection:SKTransitionDirectionDown duration:0.5];
SKView *skView = (SKView *)self.view;
SKScene * scene = [[GameOverScene alloc] initWithSize:self.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:scene transition:transition];
The elements that compose GameOverScene (buttons, images, etc.) are added on its init method.
The problem is that the transition is not seen. One scene immediately cuts to the other one.
I guess that transition happens before the next scene has a chance to build its elements.
I have tried to move the creation of the next scene to didMoveToView without success.
For test purposes I have tried to delay the presentScene line in times even bigger than 2 seconds. When I do that I barely see the end frames of the transition.
How do I do that? What is the correct way of building the next scene and doing a transition that works.
If the new scene is particular resource heavy, i.e., requires a lot of texture loading, that will delay any frame rendering. If the texture loading takes longer than your transition time, you will miss all of it because the first frame that will be displayed has been rendered after your transition is finished.
I ran into this as well, although I was better able to determine the root cause because I had a transition of 2 seconds of which only the last 0.5 seconds were shown.
How to fix this? Preload your textures. See Apple Docs.
+ (void)preloadTextures:(NSArray *)textures withCompletionHandler:(void (^)(void))completionHandler
I should emphasize the differences between SKSprite, SKTexture and your "image.png".
SKSprite draws itself based on its texture property (or background color), and size. You can use one SKTexture for many sprites. In turn, an image file ("image.png") can supply multiple SKTexture objects.
Important: you want to use textures from the array of SKTexture objects passed to the method above to actually benefit from the texture loading. This will require some form of texture management.
If your problem is indeed texture related, let me know if you need me to expand on the texture management. A related post (which may be a bit dry) can be found here: SO sktexture-preloading.
I wrote exactly the same code in my app to show the game over scene with one minor difference. My code is as follows:
- (void)showGameOverScene
{
SKTransition* reveal = [SKTransition doorsCloseVerticalWithDuration:0.5];
SKScene* gameOverScene = [[RSGameOverScene alloc] initWithSize:self.size];
[self.view presentScene:gameOverScene transition: reveal];
}
And it's been working like a charm. The only difference is that I am not calling:
scene.scaleMode = SKSceneScaleModeAspectFill;
In my RSGameOverScene I set up nodes in -(id)initWithSize:(CGSize)size, and again there is no problems. Everything just works.
I can't see a reason why this line could be a source of a problem but you might want to try commenting it out to see if it helps. However your code looks ok so the source of the problem can be somewhere else.
I have a subclass of SKSpriteNode (monsterNode). It automatically runs around the screen using vectors to follow the player. I am currently using the following action to make it run around:
SKAction *actionMove = [SKAction moveTo:actualDistance duration:time];
[self runAction:actionMove completion:^ {
_currentState = SVGMonsterStateIdle;
}];
I am wondering if its possible to make it so the monsterNode actually STOPS running the action if it hits the boundary of the iOS device screen. I currently have SKSpriteNode boundaries on the edges of the screen, linked with a contact delegate to notify if the monster and walls make contact. However, that means nothing if I can't actually stop the monster's actionMove action from going to completion. The monster needs to stop at the boundaries of the screen. If it is not possible to stop an SKAction mid-execution, is there a roundabout way to do so?
Look at the SKNode.h header file - it has two functions listed:
- (void)removeActionForKey:(NSString *)key;
- (void)removeAllActions;
The latter will work: [monsterNode removeAllActions];
I've found a peculiarity with Cocos2D and I cannot seem to fix it. From the AppDelegate I load into a Menu, which is a CCScene. The scene holds a CCLayer, which itself holds the CCMenu. Everything works find the first time through. After my game has ended, I bring the user to a GameOverScene and prompt them to return to the menu. I am reloading the menu scene and calling [[CCDirector sharedDirector] replaceScene:menu]. After entering this menu, though, not all input functions. I can no longer tap on menu items, but I can pan / multi-touch on them to trigger the item. What gives?
Is there some way on initialization to reset the CCScene to receive input, and if so, will this mess up CCMenu's input receiving?
Did you override any of the on* methods like onEnter, onExit, etc. in any of your classes?
If so, you must call the super implementation (ie [super onEnter]) in each, otherwise some cocos2d functionality like scheduling or input may stop working.
I finally figured it out, and the answer was right under my nose. Earlier in my game development I needed a way to stop KKInput from swallowing gestures. I'm not entirely sure of the ramifications of this action, but I was able to do so like this:
KKInput* input = [KKInput sharedInput];
UITapGestureRecognizer* tapGestureRecognizer;
tapGestureRecognizer = input.tapGestureRecognizer;
tapGestureRecognizer.cancelsTouchesInView = NO
It seems that the default Kobold2D behavior is to swallow all touches, which was preventing the CCMenu from receiving any tap gesture.
I have an app that uses sprite sheets and is a Cocos2D/UIKit app. In the init method of my scene I do this:
spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"TheSpriteSheet.pvr.ccz"];
[self addChild:spriteSheet z:1];
Now when the game is over, I do this:
[[[CCDirector sharedDirector] actionManager] removeAllActions];
[self unscheduleAllSelectors];
This causes the CCSprites that are currently still as a child to remain there so once I finish my UIKit view transition to my other UIViewController I try to call a method back in my CCLayer class (the class the scene is in) to remove all the children that were in my CCSpriteBatchNode, the app crashes with EXC_BAD_ACCESS.
Now I HAVE to call the method at this point because I want to achieve the effect of the enemies still being on screen while I am doing my custom view transition so they don't magically disappear before I finish switching views. Also I do not do replaceScene anywhere or release my batch node explicitly so I don't know why this is happening.
Anyway how would I fix this issue?
Thanks!