Search code examples
iossprite-kitscreenresolutionvisible

SpriteKit - Getting the actual size/frame of the visible area on scaled scenes .AspectFill


I have been making a game in SpriteKit and I have come into a problem when supporting all screen sizes. I attempted to resolve the problem by setting all my scenes scale modes to .AspectFill and setting a fixed size for all screens with a width of 480 and a height of 800 as shown:

let scene = GameScene(size:CGSize(width: 480, height: 800))

I went to add a border (an SKShapeNode) that aligns to the edges of the game scene and I found that self.frame.width and self.frame.height aren't don't give the dimensions for the device's visible area. After a bit of research I had discovered a lot of suggestions by using UIScreen.mainScreen().bounds.height and UIScreen.mainScreen().bounds.width. I implemented this way of getting visible screen dimensions instead and it still gave me dimensions that didn't match the actual size of the scene of which is visible to the player.

I am unsure if it has anything to do with the fact that all my scenes no matter the device's resolution have a fixed width and height and are scaled using .AspectFill. I have been stuck on the problem of supporting all screen sizes for a while now and I am unsure of how to go about it. If it helps I am using vector graphic files rather than PNG's. So my questions are: Is there any quick way of supporting all screen sizes or do I have to resort to hardcoding the coordinates of every node on my scenes for every device? I can't see why this would be the solution as it seems too tedious and the code would look messy.

I am also asking about how to get a rectangle/frame that gives the dimensions of the scenes visible area on a device after the scene has been scaled?

I am sorry if either of my questions have been asked before but every answer I find either doesn't solve my problem specifically or seems too messy to write. I think I have given enough information towards the problem but if I haven't please ask me for the required information.

Thank you so much and any help is appreciated


Solution

  • Here is how Aspect Fill works:

    The dimensions of your scene will always remain the same, so if you say your scene is 480 points by 800 points, it will always remain that way regardless of device.

    What then happens is it will scale the scene starting from the center of the SKView until it fills the farthest walls.

    This will in turn crop your scene.

    So when designing your game across the different aspect ratios, you will need to design your game to handle cropping.

    Now in your particular case, You will need to figure out what the aspect ratio is for the device, and work off the difference from the scene aspect.

    E.G. Your scene is 3x5, device is 9x16, This means your device is going to grow to become 9.6x16 to fill up to the size of your 9x16 device. We get this because our height has more distance than our width, so we need to do 3/5 = a/16, 3*16 = a * 5, (3*16)/5 = a so a = 9.6.

    This means you have .6 of cropping to your scene on the width, .3 on both edges.

    Now you are going to ask, How does the .3 relate to the various screen sizes.

    Well we need to figure out what percentage .3 is of 9.6. We do that with division, .3/9.6 = .03125 or 3.125%; This means we need to push our border in 3.125% in the scene's width, which is 15 points. That means .03125 * 480 = 15. Your borders should then start at 15 width and at 465.

    Pseudo Code:

    let newSceneWidth = (scene.width * view.height) / (scene.height)
    let sceneDifference = (newSceneWidth - view.Width)/2
    let diffPercentage = sceneDifference / (newSceneWidth)
    
    let leftEdge = diffPercentage * scene.width;
    let rightEdge = scene.width - (diffPercentage * scene.width);
    

    Now if there is more distance to scale in the height than the width, you would have to swap the width and height variables.