Dealing with different iOS device resolutions in SpriteKit - ios

I'm playing around with SpriteKit in Xcode 6, iOS 8 beta 5. Everything is all laid out and working perfectly on the iPhone 4S simulator, however when switching to the 5S, the elements at the bottom of the screen are cut off.
It was to my understanding that the bottom left corner of the iPhone screen should be CGPoint(0, 0) but after checking the location by printing the coordinates to the console that the lowest point of the left corner I could click was around (5, 44). Is there something wrong in my scene setup thats causing this?
No changes have been made to the GameViewController file and even after I strip the GameScene file the problem persists.
Can anyone at least point me in the right direction with this?

Adding the following code will fix your problem (code is in Swift):
scene.scaleMode = SKSceneScaleMode.ResizeFill
Now if you want to know why this fixes your problem, what your problem actually is, and how to handle multiple resolutions – I suggest you continue reading.
There are three things that can impact the position of nodes in your scene.
1) Anchor Point
Make sure your scene's anchor point is set to (0,0) bottom left. By default the scene's anchor point starts at (0,0) so i'm assuming that is not causing the issue.
2) Size Check the size of your scene. I typically make my scene size match the size of the device (i.e. iPad, iPhone 4-inch, iPhone 3.5 inch), then I place another layer in the scene for storing my nodes. This makes me able to do a scrolling effect for devices with smaller resolutions, but it depends on your game of-course. My guess is that your scene size might be set to 320, 480 which could be causing the positioning problems on your iPhone 5s.
3) Scale Mode The scale mode has a huge effect on the positioning of nodes in your scene. Make sure you set the scale mode to something that makes sense for your game. The scale mode kicks in when your scene size does not match the size of the view. So the purpose of the scale mode is to let Sprite Kit know how to deal with this situation. My guess is that you have the scene size set to 320,480 and the scene is being scaled to match the iPhone 5 view which will cause positioning problems identical to what you described. Below are the various scale modes you can set for your scene.
SKSceneScaleMode.AspectFill
The scaling factor of each dimension is calculated and the larger of
the two is chosen. Each axis of the scene is scaled by the same
scaling factor. This guarantees that the entire area of the view is
filled, but may cause parts of the scene to be cropped.
SKSceneScaleMode.AspectFit
The scaling factor of each dimension is calculated and the smaller of
the two is chosen. Each axis of the scene is scaled by the same
scaling factor. This guarantees that the entire scene is visible, but
may require letterboxing in the view.
SKSceneScaleMode.Fill
Each axis of the scene is scaled independently so that each axis in
the scene exactly maps to the length of that axis in the view.
SKSceneScaleMode.ResizeFill
The scene is not scaled to match the view. Instead, the scene is
automatically resized so that its dimensions always matches those of
the view.
Conclusion
It looks like you want to remove the scaling of your scene, that way your positions in the scene will match the actual positions in the view. You can either set your scene's size to match the view size, in which case no scaling will take place. Or you can set your scene's scale mode to ResizeFill which will always make the scene's size match your view's size and it won't scale anything. In general I would stay away from any scaling and instead adjust the interface and the scene size to best suit each device. You may also want to add zoom and/or scrolling to allow devices with smaller resolutions to achieve the same view field.
But what if I want to scale my scene?
If however you need to scale your scene, but you still want positions to be relative to the view (i.e. You want (0,0) to be the bottom left of screen even when scene is cutoff) then see my answer here
Additional Info
See answer here for sample code showing how I layout nodes dynamically.
See answer here for more details about scaling to support multiple devices.

If you want to preserve the size of your scene (usually desired when you work with a fixed size and coordinates system), you might want to add padding to either side of your scene. This would remove the letter boxing and preserve all the physics and dynamics of your app on any platform.
I created a small Framework to help with this:
https://github.com/Tokuriku/tokuriku-framework-stash
Just:
Download the ZIP file for the Repository
Open the "SceneSizer" sub-folder
Drag the SceneSizer.framework "lego block" in your project
Make sure that the Framework in Embedded and not just Linked
Import the Framework somewhere in your code import SceneSizer
And you're done, you can now call the sizer Class with:
SceneSizer.calculateSceneSize(#initialSize: CGSize, desiredWidth: CGFloat, desiredHeight: CGFloat) -> CGSize

Just in case, try doing CMD+1, worked for me. Some of the elements were cut off because they were simply not displayed in Simulator - I stress this, this is just a simulator feature (and a bug if you ask me, wasted hours of time to solve this). CMD+2, CMD+3 views can sometimes hide parts of the scene.

Related

iOS landscape positioning problems Swift SpriteKit

For some reason, I am unable to position a node at the top or bottom of my screen. I used to be able to do this just fine using self.frame.height / 2 but now that I am working in landscape, this line of code is not working.
Notes: my anchor point is 0.5, 0.5 and my scale mode is .aspectFill
Thanks for all your help.
This probably has something to do with the aspect ratio of your scene and using .aspectFill.
When you use .aspectFill, SpriteKit will resize the scene to fill the viewport from edge to edge, both horizontally and vertically, but it won't stretch it. That means that for a landscape orientation, you will probably end up "cropping" off the top and the bottom of the scene unless you have a particularly shallow scene designed.
In other words, you're encountering the opposite of letterboxing. The width and height of the scene are fixed, and when the device size differs from that in aspect ratio, it "overflows" one axis offscreen. In your case, it's probably overflowing the scene off the top and the bottom in order to fill your scene left and right.
There are a few strategies to fix this.
First, you could choose a different scaleMode. .aspectFit, for instance, would probably return your height calculations to what you expected, but it will come at the cost of also including letterboxing on the left and right, which is probably not what you want, but maybe.
Second, you could compute a new scene size to match the aspect ratio of your viewport. This will give you the behavior you expect in both directions, but it will also mean that your scene varies in aspect ratio based on the device it is played back on, which will require you to adjust your gameplay and scene layout to accommodate this variable shape. This is a lot of work, but it is typically the best solution. (One solution is to resize the scene, but then leave your "real scene" centered in the middle of it, but that can be unsatisfying and problematic for some game types.)
Third, you could use the scaleMode that simply scales the scene to the viewport. This will cause some squishing/stretching depending on the device, so it is generally not a great option, but depending on the game type you are making, this might be an option.

Body Type = Alpha Mask Scaling Issue Sprite Kit

I am creating a Sprite Kit game were it's important for me to be able to exclude transparent parts of SKNode Textures when listening to "touches" (touchesbegan).
The solution that works for me is to set the physical body of the nodes to be
BodyType = Alpha mask.
In touches began method I can now iterate over all physicsbodies that are located in the point of the touch.
The PROBLEM now is that this works but the area of the masked body seems to be bigger then the actual size of the Node that but the shape of the body is correct.
Actually the physical body seems to be as big as the original image is before I set the node to be lets say half the size of the original image.
I have seen this behaviour previous in Sprite Kit.
My Scene in Interface Builder is 1024 points wide and I use AspectFill as the scaling option. What I can see is that if I print the scenes size its actually telling me 375 when running on the Iphone 6s (as the resolution of the iphone6s)
But e.g if I want a node to be wide as the screen I have to set it to 1024.
Can someone please help me understand this as it seems scaling problem.

IOS Swift SpriteKit Scaling Entire Views According to Frame Size

I would like the scenes of my game in SpriteKit to appear differently sized depending on the size of the device. Right now I am manually resizing and positioning some of my nodes with an 'if' statement checking for frame size, but unfortunately I cannot do this for all of my sprites due to the mechanics of my game (I am detecting collisions based off of the positions of my nodes, not by using PhysicsBodies. If I were to change the size of the nodes, these collisions would not be detected).
Is there a way to scale the appearance of the view according to the device size instead of actually scaling the sprites themselves (i.e. the view would stretch out to fit frame size but in reality the sprites would actually be the same size)? Is this something I would achieve by changing the SKSceneScaleModes from .ResizeFill (the one I am currently using)?
I don't believe there is an easy way to fix my problem, nor do I believe that the solution I was looking for exists. This is how I fixed my problem:
First, I changed my game mechanics. I've learned that when you are going about designing and initially planning out your game, you have to really focus on making your game both easily expandable and universal. The way that I had my game first set up was quite limited.
I figured out how I wanted to scale my game and set up my own little system (not really my 'own', its probably a pretty common setup):
var scene = GameScene(size: self.size)
var skView = self.view! as SKView
scene.size = skView.frame.size
var scale = self.frame.size.height / 736
Then I just set the scale to my nodes like so:
self.titleText.setScale(scale)
First, I am setting the size of the current scene as the size of the frame size of the skView. With that, I am creating a scale factor by dividing the size of the scene by the frame size of an iPhone 6 plus. The sprites will now appear at a scale factor of 1 on the iPhone 6 plus and downscale from there.
Anyways, I know this explanation wasn't perfect and that there is probably a more efficient way of doing this. I am relatively new to SpriteKit and have a lot to learn, but I didn't want to leave this question unanswered on the off chance that somebody stops by here. Thanks, and feel free to message me if you have any questions or I messed up somewhere.

Do I need to create a new SpriteKit level editor file for every new .swift file?

I'm trying to get comfortable with Sprite Kit level editor. By default, there is one "gamescene.sks" file that's attached to "gamescene.swift".
If i'm making a "gameoverscene" or "playscene" for example, do I need to create both of them (.swift and .sks) if I want to work with my game in the level editor ?
Also, I'm interested in it's size management. I'm making a universal devices game. Should I change it's default size (1024x768) or do not bother ?
Also that scene canvas looks horizontal, should I change it if my game is a portrait-mode only ?
Short Answer. Yes.
Reason..
Every time you want to present or load an .sks file, You need to load it with a class like so
let doors = SKTransition.doorwayWithDuration(1.0)
let archeryScene = GameScene(fileNamed: "GameScene")
self.view?.presentScene(archeryScene, transition: doors)
If you look a the constant loads the file with a class
MyScene(fileNamed: "MyScene")
So yeah you do need a .swift file for each .sks file.
For more info on .sks files look here.
Techotopia - An iOS 8 Swift Sprite Kit Level Editor Game Tutorial
Also when I use the Spritekit Level Editor, I usually set the Scene size to 960 x 640 to support the smallest iPhone. Alsong as you have #1x, #2x, #3x and possibly #1x~iPad and #2x~iPad, Everything will be fine. Just to be sure in the ViewDidLoad or the initWithSize(CGSize) to add self.scaleMode = .AspectFill. You can set it to what ever it may be. Your options are,
SKSceneScaleMode.Fill
Each axis of the scene is scaled independently so that each axis in the scene exactly maps to the length of that axis in the view.
SKSceneScaleMode.ResizeFill
The scene is not scaled to match the view. Instead, the scene is automatically resized so that its dimensions always matches those of the view.
SKSceneScaleMode.AspectFit
The scaling factor of each dimension is calculated and the smaller of the two is chosen. Each axis of the scene is scaled by the same scaling factor. This guarantees that the entire scene is visible, but may require letterboxing in the view.
SKSceneScaleMode.AspectFill
The scaling factor of each dimension is calculated and the larger of the two is chosen. Each axis of the scene is scaled by the same scaling factor. This guarantees that the entire area of the view is filled, but may cause parts of the scene to be cropped.
(Credits to #epicbyte - Dealing with different iOS device resolutions in SpriteKit)

Spritekit - Bottom of screen coordinates

What are the coordinates for the bottom of the screen... or how can I create a "floor" at the bottom of the screen in spritekit?
Sorry, but I don't understand screen coordinates that well in spritekit.
You need to understand the Sprite Kit coordinate system as explained in Apple's Documentation here.
Here's how you create a floor at the bottom of the screen in SpriteKit:
SKNode *floor = [SKNode node];
node.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(CGRectGetMidX(self.frame),1.0 , CGRectGetWidth(self.frame), 1)];
[self addChild: floor];
You need some universal approach to get coordinates of corners on the screen.
Using code from that answer you can get CGRect with necessary information.
Example:
let screenRect = getVisibleScreen(
sceneRect: self.scene!.frame,
viewRect: self.view!.frame )
And then you can use it:
screenRect.minX
screenRect.maxX
screenRect.minY
screenRect.maxY
screenRect.width
screenRect.height
This is more then enough to calculate coordinates of "floor" or any other relative positions.
The location of the bottom of the screen will depend on what coordinate system you are using for your scene.
Out of the box, the bottom of the screen will be at y coordinate zero, but there are a few things that can happen that will affect that.
For instance, if you are using the scene editor in xCode, and your scene's anchorPoint property is something other than y=0, then the "origin" of your scene will not be at the bottom of the screen. In the recent xCode beta, they changed the default behavior to have the scene's origin at the center of the scene instead of the lower left corner, so that would explain why you might be seeing things in the center of the screen when you expect them to be at the bottom.
Also, the "bottom of the screen" will be relative to whatever parenting structure you have in your scene. For instance, if you place a background sprite in your scene, and want to attach a floor sprite to that which is at the bottom of the screen, you'll have to do some computing to figure out where to place it because you are going to inherit the translation and rotation of the floor's parent node (and any parents that node has).
To keep things simple, you can just place everything directly on the stage and manage their z-order manually. This will let you, basically, use the same coordinate system for everything. This is often fine; as long as you're not trying to do anything complex with your sprites, you don't need a complicated "tree" of nodes.
But even with this approach, the metrics of your scene are going to have to be handled dynamically. The width and height of your scene are going to depend on how you approach displaying your scene on different devices with different sizes. For instance, the top right of an iPhone 4 is going to be in a different place than the top right of an iPad Pro. A full discussion of how to deal with that is beyond the scope of your question, but generally, you'll probably want to use a "reference width" or a "reference height" for your scene, use .AspectFit or .AspectFill for the scaleMode, and set your scene's size accordingly. (I.e., inspect the view's frame to get the actual aspect ratio of your scene and set your scene size to match your reference metric on one axis and scale the other axis to match the device's aspect ratio.) This will let you use the same metrics for all devices (although one of your two axes will be fluid).

Resources