1

I have an issue with a demo game I'm working on. All I'm trying to do is to pause the game when it enters the background and restart it when it becomes active. I've tried the solution from this link https://stackoverflow.com/questions/19014012/sprite-kit-the-right-way-to-multitask

1: SpriteKit- the right way to multitask and this link SpriteKit - how to correctly pause and resume app

and a bunch of other ones, but I had no success with any of them. I've even tried the following in app delegate:

#import "AppDelegate.h"
#import <SpriteKit/SpriteKit.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application
{
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.

    // pause sprite kit
    SKView *view = (SKView *)self.window.rootViewController.view;
    view.paused = YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

    // resume sprite kit
    SKView *view = (SKView *)self.window.rootViewController.view;
    view.paused = NO;
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

@end

and nothing happens. I pause and unpause in my scene as follow:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInNode:_bgLayer];
    [self moveZombieToward:touchLocation];

    CGPoint location = [touch locationInNode:self];
    SKNode *node = [self nodeAtPoint:location];

    if (node == _pauseButton) {
        [_pauseButton setTexture:[SKTexture textureWithImageNamed:@"pausebutton"]];
        _pauseButtonPressed = YES;
        _playButtonPressed = NO;
        self.scene.paused = YES;
    }

    if (node == _playButton) {
        [_playButton setTexture:[SKTexture textureWithImageNamed:@"playButton"]];
        _playButtonPressed = YES;
        _pauseButtonPressed = NO;
        self.scene.paused = NO;
    }
}

edit:

- (void)moveZombieToward:(CGPoint)location
{
    [self startZombieAnimation];
    _lastTouchLocation = location;
    CGPoint offset = CGPointSubtract(location, _zombie.position);

    CGPoint direction = CGPointNormalize(offset);
    _velocity = CGPointMultiplyScalar(direction, ZOMBIE_MOVE_POINTS_PER_SEC);
}

- (void)update:(NSTimeInterval)currentTime
{
    if (_lastUpdateTime) {
        _dt = currentTime - _lastUpdateTime;
    } else {
        _dt = 0;
    }
    _lastUpdateTime = currentTime;
    if (_pauseButtonPressed) {
        _lastUpdateTime = 0;
        return;
    }
Cœur
  • 37,241
  • 25
  • 195
  • 267
Adrian P
  • 6,479
  • 4
  • 38
  • 55
  • pausing the view and pausing the scene are two different things, you should do one and the same for both situations (pause button vs app entering background) – CodeSmile Apr 08 '14 at 15:32
  • @LearnCocos2D, all I'm trying to do is handle application did enter background and foreground. the reason that I'm pausing the scene is to pause all of the actions inside the scene. my game structure has one of the actions out side the loop and it does not work with view.paused = YES; i had to use self.scene.paused = YES; any solution you can help out with. – Adrian P Apr 08 '14 at 15:35
  • basically all you need to try is change this "view.paused = YES;" to this "view.scene.paused = YES;" – CodeSmile Apr 08 '14 at 15:38
  • @LearnCocos2D, i did add the Self.view.paused No & Yes after the scene being paused. In MyScene it works and does pause the whole thing but in app delegate( when app comes back from background it does it stop the action of the zombie. The zombie moves. How would I stop and restart the character from moving when app goes to background and comes back? Can you please offer some help? – Adrian P Apr 08 '14 at 16:49

1 Answers1

2

Got your code. OMG I hate zombie conga :) but it was an great learning tutorial for me too. All your code looks fine. At least I couldn't see anything wrong with it. Game play is paused/un-paused correctly according to a couple NSLogs I dropped in. You are also pausing/un-pausing the right way according to RW's iOS Games by Tutorials page 481 (absolutely great book btw, I highly recommend it).

I believe the reason the pause function is not working properly is because the movements in the game are time based. For example:

_dt = currentTime - _lastUpdateTime;

shortly after the _dt line a call is made to move the sprite

[self moveSprite:_zombie velocity:_velocity];

which includes the line

CGPoint amountToMove = CGPointMultiplyScalar(velocity, _dt);

Un-pausing the game, will result character movements to be greatly exaggerated because a lot of time could have passed from when the game was initially paused.

The unexpected results you are having is caused by the game's logic rather than your programming skills. To remedy this specific app to function properly with pause, I would suggest changing the movement to incremental updates (e.g. x=x+1) in the update method instead of using time based calculated movement.

UPDATED: Included non time based movement code

//
// touch anywhere on the screen to move the object to that location
//
@implementation MyScene
{
    SKSpriteNode *object1;
    CGPoint destinationLocation;
}

-(id)initWithSize:(CGSize)size
{
    if (self = [super initWithSize:size])
    {
        [self createObject];
        destinationLocation = CGPointMake(300, 150);
    }
    return self;
}

-(void)createObject
{
    object1 = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(50, 50)];
    object1.position = CGPointMake(300, 150);
    object1.zRotation = 1;
    [self addChild:object1];
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    destinationLocation = [touch locationInNode:self.scene];
}

-(void)update:(CFTimeInterval)currentTime
{
    float x = fabs(object1.position.x - destinationLocation.x);
    float y = fabs(object1.position.y - destinationLocation.y);

    float divider = 0;
    if(x > y)
    {
        divider = x;
    } else
    {
        divider = y;
    }

     float xMove = (x/divider)*2; // you can change the 2 to any value you want.
     float yMove = (y/divider)*2; // just make sure both x & y are multiplied by the same value

    if(object1.position.x > destinationLocation.x)
        object1.position = CGPointMake(object1.position.x-xMove, object1.position.y);

    if(object1.position.x < destinationLocation.x)
        object1.position = CGPointMake(object1.position.x+xMove, object1.position.y);

    if(object1.position.y > destinationLocation.y)
        object1.position = CGPointMake(object1.position.x, object1.position.y-yMove);

    if(object1.position.y < destinationLocation.y)
        object1.position = CGPointMake(object1.position.x, object1.position.y+yMove);
}

@end
sangony
  • 11,636
  • 4
  • 39
  • 55
  • You don't need to add code in the AppDelegate. Your scene is just being set up to receive a notification (and run the method specified in the selector) whenever your app is about to go into background and/or foreground. I am not sure what you are referring to with the touchesBegin... are you referring to a pause button you have on your screen? – sangony Apr 08 '14 at 15:41
  • In your Scene, to pause you can use this self.paused = true; to un-pause you can use this self.paused = false; – sangony Apr 08 '14 at 16:01
  • What exactly is not working? You are not getting the notification? The game is not pausing properly? Something else? – sangony Apr 08 '14 at 16:06
  • I did exactly that, and it won't do anything when the app comes back from background. It still restarts the app from begining. I'll try to create a demo and push it to github tonight and will let you know. If you could look at it, it would be awesome. Thanks for your help. – Adrian P Apr 08 '14 at 18:01
  • Will do. Let me know when it's uploaded. – sangony Apr 08 '14 at 18:05
  • here is the link to the demo app. currently it has my other way of trying to get things done through app delegate and not yours. i appreciate your help. feel free to change it but if you can leave me some comments so i see how it works. after all I'm new to sprite kit and am learning. thanks again. https://github.com/AdrianPhillips/DemoApp – Adrian P Apr 09 '14 at 13:21
  • thanks for your time. can you offer an example based on the idea of this scenario for incremental update? – Adrian P Apr 09 '14 at 18:51
  • well the solution that you are offering, technically answers my question . so I'm awarding the right answer to you. but i would appreciate if you can offer some assistance with the movement increment, if not then thank you again.as i mentioned, this is my learning point of sprite kit and with what i have gathered so far, this demo is grown to almost a full game. which i intend to use as a starting point for getting into game development. so i guess I'm saying thank you again. – Adrian P Apr 09 '14 at 19:18
  • Thanks for the accept! To do incremental position updates you will need to store touch destination as a property CGPoint. Then in your updates check if player.position.x > destination.x then player.position.x-3. If player.position.x < destination.x then player.position.x+3. Change 3 for more or less speed per update. Same exact principal for the Y position. If you do it this way, you shouldn't have the time issue (_dt) to deal with in regards to movements. – sangony Apr 09 '14 at 22:19
  • I tried writing a new method for incrimenting the movement but can't make it to work. If it's not much of trouble, would you mind providing a sample code? I'll be greateful. – Adrian P Apr 10 '14 at 02:21
  • i added the methods provided and also tricked a bit with the movements and the velocity but it did not work when application comes back from background and also i can't set the last update time to zero in order to stop the movement. i guess either I'm tiered or dumb. if you get a chance would you mind plug it in my project on github. i know I'm asking for much, and thank you. – Adrian P Apr 11 '14 at 14:51
  • You can make his original code work by overriding paused in the GameScene. All he has to do is set _lastUpdateTime to zero every time the pause property changes. He currently has similar logic in his updateCurrentTime method. This is a problem because it's too late, the nodes are already moved in an exaggerated manner. – 3366784 Mar 28 '16 at 06:51