I am trying to adjust SKScene position based on the device the game is running. For example due to 'top notch' area of iPhone XS Max, I need to subtract that height from SKScene height and position the scene accordingly.
So here is how I calculate the height aka safeAreaHeight in the AppDelegate and subtract from scene height aka sceneHeight:
safeAreaHeight = UIApplication.shared.statusBarFrame.size.height
sceneHeight -= safeAreaHeight
In GameViewController I try to re-position the SKScene like below:
if let view = self.view as! SKView? {
// Load the SKScene from 'MainMenuScene.sks'
if let scene = SKScene(fileNamed: "MainMenuScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFit
scene.size.height = sceneHeight
scene.size.width = sceneWidth
scene.position.y -= safeAreaHeight
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
However, I am getting below error:
SKScene: Setting the position of a SKScene has no effect.
Additionally, I tried to use SKAction to move position of the SKScene as well, then I received below error:
SKScene: Animating the position of a SKScene has no effect.
Thank you for the help.
Well I figured how to achieve this, I am sharing in case it would help someone else.
Inside AppDelegate you get the correct safe area values and store it in a singleton (in my case: safeAreaHeight) for access throughout the app. Subtract the amount from scene height (sceneHeight).
let kWindow = UIApplication.shared.windows[0]
safeAreaHeight = kWindow.safeAreaInsets.top + kWindow.safeAreaInsets.bottom
screenHeight = screenRect.size.height - safeAreaHeight
Don't forget the adjust the sceneHeight in GameViewController before presenting it:
if let view = self.view as! SKView? {
// Load the SKScene from 'MainMenuScene.sks'
if let scene = SKScene(fileNamed: "MainMenuScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFit
scene.size.height = sceneHeight
scene.size.width = sceneWidth
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
Inside loaded scene, in this case it is MainMenuScene, you simply find subview of SKScene and adjust its Y position by subtracting the safeAreaHeight at its center
guard let subView = self.view else { return }
subView.center.y -= safeAreaHeight
PS.: Do not adjust all the views in different scenes (ie.: GameScene, GameOverScene etc.) Once you adjust, it remains throughout the app.
Related
My SKScene will show sized correctly in the Simulator, before rotation, but not after?
If the Simulator is preset to Portrait, my SKScene is sized correctly .. and ditto if preset to Landscape.
But, not after rotation-in-place.
Here are some code snippets …
class GameViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// thisScene = a String name, e.g. “CreditsScene” or “AboutScene”
showScene(theScene: thisScene!)
} // viewDidAppear
func showScene(theScene: String) {
view = SKView()
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: theScene) {
scene.scaleMode = .aspectFill
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
} // showScene
override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
if fromInterfaceOrientation.isLandscape {
let width = UIScreen.main.bounds.width
let height = UIScreen.main.bounds.height
if let scene = SKScene(fileNamed: thisScene!) {
print("\(scene)")
scene.size = CGSize(width: width, height: height)
print("\(scene)")
}
}
else {
let width = UIScreen.main.bounds.width
let height = UIScreen.main.bounds.height
if let scene = SKScene(fileNamed: thisScene!) {
print("\(scene)")
scene.size = CGSize(width: width, height: height)
print("\(scene)")
}
}
showScene(theScene: thisScene!)
} // didRotate
}
Here’s the Console output after rotation from Portrait to Landscape:
name:'(null)' frame:{{-375, -667}, {750, 1334}} anchor:{0.5, 0.5} // before Landscape
name:'(null)' frame:{{-590, -410}, {1180, 820}} anchor:{0.5, 0.5} // after Landscape
Then, after rotation back to Portrait:
name:'(null)' frame:{{-375, -667}, {750, 1334}} anchor:{0.5, 0.5} // before Portrait
name:'(null)' frame:{{-410, -590}, {820, 1180}} anchor:{0.5, 0.5} // after Portrait
The 2nd lines, “after ..” reverse as they should.
For the life of me, I do not understand the 1st lines, “before”?
and for both lines what is name:'(null)' .. doesn't it have the name fileNamed:thisScene!
It’s almost as if
if let scene = SKScene(fileNamed: theScene) { .. }
doesn’t call view.presentScene( .. )
To complete the diagnostics, I have one GameViewController and 3 GameScenes (.sks files). I am successful in switching scenes .. but not in rotating them to size.
Any help is very, very appreciated!
SOLVED
See the above code for showScene, in particular the 1st line in the func showScene:
view = SKView()
Eliminate it.
Found this jewel here at SO:
The size specified in the .sks file is the size of the scene not the view. Set your scene's scale mode to .resizeFill to force the scene size to always match your view size.
scene.scaleMode = .resizeFill
BOOM!
DONE!
I'm trying to be a competent iOS Programmer ... I have no aspirations to be a Movie Producer, but:
lovesongforever.com/monsterpaddle
I'm a newbie in Swift and SpriteKit. I'm trying to develop a simple 2D game for iPhone X, so I want to draw a playable area where I can put game's sprites. The problem is that when I draw the rectangle it fits the screen width, but not the screen height.
The GameScene size is 2048x1536, with anchor point in (0.5, 0.5).
The default screen orientation is landscape.
This is what I did at the moment:
GameViewController.swift
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
}
GameScene.swift
class GameScene: SKScene {
override func didMove(to view: SKView) {
let rectangle = SKShapeNode(rect: self.frame)
rectangle.strokeColor = .green
rectangle.lineWidth = 20
rectangle.zPosition = 1
addChild(rectangle)
}
}
When I run this code, the rectangle green borders fit the width of the screen (in landscape), so I can see them, but the top and bottom borders are off-screen.
My purpose is to have a playable rect as high as the phone screen, and his width should be screen width - left safe area insets (to exclude the notch from the playable area).
Thank you.
When using aspect fit in swift, the sides of the screen are removed. Is there a way I can cover this up by using a background colour to fill the whole screen or use a background image?
Here is the code for creating the scene as aspect fit in the GameViewController
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "mainMenu") {
// setting scene here to aspect fit
scene.scaleMode = .aspectFit
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
scene with aspect fit and background set to clear
scene with aspect fill
scene with aspect fit and background set to green
Make sure you set the screen size for the scene.
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height
scene?.size = CGSize(width: screenWidth, height: screenHeight)
After, set scene.scaleMode = .aspectFill
If this doesn’t work, you can try and set the background color by doing this, but I’m unaware if this option will give you what you’re looking for as I didn’t test it.
scene?.backgroundColor = UIColor.white //color you want
Otherwise try setting scene?.size = image.size
I'm currently making a game in Swift/SpriteKit and I'm having a problem. Whenever I make the border for my game, the border is always around the iPhone/iPad FRAME and not the screen. So when my ball bounces it goes off of the screen and bounces off the "iPhone FRAME" and not the "scene/frame."
Can someone help me make it so my border is around the SCENE and not the FRAME?
Here is my code:
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
I was meant to say (in comments) that you should set the scene's size to match the view's size, like this: In the GameViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
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
scene.size = skView.bounds.size
skView.presentScene(scene)
}
}
Note the scene.size = skView.bounds.size which sets the scene's size to a size of the view.
I've been getting stuck doing what should be really simple stuff in Xcode, I am trying to add a sprite to the scene and for it to sit in the bottom left corner:
var groundTexture : SKTexture = SKTexture(imageNamed: "dirt-block")
var ground : SKSpriteNode = SKSpriteNode(texture: groundTexture)
ground.position = CGPointMake(ground.size.width/2, ground.size.height/2)
self.addChild(ground)
This doesn't show anything, so I took a look at what the scene dimensions are and self.frame.size.width and self.frame.size.height are returning a width of 1024 and a height of 768, regardless of orientation.
I want this fixed in landscape mode if that makes any difference to the answer?
I'm obviously missing a very fundamental idea to do with setting up the scene, if anybody could shed any light it would be much appreciated.
Thanks.
You have to edit the GameViewController:
Cut everything from viewDidLoad() except super.viewDidLoad()
Overwrite viewWillLayoutSubviews
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
// Configure the view.
var 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.size = skView.bounds.size
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
Also you should set the scene size as you can see
scene.size = skView.bounds.size
Hope this helps
There is also a great Tutorial where this is explained better:
http://www.raywenderlich.com/49721/how-to-create-a-breakout-game-using-spritekit
(Chapter 1 or 2)