I am having trouble fully grasping the coordinate system with SpriteKit using Swift. I have looked at many resources including apples dev docs but for some reason I am very confused (i'm sure this must be simple).
I have a GameScene class inheriting from SKScene. I am just trying to add a SKSpriteNode to the screen using various coordinates to get used to the screen layout. I believe that when you create any SKNode and give it a position by doing the following
var node = SKSpriteNode(color: UIColor.brownColor(), size: CGSizeMake(100, 100))
node.position = CGPointMake(0, 0)
and then place any SKNode using
self.addChild(node)
The coordinates that have been supplied to the node are coordinates in the parent node. Does this mean that the brown square that has been created should be placed at the bottom left corner?
This is obviously not the case and is what is confusing me. I am using the iPhone 5s simulator for testing in portrait.
The sprite node only appears in the bottom left when I set the position to (350, 50).
Select the GameScene.sks in the Project Navigator, select the SKScene (it should have a yellow border round it) and set its size property to 320 points x 569 points (this is assuming iPhone5 / 5S) either way this is the problem. Also make sure you double check it as I am sure it changed back to 1024x768 on me, but seems to be working now.
In the GameViewController class at the function viewDidLoad(), add this line of code:
scene.size = skView.bounds.size
Related
I have been looking and trying all solutions I've found on the internet. None of them are working as expected. My game requires a border as shown in the image attached. It works fine with iPhone 8 and 8 Plus. On X, I cannot seem to find the proper way to fill it without disrupting my game scene. Even if I use resizeFill or aspectFill it cuts off the important contents from my gaming area. The one in the image is aspectFit but gives me black borders which does not look good and is not taking advantage of the larger screen. The layout in the GameScene file is not according to iPhone X. How can I make the game optimised for all of these 3 devices. I know how to do it for an app on storyboard with constraints and auto layout, but I am kinda new to SpriteKit and GameScenes. I do not mind even if the black borders on the iPhone X is filled the colour of my border. But I do not know how to implement it and I do not think its the best practice for optimising games for all of these 3 devices. Thank you in advance for your help.
I guess you want something like this right 👆
1. Resizable scene
First of all you want your scene to resize (changing size and aspect ratio) in order to perfectly match the display. So open GameViewController.swift and set
scene.scaleMode = .resizeFill
2. Resizable rectangle
Now you want to create a rectangle matching the perimeter of your scene.
That's easy, open your scene and write what follows
class GameScene: SKScene {
override func didMove(to view: SKView) {
let rectangle = SKShapeNode(rect: self.frame)
rectangle.strokeColor = .green
rectangle.lineWidth = 20
addChild(rectangle)
}
}
How does it work?
As you can see I am creating a rectangle using the same frame of the scene. So, you are sure your rectangle will always match the perimeter of the scene (which will match the display).
That's it.
I am making this basic space shooting game but I can't get the x-coordinates of enemies right. Sometimes, they go out of the screen - or remain half inside at the edges. How can I fix this permanently regardless of which iPhone the app runs on?
here is the code for my positioning: (note that egg is name of my enemy. it is function I made for calling it every single time)
func egg() {
var egg = SKSpriteNode(imageNamed: list[Int(arc4random_uniform(6))])
var min = self.size.width / 8
var max = self.size.width
var point = UInt32(max - min)
egg.position = CGPoint(x: CGFloat(arc4random_uniform(point)), y: self.size.height)
let action = SKAction.moveToY(-100, duration: 2)
let actionDone = SKAction.removeFromParent()
egg.runAction(SKAction.sequence([action, actionDone]))
}
There are several places where things can be going sideways.
• First, it looks like you're assuming the location of your scene's anchorPoint is at (0,0). If it's not (the default .sks file now has an anchorPoint at (0.5,0.5), for instance), that could explain why your enemies never show up. The first thing I would do is double-check the anchorPoint of my scene.
• If you're adding these eggs to some other node besides the scene itself, then those eggs will "inherit" whatever translation, rotation, and scale that their parent node has. In other words, if you add an egg at (20,20) to a node at (30,30), it will appear at (50,50). Make sure the "context" of the eggs is what you expect.
• Your "min" and "max" values seem a little odd. The "min" looks like it is intended to be "indented" by 1/8th of the screen size on the left, but you aren't indenting it on the right. Maybe that's what you intend, but if you intend them to have symmetrical behavior, then you'll want to back "max" off by an eighth, too. Also, you're not adding that eighth back into the x value when you're determining the position, so this could stick an egg at x=0. This would explain why your sprites are sometimes "half inside at the edge".
• The size of your scene is not necessarily the screen dimensions. This depends heavily on the "scaleMode" of your SKScene. Check the documentation for more information on this, but briefly, the scaleMode tells SpriteKit how to render a scene that doesn't match its view in size or aspect ratio. Does it stretch? Does it crop? Does it letterbox? If you run a square 400x400 scene on a 1024x768 screen device, it has to have some way of knowing what you mean. Does it draw that 400x400 in the middle of the screen and let stuff be seen "outside" that rect? Or does it scale it up to fill the screen, cropping off the top and bottom? Or does it scale it up to FIT the screen, allowing space above and below that is technically outside the scene's size? Or does it scale it up and squash the scene to fit exactly? If your scene isn't matching up exactly with your device's screen, this could explain why things are not playing nice and staying within visible bounds.
I'm doing my first iOs game using Sprite Kit and Swift.
I start positioning all my Sprites and labels like:
sprite.position = CGPoint(x: 0, y: 200)
when i run in a 4-inch device, it looks really good but when i run it in a 3.5 device the game looks incomplete.
Is there any good solutions to resize all the layers instead of redesign all my scenes?
If you didn't change the initial setting for your scene scaleMode, you should have it set to .aspectFill. This setting will size your scene keeping it's aspect ration but trying to fill your screen. It will chop part of it basically. There's little you can do except go for an .aspectFit setting. It will keep the aspectRation but will fit all your scene in the screen leaving you with black letter boxing bands.
Most people do not use .aspectFit but if you know how to resize the scene depending on the screen size, you can add the needed padding on either side to remove the black letter boxing. I created a framework that does that for you and also calculates your original anchor point for your scene so you loose nothing of your current coordinates implementation. All with 2 methods.. I highly suggest you take a look at it: SceneSizer
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
Documentation is in the folder for a clean and full use with a standard scene. Hope this helps!
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.
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).