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.
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.
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
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
Uiwebview Not Go to Didfailloadwitherror When Weblink Not Found
How to Create a Uiimage with Uibezierpath
iOS App Getting Throttled from Local Searches
Xcode Creates Wrong IPA Folder Structure
Swift - Add Gesture Recognizer to Object in Table Cell
Game Center Login Dialog Not Shown Again After Cancelling It for the First Time (Ios7)
How to Properly Send an Image to Cloudkit as Ckasset
Error Using Swift - Instance Member Cannot Be Used on Type 'Viewcontroller'
Swiftui Foreach Index Out of Range Error When Removing Row
App Running on iPad While Its Iphone-Only
iOS Notification When Application Is Closed
Xcode 7 Beta 6, Dyld _Nsarray0_ Crash
Autolayout: Origin and Size Should Change According to Width and Height Factor
Understanding Model-View-Controller
iOS Lazy-Loading of Table Images
Which Cgimagealphainfo Should We Use
Is This a Bug with Mkmapkitdelegate Mapview:Didupdateuserlocation