SpriteKit Pause and Resume SKView - ios

I want to Pause and Unpause a Scene in SpriteKit, with 2 Buttons on the same position.
While the Scene is running, I want to show the 'Pause' Button.
While the Scene is paused, I want to hide the 'Pause' Button and show the 'Play' Button.
In SpriteKit you can use self.scene.view.paused which is defined in SpriteKit.
My Code:
#implementation MyScene {
SKSpriteNode *PauseButton;
SKSpriteNode *PlayButton;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
[self Pause];
}
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:#"PauseButton"]){
self.scene.view.paused = YES;
[PauseButton removeFromParent];
[self Resume];
}
if([Node.name isEqualToString:#"PlayButton"]){
self.scene.view.paused = NO;
[PlayButton removeFromParent];
[self Pause];
}
}
-(void)Pause{
PauseButton = [SKSpriteNode spriteNodeWithImageNamed:#"Pause.png"];
PauseButton.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 1.04);
PauseButton.zPosition = 3;
PauseButton.size = CGSizeMake(40, 40);
PauseButton.name = #"PauseButton";
[self addChild:PauseButton];
}
-(void)Resume{
PlayButton = [SKSpriteNode spriteNodeWithImageNamed:#"Play.png"];
PlayButton.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 1.04);
PlayButton.zPosition = 3;
PlayButton.size = CGSizeMake(60, 60);
PlayButton.name = #"PlayButton";
[self addChild:PlayButton];
}
It pauses the Scene, but the there is still the Pause Button, and if I touch the Pause Button again, the Scene resumes. So now only the Images won't change. How can I fix this?

You can't update the button (or anything else in the scene) while the SKView is paused. In your touchesBegan method, you are pausing the view before updating the button (changing the order won't work). You will need to return to the run loop so your button is updated before pausing the game. Here's one way to do that:
This calls a method to pause the view after a short delay. Add this after your [self Resume] statement in touchesBegan, and delete self.scene.view.paused = YES.
[self performSelector:#selector(pauseGame) withObject:nil afterDelay:1/60.0];
This method pauses the SKView. Add this to your MyScene.m
- (void) pauseGame
{
self.scene.view.paused = YES;
}

Related

Issues with touchesBegan method

So the problem is that when I start it the soundOff button is above the soundOn but it's invisible. So all I see is the soundOn button, when I try to tap the soundOn button it really is just hitting the soundOff button and not giving it a chance to run the touchesBegan method properly.
#implementation GameScene
{
SKSpriteNode *soundLogo;
SKSpriteNode *soundOff;
}
-(void) addSoundOff:(CGSize)size {
soundOff = [SKSpriteNode spriteNodeWithImageNamed:#"soundOff"];
//resize sprite
soundOff.size = CGSizeMake(soundOff.size.width/2.25, soundOff.size.height/2.25);
//position it
soundOff.position = CGPointMake(65, 25);
//name sound off
soundOff.name = #"soundOff";
soundOff.alpha = 0;
[self addChild:soundOff];
}
-(void) addSoundOn:(CGSize)size {
SKTexture *soundOn = [SKTexture textureWithImageNamed:#"soundLogo"];
soundLogo = [SKSpriteNode spriteNodeWithTexture:soundOn];
//resize sprite
soundLogo.size = CGSizeMake(soundLogo.size.width/2.25, soundLogo.size.height/2.25);
//position sprite
CGPoint myPoint = CGPointMake(65, 25);
soundLogo.position = myPoint;
//name sound logo
soundLogo.name = #"soundOn";
//add action
[self addChild:soundLogo];
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//sound logo pressed to turn sound on/off
if ([node.name isEqualToString:#"soundOn"]) {
soundOff.alpha = 1;
soundLogo.alpha = 0;
NSLog(#"sound on is pressed");
}
if ([node.name isEqualToString:#"soundOff"]) {
soundOff.alpha = 0;
soundLogo.alpha = 1;
NSLog(#"sound off is pressed");
}
}
Rather then changing the alpha to 0/1 what you can do is remove sprite from parent and you can add to child.
Don't add sound off and sound on button at same time first add the default button that is sound on.
now when the sound on button is clicked then remove the sound on button from parent and add sound off.
-(void) addSoundOff:(CGSize)size {
soundOff = [SKSpriteNode spriteNodeWithImageNamed:#"soundOff"];
//resize sprite
soundOff.size = CGSizeMake(soundOff.size.width/2.25, soundOff.size.height/2.25);
//position it
soundOff.position = CGPointMake(65, 25);
//name sound off
soundOff.name = #"soundOff";
soundOff.alpha = 0;
//-------------Remove below line-----------------//
//[self addChild:soundOff];
//Dont add sound off button.....
}
-(void) addSoundOn:(CGSize)size {
SKTexture *soundOn = [SKTexture textureWithImageNamed:#"soundLogo"];
soundLogo = [SKSpriteNode spriteNodeWithTexture:soundOn];
//resize sprite
soundLogo.size = CGSizeMake(soundLogo.size.width/2.25, soundLogo.size.height/2.25);
//position sprite
CGPoint myPoint = CGPointMake(65, 25);
soundLogo.position = myPoint;
//name sound logo
soundLogo.name = #"soundOn";
//add action
[self addChild:soundLogo];
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//sound logo pressed to turn sound on/off
if ([node.name isEqualToString:#"soundOn"]) {
[soundLogo removeFromParent];
[self addChild:soundOff];
NSLog(#"sound on is pressed");
}
if ([node.name isEqualToString:#"soundOff"]) {
[soundOff removeFromParent];
[self addChild:soundLogo];
}
}

stop impulse on SKSpriteKit click

i'm trying to create an pause button in my game. at the moment when you click on the screen it will apply an impulse on the mover spriteNode. The problem is that it still applying the impulse when i click the pause button. How can i create an pause button without creating the impulse also.
i've tried changing the if statements to an else if and then making the last impulse part the else part, but this wont work.
touchBegan method:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//if fire button touched, bring the rain
if ([node.name isEqualToString:#"repeat"]) {
SKTransition *reveal = [SKTransition fadeWithDuration:2.0 ];
MyScene *newScene = [[MyScene alloc] initWithSize: CGSizeMake(self.size.width,self.size.height)];
// Optionally, insert code to configure the new scene.
[self.scene.view presentScene: newScene transition: reveal];
}
if ([node.name isEqualToString:#"home"]) {
SKTransition *reveal = [SKTransition fadeWithDuration:2.0 ];
Menu *newScene = [[Menu alloc] initWithSize: CGSizeMake(self.size.width,self.size.height)];
// Optionally, insert code to configure the new scene.
[self.scene.view presentScene: newScene transition: reveal];
}
if ([node.name isEqualToString:#"pause"]) {
[self pausedMenu];
}
if ([node.name isEqualToString:#"start"]) {
[self startMenu];
}
showpipes = showpipes + 1;
if (showpipes == 1) {
self.physicsWorld.gravity = CGVectorMake( 0.0, -5.0 );
SKAction* spawn = [SKAction performSelector:#selector(spawnPipes) onTarget:self];
SKAction* delay = [SKAction waitForDuration:2.0];
SKAction* spawnThenDelay = [SKAction sequence:#[spawn, delay]];
SKAction* spawnThenDelayForever = [SKAction repeatActionForever:spawnThenDelay];
[self runAction:spawnThenDelayForever];
}
started = 1;
if (noImpulse == 1) {
if (started == 1) {
mover.physicsBody.restitution = 0.0;
mover.physicsBody.velocity = CGVectorMake(0, 0);
[mover.physicsBody applyImpulse:CGVectorMake(0, 15)];
}
}
}
PAUSEDMENU METHOD:
-(void)pausedMenu
{
self.scene.paused = YES;
mover.scene.paused = YES;
[repeatButton removeFromParent];
[homeButton removeFromParent];
[menuBackground removeFromParent];
menuBackground = [SKSpriteNode spriteNodeWithColor:[SKColor colorWithWhite:0.0 alpha:0.3] size:CGSizeMake(self.frame.size.width*2, self.frame.size.height*2)];
[self addChild:menuBackground];
[pauseButton removeFromParent];
startButton = [SKSpriteNode spriteNodeWithImageNamed:#"staart"];
startButton.name = #"start";
startButton.position = CGPointMake(20, self.size.height-20);
startButton.size = CGSizeMake(40, 40);
[self addChild:startButton];
repeatButton = [SKSpriteNode spriteNodeWithImageNamed:#"repeat"];
repeatButton.name = #"repeat";
repeatButton.position = CGPointMake(self.size.width/2+30, self.size.height/2);
repeatButton.size = CGSizeMake(repeatButton.size.width/2, repeatButton.size.height/2);
[self addChild:repeatButton];
homeButton = [SKSpriteNode spriteNodeWithImageNamed:#"home"];
homeButton.name = #"home";
homeButton.position = CGPointMake(self.size.width/2-30, self.size.height/2);
homeButton.size = CGSizeMake(homeButton.size.width/2, homeButton.size.height/2);
[self addChild:homeButton];
}
Check out
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKView/Reference/Reference.html#//apple_ref/occ/instp/SKView/paused
If you pause your SKView that contains the game, all the state will be truly paused and you will be able to resume it after the pause menu has been dismissed. You'll have to present the pause menu in another SKView then the one that your game is contained in.

stop Impulse on sprite touch [duplicate]

This question already has an answer here:
stop impulse on SKSpriteKit click
(1 answer)
Closed 8 years ago.
i'm creating an game where there is a pause button. For checking wether its touched i'm checking the location of the touch and if its equal to the paused button name.
When the pausedButton is clicked its call a method which pause the scene.
The problem is that in the touchBegan method whenever you touch the screen it apply an impulse, so when i press the pauseButton and unpause it the applyforce will come after. This is not ideal for the game. I've tried with a bool like shouldImpulse, but havent got it to work. here is my touchedBegan method:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"home"]) {
SKTransition *reveal = [SKTransition fadeWithDuration:2.0 ];
Menu *newScene = [[Menu alloc] initWithSize: CGSizeMake(self.size.width,self.size.height)];
// Optionally, insert code to configure the new scene.
[self.scene.view presentScene: newScene transition: reveal];
}
if ([node.name isEqualToString:#"pause"]) {
[self pausedMenu];
}
if ([node.name isEqualToString:#"start"]) {
[self startMenu];
}
showpipes = showpipes + 1;
if (showpipes == 1) {
self.physicsWorld.gravity = CGVectorMake( 0.0, -5.0 );
SKAction* spawn = [SKAction performSelector:#selector(spawnPipes) onTarget:self];
SKAction* delay = [SKAction waitForDuration:2.0];
SKAction* spawnThenDelay = [SKAction sequence:#[spawn, delay]];
SKAction* spawnThenDelayForever = [SKAction repeatActionForever:spawnThenDelay];
[self runAction:spawnThenDelayForever];
}
started = 1;
if (started == 1) {
mover.physicsBody.restitution = 0.0;
mover.physicsBody.velocity = CGVectorMake(0, 0);
[mover.physicsBody applyImpulse:CGVectorMake(0, 15)];
}
}
You can do that with a simple IF, ELSE IF, ELSE:
if([node.name isEqualToString:#"home"])
{
// do stuff...
} else if ([node.name isEqualToString:#"pause"])
{
// do stuff...
} else if ([node.name isEqualToString:#"start"])
{
// do stuff...
} else
{
// do whatever else here...
}

How to calibrate a sprites touchLocation after zoom

I add a sprite on the background sprite and then zoom (pinch) the background and by that the sprite as well.
After the zoom i want to be able to click right on the "zoomed" sprite and then move (pan) it. However, to catch the sprite i have to click outside of the sprite. I guess that is due to the zoom and coordinates.
I would really need some help how to make it so that i always only need to click on the actual sprite if it is in it's original state or zoomed up or down.
I will also implement UIScrollView so i can move around the background but would like to fix this first.
Here is the code i use to test this:
- (void)didMoveToView:(SKView *)view {
_background = [SKSpriteNode spriteNodeWithImageNamed:#"background"];
_background.name = #"background";
[_background setAnchorPoint:CGPointZero];
// _background.anchorPoint = CGPointMake(0.0, 0.1);
self.scaleMode = SKSceneScaleModeResizeFill;
[self addChild:_background];
SKLabelNode *texT = [SKLabelNode labelNodeWithFontNamed:#"Ariel"];
texT.text = #"X";
texT.position = CGPointMake(160, 260);
[_background addChild:texT];
_panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(handlePan:)];
[self.view addGestureRecognizer:_panGesture];
_pinchGesture = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:#selector(handlePinch:)];
[self.view addGestureRecognizer:_pinchGesture];
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
sprite.xScale = 0.2;
sprite.yScale = 0.2;
sprite.position = CGPointMake(160, 260);
sprite.name = #"sprite";
[_background addChild:sprite];
}
- (void)handlePan:(UIPanGestureRecognizer *)sender {
NSLog(#"handlePan:");
if (![_currentNode.name isEqualToString:#"background"]) {
_touchLocation = [sender locationInView:sender.view];
_touchLocation = [self convertPointFromView:_touchLocation];
_currentNode.position = _touchLocation;
}
}
- (void)handlePinch:(UIPinchGestureRecognizer *) sender {
NSLog(#"handlePinch: %f",sender.scale);
[self runAction:[SKAction scaleBy:sender.scale duration:0]];
sender.scale = 1;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"touchesBegan");
//////DO THE FIRST TOUCH//////
_actualTouch = [touches anyObject];
_touchLocation = [_actualTouch locationInNode:self];
_currentNode = [self nodeAtPoint:_touchLocation];
NSLog(#"TouchBegan:_touchLocation:%#", NSStringFromCGPoint(_touchLocation));
NSLog(#"_currentNode.name: %#", _currentNode.name);
}
#end
I had this same problem and I found that the solution is to resize instead of scale. The physics seem to still work as expected and now the touch events will too. I came across this solution here. The scaleMode in your didMoveToView needs to be changed as well, see below.
self.scaleMode = SKSceneScaleModeAspectFill;
- (void)handlePinch:(UIPinchGestureRecognizer *) sender {
NSLog(#"handlePinch: %f",sender.scale);
//[self runAction:[SKAction scaleBy:sender.scale duration:0]];
self.size = CGSizeMake(self.size.width/sender.scale,self.size.height/sender.scale);
sender.scale = 1;
}
In my own implementation I had set upper and lower limits to the zoom level as well.

How to stop position update, when pause button is pressed?

I have this code to make the particles follow my b2body object:
-(void)updateMagicFruitParticlePosition:(ccTime)dt{
if (!gameManager.isPaused) {
magicFruitParticle.position = especialObject.ccPosition;
}
else
[self unschedule:_cmd];
}
The code works, but when the pause button is pressed and the player restarts the game, it crashes. Theoretically it should stop once the button was pressed, but it happens to be in the middle of the update even when the method has unscheduled.
The code below is what happens when the pause button is pressed:
-(void)pauseGame:(id)sender{
if (!gameManager.isPaused ) {
gameManager.isPaused = YES;
[gameManager pauseAllSoundEffects];
[gameManager pauseBackgroundMusic];
[[CCDirector sharedDirector] pause];
pauseLayer = [CCLayerColor layerWithColor:ccc4(0, 0, 0, 125) width:screenSize.width height:screenSize.height];
pauseLayer.position = CGPointZero;
[self addChild:pauseLayer z:kSecondLayer];
backgroundPauseA = [CCSprite spriteWithSpriteFrameName:#"BackgroundPauseA"];
backgroundPauseA.position = ccp(screenSize.width /2, screenSize.height /2);
[self addChild:backgroundPauseA z:kSuperiorLayer];
backgroundPauseB = [CCSprite spriteWithSpriteFrameName:#"BackgroundPauseB"];
backgroundPauseB.position = ccp(screenSize.width /2, screenSize.height /2);
[self addChild:backgroundPauseB z:kSuperiorLayer];
[self pauseMenu];
}
}
Is there any other way to make a particle effect follow an object or sprite? Or how can I fix it to work?
Any help will be appreciated!
Thanks!

Resources