Luiz Luiz - 6 months ago 45
Swift Question

Universal game position

Explanation



I'm building a game in a iPhone 5s, but now I want to make it universal, so it can run in all iPhones (or at least 4s ahead) and all iPads (or at least iPad 2 ahead).

By now, I pretty much created those 3 images (1x, 2x and 3x). So there's a 50x50 square (@1x), a 100x100 square (@2x) and a 150x150 square (@3x).



This is the sample project (download here) I'm testing in:

import SpriteKit

class GameScene: SKScene {

override func didMoveToView(view: SKView) {
/* Setup your scene here */

scene!.scaleMode = SKSceneScaleMode.ResizeFill //usually at GameViewController, not GameScene

let square = SKSpriteNode(imageNamed: "square")
square.anchorPoint = CGPointZero
square.position = CGPoint(x: self.frame.width / 1.095, y: self.frame.height / 1.1875) //superior right on iPhone 5/5s
addChild(square)
}
}


and these are the images:

enter image description here enter image description here enter image description here








Testing



When I run on each device, this is happens:


  • iPhone 4s - wrong position

  • iPhone 5/5s - right position (it was set up here)

  • iPhone 6/6s - wrong position

  • iPhone 6+/6s+ - wrong position

  • iPad 2 - wrong position

  • iPad Air/iPad Air 2 - wrong position

  • iPad Pro - wrong position

  • iPad Retina - wrong position





You can see better what happens by clicking on the image below.
enter image description here








Question



Basically, my question is: how can I make this universal? I mean, how can I make the square be positioned at the same relative place on the devices above?








Attempts





Michael Austin's attempt (download here)

import SpriteKit

// MARK: Screen Dimensions
let screenWidth = CGFloat(1024)
let screenHeight = CGFloat(768)

// MARK: Node Sizes
let square = SKSpriteNode(imageNamed: "square")
let nodeConstantWidth = screenWidth/square.size.width * 0.088
let nodeConstantHeight = screenHeight/square.size.height * 0.158

class GameScene: SKScene {

override func didMoveToView(view: SKView) {
/* Setup your scene here */

scene!.scaleMode = SKSceneScaleMode.Fill //usually at GameViewController, not GameScene

square.xScale = nodeConstantWidth
square.yScale = nodeConstantHeight

square.anchorPoint = CGPointZero
square.position = CGPoint(x: screenWidth / 1.5, y: screenHeight / 1.5)
addChild(square)
}
}




Timmy Sorensen's attempt (download here)

import SpriteKit

class GameScene: SKScene {

override func didMoveToView(view: SKView) {
/* Setup your scene here */

scene!.scaleMode = SKSceneScaleMode.ResizeFill //usually at GameViewController, not GameScene

let square = SKSpriteNode(imageNamed: "square")
square.anchorPoint = CGPointZero
square.position = CGPoint(x: self.frame.width / 1.095, y: self.frame.height / 1.1875) //superior right on iPhone 5/5s
addChild(square)
}
}


the square isn't positioning at the same place on every device, only on the device where position was set up. I made a little comparison below.
enter image description here

Answer

Try mathematically setting the size. Use UIScreen.mainScreen().bounds.width and UIScreen.mainScreen().bounds.height to find the device size. Use a little math to set up a proportion between your .sks scene and the device's size. Then use node.setScale to set the scale of your Sprite

Here is some sample code. Declare these as universal constants:

// MARK: Screen Dimensions
let screenSize = UIScreen.mainScreen().bounds
let screenWidth = screenSize.size.width
let screenHeight = screenSize.size.height

// MARK: Node Sizes
let node = SKSpriteNode(imageNamed: "image")
let nodeConstantWidth = screenWidth/node.size.width*0.3

If you were to move the declaration of nodeConstantWidth to GameScene, It will continually scale itself each time the scene is rendered. To avoid this, we just declare it once universally. Mathematically, the equation above will set the node's width to 30% of the screen's width on any device, and the height will be calculated accordingly. Change the '0.3' to any number to adjust this. Then in GameScene:

node.setScale(nodeConstantWidth)