3

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

Luiz
  • 1,275
  • 4
  • 19
  • 35
  • FYI - your 3x image needs to be 150x150, not 200x200. Remember, the 3x image is 3x the width and height of the 1x image. – rmaddy May 14 '16 at 02:19
  • sorry, I wrote it fast. It's 150x150, actually – Luiz May 14 '16 at 02:20
  • What do you mean by *the same place on every device*? Considering that the screens are different sizes and have different aspect ratios, it's not clear how you want the positioning to work. Perhaps that lack of definition is part of the problem? – Caleb May 17 '16 at 04:37
  • sorry, it's actually the same relative place. I don't know, but let's get a Flappy Bird's bird as an example. The bird should be at the same relative position and have the same relative size for every device. This size "problem" is solutioned (at least I think it worked in my testing project) by using those 3 images for each node. – Luiz May 17 '16 at 04:51

2 Answers2

2

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)
Michael
  • 1,115
  • 9
  • 24
  • I just updated my post with some changes regarding self.bounds.width and self.bounds.height. I've also seen that my .sks scene is set to 1024x768, but did not find out what to change in there. And I can't (at least I think) set a proportion if the device's size changes on each device. – Luiz May 15 '16 at 22:14
  • I added some sample code from one of my projects for you to reference – Michael May 15 '16 at 23:24
  • I tried to do it, but it is still changing the position when I run in another device. I updated the code with this attempt. – Luiz May 16 '16 at 00:49
  • Are the sizes standard? – Michael May 16 '16 at 00:50
  • What do you mean by standard? They're 50x50 for 1x, 100x100 for 2x and 150x150 for 3x. – Luiz May 16 '16 at 00:53
  • Also declare nodeConstantWidth outside of game scene – Michael May 16 '16 at 00:53
  • Just updated the code with it, but it is still not working =( – Luiz May 16 '16 at 00:58
  • Can you give me a bit of feedback? What exactly is happening when you run it? – Michael May 16 '16 at 01:02
  • When I run on iPhone 5s simulator, it is right positioned (right before the fps and node counter - the only reference I found), but when I run it on the others devices (unless iPhone 5, of course), it isn't positioned at the same place. – Luiz May 16 '16 at 01:05
  • Ah yes. The fps counter is set in pt size, and your node is in percentage relationship to the screen. So actually, your node position should be universal. – Michael May 16 '16 at 01:07
  • If you want, you can download the project [here](https://www.dropbox.com/sh/wup5outt80ydmhc/AAAREBprSQ3NERZT-iFplljWa?dl=0) (I don't mean to be rude, I'm just trying to help you to help me) – Luiz May 16 '16 at 01:07
  • Hmm I got it. Good luck =) – Luiz May 16 '16 at 01:11
  • So you're saying that the node is in the same relative position on 5s and 6s, for example? – Luiz May 16 '16 at 01:11
  • Yeah. See for yourself. Try taking screenshots and comparing them – Michael May 16 '16 at 01:12
  • It's still not at the same relative position =( – Luiz May 16 '16 at 04:53
  • I updated my question with some comparison images that may help =) – Luiz May 16 '16 at 07:04
  • Oooh try .Fill in App Delegate – Michael May 16 '16 at 11:43
  • Sorry for my ignorance, but how am I gonna use this in App Delegate? – Luiz May 16 '16 at 15:23
  • I think it is set in scene.scaleMode, it is replace whatever it says with .Fill – Michael May 17 '16 at 02:44
  • all I found about scaleMode was inside GameViewController, but when replacing .ResizeFill for .Fill on GameViewController, my node get it's height shrank and size reduced. – Luiz May 17 '16 at 02:52
  • Oh yeah, I meant game view controller.. Umm, then if your nose has square dimensions try node.xScale = nodeConstantWidth and node.yScale = nodeConstantWidth – Michael May 17 '16 at 02:56
  • actually it's justing a testing project. My actual project has nodes with different dimensions =( – Luiz May 17 '16 at 02:58
  • Then let nodeConstantHeight = screenWidth/node.size.height*0.088 and node.yScale = nodeConstantHeight – Michael May 17 '16 at 03:05
  • that's how it's. I updated my question yesterday with your suggestions (and other answers attempts) – Luiz May 17 '16 at 03:05
  • Yes I see.. Try the nodeConstantHeight thing.. Tell me if it works, and use .Fill – Michael May 17 '16 at 03:06
  • I updated my question with these changes. It's stretching the square, as you can see [here](http://i.imgur.com/7FOO7kf.png?1) =( – Luiz May 17 '16 at 03:18
  • Final fix I promise: because we are using .Fill, just change sceenHeight to the GameScene.sks height and change screenWidth to the GameScene.sks width – Michael May 17 '16 at 03:24
  • hahah no problem. You can see what happened [here](http://i.imgur.com/SQdHEMM.png). I updated my question with this new code. – Luiz May 17 '16 at 03:31
  • Are you sure that is you .sks dimensions? Because if you use support landscape mode, the .sks flips, so try switching those two values – Michael May 17 '16 at 03:33
  • I guess it's right because the higher number (width when on landscape mode) has a W below it (width) and the lower number (height when on landscape mode) has a H below it (height). I got these values from that sidebar on the right, clicked the button on the right of the question mark button and went to sizes. All of it inside .sks file. – Luiz May 17 '16 at 03:39
  • Can you please just try switching them? I'm curious. Also, change the square.setScale to square.xScale like I mentioned earlier – Michael May 17 '16 at 03:42
  • Yes, of course. I did, but it's still not working. It went out of the screen =( – Luiz May 17 '16 at 03:51
  • Oh okay. Change it back, but make sure you do the .xScale thing – Michael May 17 '16 at 03:52
  • I think it now worked. I saw that I also had to change that proportion on `constantHeight`, since the width was working just well. I updated the code with the (I hope) last code haha. Tomorrow I'll try to adapt it to my actual project, if it really works, I come back here to accept your answer, if not, I come back here anyway hahah. Thanks you!! – Luiz May 17 '16 at 04:09
0

This is how I placed my objects

 scoreLbl.position = CGPoint(x: scene!.frame.width / 2, y: scene!.frame.height / 1.3)

 airDefense.positon = CGPoint(x: frame.size.width / 2, y: frame.size.height / 5

This takes the screen and divides it out so its always in the same place no matter how big the screen is.

It is the same as using

CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)

to place something in the middle of the screen.

Timmy Sorensen
  • 570
  • 6
  • 16
  • That's what I also thought that should happen, but I don't know why my node isn't at the same place when running on different devices than iPhone 5/5s (where was set up). By the way, I updated my question with some comparison images that may help =) – Luiz May 16 '16 at 07:03
  • It will move a little but because 1/4 of an iPhone screen is a different position for 1/4 of an iPad Pro. – Timmy Sorensen May 16 '16 at 17:57
  • but it should be at the same relative position on all devices to be universal – Luiz May 16 '16 at 18:17
  • if you want it to be exactly in the same spot on each device then you will need to create the cgpoints for each device and then check to see what device the user is using. then it would be something like this `if ipadPro == true { //use cgpoint for ipadpro }` – Timmy Sorensen May 16 '16 at 18:21
  • that's what I thought. there isn't any other easier way to do it? otherwise I should do it for all nodes (which are many) and some labels =( – Luiz May 16 '16 at 18:36