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.
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 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.
I am having deep trouble understanding how the resolution on different devices work. so what I have done is made a background image 1024 x 768 then tested on an iphone 5 device. when i run it, the bottom half of the screen is chopped off so as the top. I did use AspectFill as my scale mode, but even when i test it on iphone 6 or 6+, the background is still chopped off.
the following is my code for gameviewcontroller
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene(size: CGSize(width: 1024, height: 768))
let skView = self.view as! SKView
skView.multipleTouchEnabled = true
if GameSettings.Debugging.ALL_TellMeStatus {
skView.showsFPS = GameSettings.Debugging.ALL_ShowFrameRate
skView.showsNodeCount = GameSettings.Debugging.ALL_ShowNodeCount
skView.showsDrawCount = GameSettings.Debugging.IOS_ShowDrawCount
skView.showsQuadCount = GameSettings.Debugging.IOS_ShowQuadCount
skView.showsPhysics = GameSettings.Debugging.IOS_ShowPhysics
skView.showsFields = GameSettings.Debugging.IOS_ShowFields
}
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
_ = SGResolution(screenSize: view.bounds.size, canvasSize: scene.size)
skView.presentScene(scene)
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .Landscape
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
and my code for adding the background image in.
let background = SKSpriteNode(imageNamed: "Artboard 1")
background.posByCanvas(0.5, y: 0.5)
background.xScale = 1.2
background.yScale = 1.2
background.size = self.frame.size
background.zPosition = -1
addChild(background)
is this the right way to start the screen size or should i start with an iPad screen size then descale it? If anyone can lead me on how to attack this problem that would be great!!
Aspect fill is going to chop off parts of your background.
Your scene has an aspect ratio of 4:3
Your device has an aspect ratio of 16:9
AspectFill will scale your scene in its current aspect ratio(4:3) till the farthest borders are filled, allowing for the entire view to show the scene and have no black borders;
AspectFit will scale till the nearest borders are filled in, which will give you black bars to preserve the aspect ratio.
If you want the 4:3 to not be preserved, then you use .Fill, this of course is going to make your sprites fatter on 16:9 because it will just stretch the scene till it hits all 4 borders
Finally you have .ResizeFill, and what this does is resizes the scene bounds to match your screen size, so if you start with a 1024x768 scene, it will turn it into a 736x414 scene when you view it on iPhone 6+ (This is because we are working in points, not pixels, the pixel count would be x3 (so 2208x1242) which hardware will shrink to 1920x1080).
You need to take a step back and evaluate how you want your game to look on 3 different aspect ratios
16:9,3:2, and 4:3, and determine which of the 4 methods above works best for you.
Now in my opinion, I find that Aspect Fill works best, and I just plan for the 16:9 aspect ratio, making sure I do not put any important information in the areas that may be cropped for a game, but this does not work for everybody, and unfortunately nobody can help you with this part, since it is your game and you are the one that determines its look and feel.
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.
Im making a game with swift and spriteKit with xcode 6.1.
My problem is that, when I run the game on the iPhone 6 or iPhone 6+ the scene/images dont resize properly
I have this on my GameViewController
// Configure the view.
let skView = view as SKView
skView.multipleTouchEnabled = false
// Create and configure the scene.
scene = GameScene(size: skView.bounds.size)
scene.scaleMode = .AspectFill
// Present the scene.
skView.presentScene(scene)
And this on my GameScene
override init(size: CGSize) {
super.init(size: size)
anchorPoint = CGPoint(x: 0.5, y: 0.5)
let background = SKSpriteNode(imageNamed: "Background")
addChild(background)
}
I have tried all the scale modes but they dont seem to work. I hope someone can tell me what is going on
The size of the scene is being set to the size of the view in
scene = GameScene(size: skView.bounds.size)
so it is only the images in the scene that are too small to fill the view.
Make these changes to your GameViewController:
if you set the size of the scene to the size of the background image:
scene = GameScene(size: CGSize(width: backgroundImageWidth, height: backgroundImageHeight))
(replace backgroundImageWidth and backgroundImageHeight with actual values)
then set the scene's scale mode to fill the view:
scene.scaleMode = .Fill
(this may make the scene look stretched, you could use other scale modes)