Skspritenode Position in Universal Game

SkSpriteNode position in universal game

How can I set SkSpriteNode's position relative to visible area, so
that it'll be for example 50x50 from the corner on every device?

The "visible area" is simply the view.

So you can make your positions relative to the view, and not the scene. I actually do this a lot in my game, which is universal and runs on both OS X and iOS.

In my case I typically do this for the user-interface, so I might have a scaled scene but I want to set some positions not relative to the scaled scene but relative to the visible area (i.e. the view).

To do this, you can write a function that converts view coordinates to the corresponding scene coordinates.

Here is my function that I use. Note that I subtract my desired y-position from height of view so that I can treat (0,0) as the bottom-left like sprite-kit does instead of the top-left like UIKit does.

func convert(point: CGPoint)->CGPoint {
return self.view!.convert(CGPoint(x: point.x, y:self.view!.frame.height-point.y), to: self)
}

Here is an example of using this function:

self.node.position = convert(CGPoint(x: 50, y: 50)) 

This will always force the position of the node to be at (50,50) relative to the view (the visible portion of the screen) regardless of how your scene is scaled and sized.

Universal game position

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)

Universal 2D Game Assets and Absolute Node Positioning

I could simply use the existing 400x400px assets on Non-retina iPad
devices and 600x600px assets on Retina iPad devices for the house node
but the positioning of a child nodes would become broken. This is
because the child position value wouldn't change and would still be
{20,20} and {40,40} for iPad devices respectively, while the assets
would be bigger. This would yield inaccurate child positions relative
to the house node.

You can simply scale your house node (not the scene) to a larger size. All you need to do is set the scale on your house to a value that looks good on larger devices. And in fact, instead of checking for iPad we can come up with a formula that sets the scale depending on the size of the screen. Something like the code below should work. Note that it assumes your house is positioned perfectly on a iPhone 4 and it will consistently scale to all larger screens. Note that you really could pick any arbitrary size as your base case, but choosing the smallest screen and scaling up is easiest. Just be sure to provide larger textures so that the textures don't become blurry when scaled.

[house setScale:self.scene.size.width/320.0];

OR

You could use two nodes. A root node for holding the "actual" position, and then an image node child for displaying the image. This will allow you to separate your positional data from what's being displayed. You could resize and position your child image node however you want without messing with the actual position of the root node. You could even include this extra image node data in your JSON.


I could also scale the SKScene size (zoom effect) while using the
normal 200x200px and 400x400px sized assets for iPad devices
respectively. This works and it keeps the child nodes positioning
working but the rendered quality of the scene/assets is not good as it
should be. Also, this feels like a hack and we don't want that.

This option can definitely work if your App can handle the different aspect ratios in someway. For example you could allow scrolling the scene if the scene is scaled larger than the device screen. The loss in quality occurs because you are scaling the textures larger than their expected size. You need to provide larger textures to keep the quality high when zooming. In this case you could probably just use your 600x600 images (or maybe even larger) and let it scale with zoom. For example, in my RTS Sprite-Kit game for OS X I scale the entire scene so I get the same look across all devices. And I don't lose any quality because I make sure to provide very large textures so there is no loss in quality while scaling.


I could also use twice as big assets for iPad devices and double the
child nodes position at the runtime. In this case I would use a
400x400px asset for non-retina iPad devices and a new 800x800px asset
for retina iPad devices. While this looks great and keeps the child
nodes positioning working, it seems like a really big hack fixing
child node position during runtime with this:

This could also work, especially if your iPad requires custom layout. However, if possible avoid checking specifically for iPad and instead use the screen size to create layout rules so your nodes dynamically adjust on all screen sizes consistently (See the line of code above). Sometimes this is not possible if your iPad layout is very different from the iPhone, in which case you will have no choice but to check for iPad.


All three of these solution are good. I wouldn't consider any one of them "hacky." They all work for different purposes. You need to find the solution that works best for your game.

I would also recommend you see my two answers below. Not sure but they may help you with understanding universal positioning and scaling in Sprite Kit.

https://stackoverflow.com/a/25256339/2158465

https://stackoverflow.com/a/29171224/2158465

Good luck with your game, let me know if you have any questions.

How can I make SKSpriteNode positions the same for any simulator/device?

If you are making a Universal application you need to declare the size of the scene using integer values. Here is an example:

scene = GameScene(size:CGSize(width: 2048, height: 1536))

Then when you initialize the positions and sizes of your nodes using CGPoint and CGSize, make them dependant on SKScene size. Here is an example:

node.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2)

If you declare the size of the scene for a Universal App like this:

scene.size = skView.bounds.size

then your SKSpriteNode positions will be all messed up. You may also need to change the scaleMode to .ResizeFill. This worked for me.

Universal app background SpriteKit

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!

How to scale/position nodes Swift SpriteKit? Custom View?

To keep my games universal I make all my nodes sizes and positions dependant on SKScene size

Instead of using hardcoded numbers try using proportions from your SKScene size. For example:
Instead of writing this:

node.position = CGPointMake(100, 100)

Write somthing like this:

node.position = CGPointMake(world.frame.size.width / 32 * 16, world.frame.size.height / 18 * 9)

world - SKNode of exactly same size as your SKScene

Same for sizes

let blackSquare = SKSpriteNode(texture: nil, color: UIColor.blackColor(), size: CGSizeMake(world.frame.size.width / 32, world.frame.size.width / 32))

Also GameViewController should look something like this:

override func viewDidLoad() {
super.viewDidLoad()

// Configure the view

let skView = view as! SKView
skView.multipleTouchEnabled = false

// Create and configure the scene

let scene = GameScene(size: skView.bounds.size)
scene.scaleMode = .AspectFill
scene.size = skView.bounds.size

skView.presentScene(scene);
}

Was this helpfull?

How do I size sprites in a universal sprite kit game

What we usually do in SpriteKt is to give the SKScene a fixed size and let SpriteKit do the scaling for you on different devices.

So basically we have 2 ways to do it correctly

1) Set scene size to iPad (e.g 1024x768 -landscape, 768x1024 - portrait). This was the default setting in Xcode 7.

You than usually just have/show some extra background at the top/bottom (landscape) or left/right (portrait) on iPads which gets cropped on iPhones.

Examples of games that show more on iPads / crop on iPhones:

Altos Adventure, Leos Fortune, Limbo, The Line Zen, Modern Combat 5.

2) Apple changed the default scene size in xCode 8 to iPhone 6/7 (750*1334-Portait, 1337*750-Landscape). This setting will crop your game on iPads.

Examples of games that show less on iPads:

Lumino City, Robot Unicorn Attack

Choosing between those 2 options is up to you and depends what game you are making. I usually prefer to use option 1 and show more background on iPads.

Regardless of scene size scale mode is usually best left at the default setting of .aspectFill.

You would use the Universal asset slot and/or device specific images. This way you will have a consistent experience on all devices

Spritekit scale full game to iPad

How to make SKScene have fixed width?

Hope this helps



Related Topics



Leave a reply



Submit