AtomProgrammer
AtomProgrammer

Reputation: 347

Positioning nodes on a scene to work for all screen sizes SpriteKit

I have tried everything and nothing seems to work for positioning nodes for a universal SpriteKit game. I currently use .AspectFill as my scaling ratio and I have tried making unnecessarily complex algorithms to fit nodes (e.g buttons, a title image, etc) for my main menu in my game into bounds or sub sections of the scene. I have even tried positioning things relative to the visible parts of the scene. e.g

 sprite.position = CGPoint(scene.size.width/2, scene.size.height/2 + scene.size.height * 0.4) 

and yes this works for gameplay elements such as a player I have figured that this method is horribly unpredictable for positioning HUD and menu screen elements as buttons then tend to overlap because of dynamic sizing of sprites and/or unknown scaling of scene depending on user's device (If I get it perfectly positioned on iPhone it goes off of the screen on iPad and vice versa) e.g

sprite.size = CGSize(screen.size.width * 0.6, screen.size.width * 0.6)

I then went onto making an extension of SKSpriteNode so that it can resize itself depending on a given bounds (CGRect) and then I realised just how complex, messy and time consuming positioning one node on my scene was. I must have the complete wrong idea of positioning nodes in my scene or I am doing it wrong. Now you understand the context here are my questions:

if scene.size.width/scene.size.height >= 16/9 { //Create position here for this device } else if scene.size.width/scene.size.height >.... { etc etc

This method also seems unnecessary but I could be wrong

OR

I cannot be the only person with this problem and from doing research for hours and hours reading every single stackoverflow answer and webpage Google returns to me that might have my answer I still have not found a solution that works for my problem. This question has been asked plenty of times but obviously the answers do not match to my learning style and it needs to be explained in a different way so that I finally understand it and with the amount of apps on the app store surely someone has an easy solution to such a stressful problem that is causing me to feel unmotivated to make my app and that shouldn't be the case.

Upvotes: 0

Views: 772

Answers (2)

Charlie Dancey
Charlie Dancey

Reputation: 43

Here's a sensible route to go.

In your gameScene.didMoveTo(), do this:

let screenSize = UIScreen.main.bounds.size
let screenScale = UIScreen.main.scale
   
self.size.width = screenSize.width * screenScale
self.size.height = screenSize.height * screenScale
    
self.scaleMode = .aspectFill

Yes, you can adjust the scene size dynamically, so that's what we do.

The screenScale is an adjustment for retina devices, it basically maps points (like CGPoint coordinates) to the physical pixels of the screen. Currently retina devices use a value of 2, but don't assume it will never change as new devices come out.

Now you can be sure, whatever device you are running on, that the scene size matches the view size.

The .aspectFill doesn't really do anything since the scene and it's view now match proportions exactly, but I guess it's best to set it to something.

What you now have is a scene that's exactly the same size at the physical screen, so positioning objects is very simple.

Assuming that you scene's origin is in the centre you can calculate, say, the top left corner position as:

CGPoint(x: -scene.size.width / 2, y: scene.size.height / 2)

But take care if you don't want stuff clipped by the notch or the rounded corners.

You may want to take a look at:

UIApplication.shared.windows.first.safeAreaInsets

Which will advise on how far to stay clear of notches etc.

Upvotes: 0

Nik
Nik

Reputation: 1664

Since you're using AspectFill as your scaleMode, the scene automatically scales it. With this, you would then make the screen size 768x1024 (portrait) or 1024x768 (landscape). To make this compatible with all devices simply make the background 1024x1024 so it looks fine on iPad and you can adjust HUD elements for iPad only by doing something like this:

if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
    yourButton.position.y += 100
    yourLabel.position.y += 100
    etc
 }

You don't need any complex algorithms and the scene looks great on all screens. I also suggest you change the anchorPoint of the screen to the middle so you position all the nodes from the middle rather than from the bottom left corner as this makes it much easier to centre things for all devices

Upvotes: 0

Related Questions