Question

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;
    }
Was it helpful?

Solution

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top