How to Make Skscene Have Fixed Width

How to make SKScene have fixed width?

Giving the scene a fixed size is actually what we want to do in SpriteKit games. SpriteKit will than scale the game for each device using the scaleMode settings (defaults to .aspectFill). Its not a good idea to make the scene the size of the device or use .resizeFill for scale mode as that will lead to massive inconsistencies on different devices. I have been there before with 1 game and it was an absolute nightmare.

So we basically have 2 ways to do it correctly

1) Set scene size to iPad (e.g 1024x768 -landscape, 768x1024 - portrait. This was the default setting in Xcode 7.

You than usually just show some extra background at the top/bottom (landscape) or left/right (portrait) on iPads.

Examples of games that show more on iPads:

Altos Adventure, Leos Fortune, Limbo, The Line Zen, Modern Combat 5.

2) Apple changed the default scene size in xCode 8 to iPhone 6/7 (750*1334-Portait, 1337*750-Landscape). This setting will crop your game on iPads.

Examples of games that show less on iPads:

Lumino City, Robot Unicorn Attack

Choosing between those 2 options is up to you and depends what game you are making. I usually prefer to use option 1 and show more background on iPads.

Regardless of scene size scale mode is usually best left at the default setting of .aspectFill. This way you will have a consistent experience on all devices.

I would not try to do some random hacks where you manually change scene or node sizes/scales on difference devices, you should let xCode/SpriteKit do it for you.

In code you would initialise a SKScene with your preferred size like so

let gameScene = GameScene(size: CGSize(width: 1024, height: 768))

If you use the visual scene editor you set the scene size directly in the inspector panel on the right.

Hope this helps

Is it possible to have a fixed menu with a SKScene?

Here are a couple of approaches.

  1. If your menu is something made from UIKit and you want it in the controller, you can still do that and just have a member variable that turns it on or off.

  2. If your menu is made of sprite-kit views, just don't put it in the scrolling view and it won't scroll. Add the menu and the scrolling view to the root view.

How to make the scene stretch out fully to the width. Screenshot attached of my Xcode simulator of it not filling the screen

Once again, this happens because Apple has mistakenly removed the launch screen from the iOS-only SpriteKit game template (it's still there in the multi-platform template). You need to either add and assign a launch screen or choose Main as your launch screen.

The launch screen defines the working area for the app, so without it the view bounds and scene scale mode will not fix this issue.

Man, this is really going to continue being an issue that people are stumped by. Quite an oversight on Apple's part!

More info in Background is not filling the whole view SpriteKit.

SpriteKit SKScene size.width says 1024 docs say it should be 768

Yes, the width can be based on the device's orientation, but if you're only supporting portrait mode I don't think this is your problem. If you're creating your scene through Xcode's scene designer, you're seeing the height and width that were defined within that scene. To change this, open "YourScene.sks" and navigate through the following to change the scene's dimensions to something more appropriate:

Utilities -> SKNode Inspector -> Change Width and Height

Sample Image

Sprite Kit Scene Editor GameScene.sks scene width and height

Pixel density

First of all the size of the Scene is defined in Points not Pixels.

Let's see how the devices that support iOS 9 deal with this:

  • 1 point = 1x1 pixel: iPad 2, iPad mini
  • 1 point = 2x2 pixels: iPhone 4s, iPhone 5, iPhone 5c, iPhone 5s, iPhone 6, iPhone 6s, iPad mini 2, iPad mini 3, iPad mini 4, iPad 3,
    iPad 4, iPad Air, iPad Air 2, iPad Pro, iPod touch 5, iPod touch 6
  • 1 point = 3x3 pixels: iPhone 6 Plus, iPhone 6s Plus

This allow you to specify a size that is automatically converted for the pixel density of a particular device.

Screen size

There are several screen sizes available on the iPhone/iPad supported by iOS 9.

With SpriteKit you can easily face this problem setting the scaleMode property of your Scene.

You can chose among 4 options:

  • Fill: Scale the SKScene to fill the entire SKView.
  • AspectFill: Scale the SKScene to fill the SKView while preserving the scene's aspect ratio. Some cropping may occur if the view has a
    different aspect ratio.
  • AspectFit: Scale the SKScene to fit within the SKView while preserving the scene's aspect ratio. Some letterboxing may occur if
    the view has a different aspect ratio.
  • ResizeFill: Modify the SKScene's actual size to exactly match the SKView.

You probably want to set AspectFill but it really depends on your game.

To set the property open GameViewController.swift (or .m if you use Objective-C), in viewDidLoad you'll find this line. Just change it to mach your preference.

/* Set the scale mode to scale to fit the window */
gameScene.scaleMode = .AspectFill

SpriteKit SKScene not resizing correctly to fit iPhone 12

Fix (credit @aheze)

As mentioned here, this can be fixed by setting the launch screen file in your project settings under App Icons and Launch Images.

SKScene iPad height width reversed

Try, not using viewDidLoad and use this

- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];

SKView * skView = (SKView *)self.view;
if (!skView.scene) {
skView.showsFPS = YES;
skView.showsNodeCount = YES;

SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;

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

How do you get the frame.width of GameScene in a custom class?

Every node in SpriteKit can retrieve the scene where it lives using the scene property.

let node = SKNode()
node.scene

The scene property returns a SKScene?, the value is an optional because if the current node does not belong to a scene, of course there is not scene to retrieve.

Now, you could be tempted to use the self.scene property inside the initializer of your HudController.

class HudController: SKNode {
var width:CGFloat

override init() {
super.init() // error: property 'self.width' not initialized at super.init call
width = self.scene!.frame.width
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

But it will now work because you cannot invoke super.init before having initialized the width property.

Fine, then you could try this.

class HudController: SKNode {
var width:CGFloat

override init() {
width = self.scene!.frame.width // error: use of 'self' in property access 'scene' before super.init initializes self

super.init()
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Again compiler error because you are using self before having called super.init().

And finally there is another problem, when the initializer is executed, the node it is being created has not a parent yet, so the self.scene property does return nil.

How to solve this?

Solution

First of all declare you initializer to receive a GameScene.

class HudController: SKNode {
var width: CGFloat

init(gameScene:SKScene) {
width = gameScene.frame.width
super.init()
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Now, when you create this object (I hope you are doing it from the scene or from a node already added to the scene otherwise we are in trouble) you simply write

class Foo : SKNode {

func foo() {
guard let scene = self.scene else { return }
let controller = HudController(gameScene: scene)
}
}

On the other hand if you are creating HudController from you scene you simply write:

class MyScene : SKScene {

override func didMoveToView(view: SKView) {
super.didMoveToView(view)
let controller = HudController(gameScene: self)
}
}

That's it.

Update

If you want an instance property of HudController inside your GameScene this is the code.

class HudController: SKNode {
var width: CGFloat

init(gameScene:SKScene) {
width = gameScene.frame.width
super.init() // <-- do not forget this
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

class GameScene: SKScene {
var hud : HudController?

override func didMoveToView(view: SKView) {
super.didMoveToView(view)
hud = HudController(gameScene: self)
}
}


Related Topics



Leave a reply



Submit