Universal app background SpriteKit - ios

I'm working on universal game (iPhone and iPad) with SpriteKit and I have issue with background size.
I created image size 2048x1536 and more image for iPhone devices in size 1136x852 and still the background is cropped on iPhone.
How can I fix it?
let screenHeight = screenSize.size.height
//Backgorund
var dayBackground = SKSpriteNode(imageNamed:"BackgroundDay")
dayBackground.zPosition = -1
dayBackground.position = CGPoint(x:size.width/2, y:size.height/2)
dayBackground.anchorPoint = CGPointMake(0.5, 0.5)
dayBackground.size = self.frame.size
GameViewController:
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
iPhone 6 Plus:
iPad Pro:
iPhone 4s:
Assets file:
#1x size is 2048x1536 and #3x is 1136x852
The sense scale is .AspectFill with .ResizeAll the position of SKSprtieNode it not like what I want.
Thanks!
I tried to split the image to parts but still is cropped
I think is happen because the iPhone 6 plus screen size is 736x414 and the scene size is 1024x768, and if I change the scene to the current device I need to position the sprites for each screen

The devices's screen size ratios are different, and one will have to stretch dimensions to fit the screen, and this may look weird. But you can use .Fill if you are sure you like this option. The smoothest alternative is to remain using .AspectFill and create an image for each screen dimension. Be creative! Good luck!

Related

Swapped width and height values

Explanation
For some reason, width and height values are being swapped in my game (as you can see below) that is set to be in landscape orientation. This way, the sprite that should be centered in the screen, is totally off the right position.
Code
You can download it here.
GameScene
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
print("width:", self.frame.width)
print("height:", self.frame.height)
//Sprite
let sprite = SKSpriteNode (imageNamed: "sprite")
sprite.anchorPoint = CGPointZero
sprite.position = CGPoint(x: self.frame.width / 2 - sprite.frame.width / 2, y: self.frame.height / 2 - sprite.frame.height / 2)
addChild(sprite)
}
}
GameViewController
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Set view size.
let scene = GameScene(size: view.bounds.size)
// Configure the view.
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .ResizeFill
skView.presentScene(scene)
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .Landscape
} else {
return .Landscape
}
}
}
Thanks in advance,
Luiz.
If you go to targets-General in your xCode project (where you set version number, bundle ID etc) did you untick portrait? You also might have to check your info.plist to see if all portrait entries are removed for both devices.
You approach unfortunately is bad practice a lot of tutorials teach you and I would not continue like this.
If you use "scaleMode ResizeFill" or set the scene size to "view.bounds" your game will never look consistent on all devices. Furthermore all your values (sprite sizes, font sizes, physics values etc) will also not be the same on devices different to the one you are testing on.
Basically you will have to adjust for all of this on like 5-6 devices and its madness, especially using xCode simulator. Trust me I have been there before with 2 games and it nearly killed me. Its a game of game of "yeah this looks about right".
Dont go through this, so what you should do is
1) Change your scene size to the default scene size used by xCode
GameScene(size: CGSize(width: 1024, height: 768)) // swap if portrait
Note: Check update at bottom
If you dont do this, and leave it at view.bounds.size, point 2 will not work.
2) Change your scene scale mode to .AspectFill (also default xCode settings).
This way all your stuff will look great on all iPhones. Your values will scale correctly and you save yourself a lot of work.
On iPads you will have some extra space at the top and bottom (landscape) or left and right (portrait) which you usually just cover with some more background and have as a non playable area
Your scene basically looks like this now.
The red area is your whole scene size (iPad) but if you run on iPhones you will only see the green bit. In Portrait mode that red area would be on the left and right side.
Thats why y positioning is usually done from the centre because if you use frame.minY or frame.maxY you would be in the red zone and won't see the sprite on iPhones. That red zone you just cover with some more background (your background full screen images should be iPad size).
This also make your game more balanced, because if your would just stretch up your game than it will be easier on iPads because you have more space.
So design your game within the green zone. Than on iPads the only thing you might have to do is move up some UI, like pause button or score label, when you want to show them at the top edge. I do it this way.
// Some button
...
someButton.position = CGPoint(x: frame.midX, y: frame.midY + 200)
addChild(someButton)
if UIDevice.current.userInterfaceIdiom == .pad {
// set for iPad
}
// Some label
someLabel.positon = CGPoint(x: frame.midX - 100, someButton.position.y) // because its y position is the same as someButton I only have to adjust someButton.position.y on iPads.
addChild(someLabel)
I would not continue with your approach, even if it means redoing a lot of work.
UPDATE:
It seems with xCode 8 apple changed the default scene size from 1024x768 (768x1024) to 1334x750 (750x1334). I have only played around with those settings for a bit and they seem confusing for a universal game.
So you scene would now look like this and stuff on the xAxis on iPads is offscreen.
That makes no sense as the iPad is clearly not more widescreen than an iPhone.
I actually opened a bug report to see what apple says about this so for now I would probably keep it at 1024x768.
Hope this helps
Override viewWillLayoutSubviews in your GameViewController to ensure you get the proper width/height of the view. The implication with this also is that any objects you may instantiate would need to wait until this method has been called.
So you would want to do something like
class GameViewController: UIViewController {
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
// NOTE: Add code to do this only once!
// Set view size.
let scene = GameScene(size: view.bounds.size)
// Configure the view.
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .ResizeFill
skView.presentScene(scene)
}
Note I didn't try and compile this and it is missing a trailing }. Additionally, you would want to do this only once, I haven't added the code to do that. In my game, my scene is a property so I just check to see if it is nil and if it is, then I create the scene and present.
Following your code and according to how do you want to construct your sprite and position in GameScene you simply must change the scene.scaleMode to AspectFill or AspectFit (depend by what do you want to achieve):
sources:
#available(iOS 7.0, *)
public enum SKSceneScaleMode : Int {
case Fill /* Scale the SKScene to fill the entire SKView. */
case 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. */
case 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. */
case ResizeFill /* Modify the SKScene's actual size to exactly match the SKView. */
}

Adding a background image to a SpriteKit project

The iPhone 6 and 6s are supposed to have a 750 x 1334 resolution [1], and the screen ratio for every iPhone since the iPhone 5 is 16:9 [2]. So in order to have a background image for an app that fits perfectly, it must have a 16:9 ratio. I'm working on a project using SpriteKit and I want the game to have a wallpaper that covers the back from edge to edge. However, when I run the app on the simulator the background image always gets cropped on the right and left. I've even tried with all sorts of ratios and resolutions. The code for this background on the project is:
let background = SKSpriteNode(imageNamed: "backtImage")
background.size = self.size
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
self.addChild(background)
What am I doing wrong?
This could happened because your SKView don't match with your SKScene dimensions.
The sources:
#available(iOS 7.0, *)
public enum SKSceneScaleMode : Int {
case Fill /* Scale the SKScene to fill the entire SKView. */
case 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. */
case 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. */
case ResizeFill /* Modify the SKScene's actual size to exactly match the SKView. */
}
You can change your code as below:
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
...
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .ResizeFill // <- this is an example
skView.presentScene(scene)
}
}
If your image is getting cropped on the left and right side (in Portrait) Then we are dealing with an .AspectFill scale mode, and your aspect for your scene is shorter than the screen resolution. This is probably happening because the default scene size provided in the template build is a square. Go into your .sks file, and change the scene size to something in a 9x16 ratio, and your problem should be solved

SpriteKit screen size--how to get the correct size of images on a real phone?

Why does my app look different from the simulator when I run it on my real phone?
I am making a game using spritekit. When I test it in the iPhone 5 simulator, the size of the screen and the size of the images are exactly as I want them, but then when I run the program on my actual iPhone 5, the images show up too big, parts of the top and bottom of the screen are cut off, and the left and right of the screen don't extend all the way to the sides of the phone--there's a letterboxing effect from left to right on the screen.
All my images are named with #2x, so they should work fine with retina screens. Here's the code relating to screen size that I use when I initialize my SKScene:
GameScene *scene = [GameScene unarchiveFromFile:#"GameScene"];
scene.size = self.view.frame.size;
scene.scaleMode = SKSceneScaleModeAspectFill;
I've tried each of the available SKSceneScaleModes and none of them help--they each just give a different outcome that doesn't look right. I'm using AspectFill simply because that mode does work correctly in the simulator.
I've also tried sizing the screen like this, just as something to try, but that didn't help either:
GameScene *scene = [GameScene unarchiveFromFile:#"GameScene"];
CGRect screenRect = [[UIScreen mainScreen] bounds];
scene.size = screenRect.size;
Thanks in advance.
It was indeed due to the fact that the simulator was simulating ios 8 while my device was on ios 7. ios 8 automatically changes the coordinate system when you go into landscape mode (the native mode of my game) while ios 7 does not. The fix was simple once I figured that out--just detect which os the device is running, and, if it's lower than ios 8, swap the height and width dimensions of the screen:
if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) {
CGSize fixedSize = CGSizeMake(screenRect.size.height, screenRect.size.width);
scene.size = fixedSize;
}
else
scene.size = self.view.frame.size;

SpriteKit correct asset size

Summary
I'm creating an SpriteKit game for different devices and I'm wondering what should be the size of the different assets.
What I have right now
I'm creating the SKScene in viewDidLoad:
scene = GameScene(size: view.bounds.size)
scene.scaleMode = .AspectFill
I don't want to use viewWillLayoutSubviews because it is called twice on start up and also each time the device is rotated. I don't think that initialising the scene here is a good idea.
With this configuration, my view is initialised with a width of 600 points (default storyboard size) and then rescaled to the device width. Using println(scene.size.width) after the view has been layout it displays a 375 points size in the iPhone 6 and 414 points in the iPhone 6 Plus.
I'm using xcassets to provide the background images and I would like to know which sizes should I use. My current configuration is ready for 320 points resolutions (iPhone 4, iPhone 5)
1x: 320 pixels
2x: 640 pixels
3x: 960 pixels
The problem
The problem is that the image will be rescaled in following devices:
iPhone 6: 750 pixels - 375 points
iPhone 6 Plus: 1242 pixels - 414 points
iPad: 768 pixels - 768 points
iPad retina: 1536 pixels - 768 points
I would like to know how am I supposed to configure the images and the SKScene size to optimise it for all the devices.
If you don't want to initialize scene twice you can initialize it only once:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
skView.showsDrawCount = YES;
skView.showsPhysics = YES;
skView.ignoresSiblingOrder = YES;
if(!skView.scene){
// Create and configure the scene.
GameScene * scene = [GameScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
}
For a project I'm currently working on, I decided to make my assets in the largest possible resolution, i.e. the one of the iPhone 6 Plus (2208 x 1242, iOS Resolution Reference). Since iPhone 6 and iPhone 5 use a 16:9 aspect ratio, it is no problem to down-scale the assets implicitly with
scene.scaleMode = .AspectFit
On iPad and iPhone 4s, this will result in a border, but I can live with that.

SpriteKit coordinate system messed up

In portrait mode, i created a SKShapeNode at the position of 0,0. Yet it seems that it does not appear in the screen at all.
This is the code i am using now
let test = SKShapeNode(rect: CGRectMake(0, 0, 10, 10))
test.fillColor = SKColor.blackColor()
self.addChild(test)
The background colour white, so the shape node will definitely appear if it was on the screen
Yet 0 node appears in the screen?
children.count returns 1
And this is how i display the scene
override func viewWillLayoutSubviews () {
super.viewWillLayoutSubviews()
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
// Configure the view.
let skView = self.view as SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
The SKShapeNode is drawn, but it is outside the screen?
EDIT 1: I change the scale mode to .AspectFit, and this is what happened
Is the game forcefully running in landscape although the game is suppose to run in portrait?
Base on #fuzzygoat answer at https://stackoverflow.com/a/24170460/1879382.
Simply open GameScene.sks, then change the value of x and y to the appropriate value needed.
For me, I used 320 x 568 since i run the game on iPhone 5S

Resources