Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpriteKit support multiple device orientations

I have a SpriteKit game which I want to support all orientations. Right now when I change the orientation, the node doesn't keep its position. I use the SKSceneScaleModeResizeFill for scaling, because it will keep the right sprite size.

When I start the game, the game player is positioned in the mid screen like this:

enter image description here

Then when I rotate the device, the position becomes like this:

enter image description here

Here is my view controller code:

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    // Configure the view.
    SKView * skView = (SKView *)self.view;
    if (!skView.scene) {

        // Create and configure the scene.
        SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
        scene.scaleMode = SKSceneScaleModeResizeFill;

        // Present the scene.
        [skView presentScene:scene];
    }
}

And my scene code:

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {

        self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];

        //Add spaceship in the center of the view
        SKSpriteNode *spaceship = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship.png"];
        spaceship.position = CGPointMake(size.width/2, size.height/2);
        [spaceship setScale:.3];
        [self addChild:spaceship];

    }
    return self;
}
like image 844
chrs Avatar asked Dec 16 '22 01:12

chrs


2 Answers

Your sprite does keep its position after the scene resizes — you can see from your screenshots that it keeps the same horizontal and vertical distance from the lower left corner of the scene. The catch is that after the scene has resized, that absolute offset represents a different relative position in your scene.

Sprite Kit can resize a scene automatically, but the relative positioning of nodes after a scene resize isn't something it can do for you. There's no "right answer" to how a scene's content should be rearranged at a different size, because the arrangement of scene content is something your app defines.

Implement didChangeSize: in your SKScene subclass, and put whatever logic you want there for moving your nodes.

For example, you could make it so nodes keep their positions as a relative proportion of the scene size using something like this:

- (void)didChangeSize:(CGSize)oldSize {
    for (SKNode *node in self.children) {
        CGPoint newPosition;
        newPosition.x = node.position.x / oldSize.width * self.frame.size.width;
        newPosition.y = node.position.y / oldSize.height * self.frame.size.height;
        node.position = newPosition;
    }
}

Depending on what's in your scene and you you've arranged it, you probably don't want that, though. For example, if you have HUD elements in each corner of your game scene, you might want them at a fixed offset from the corners, not a proportion of the scene size.

like image 66
rickster Avatar answered Dec 17 '22 15:12

rickster


I add I similar issue and found this question. I solved differently, using the viewWillTransitionToSize:withTransitionCoordinator:, as stated by @GOR here.

I added the following code in my view controller (that manage the SKView and its SKScene)

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    //skView is my SKView object, with scaleMode SKSceneScaleModeResizeFill
    skView.frame = CGRectMake(0, 0, size.width, size.height);

    //currentScene is my SKScene object
    currentScene.size = skView.frame.size;

    //Then, as all the objects in my scene are children of a unique SKNode* background, I only relocate it 
    currentScene.background.position = CGPointMake(CGRectGetMidX(currentScene.frame), CGRectGetMidY(currentScene.frame));
}

and it works like a charm!

like image 28
ddb Avatar answered Dec 17 '22 13:12

ddb