SKShapeNode Using Wrong Width and Height - ios

Recently I have been creating a game where I put circles on the screen. However, I noticed that the circulars were not circles, but in fact ovals. The y axis had been stretched of the x axis had been shrunk. In order to figure out the issue, I am now trying to debug with a rectangle. I made the rectangle have the same height and width (square) however, when turned into an skshapenode it is no longer the correct shape. The code:
var testSquare = CGRect(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame), width: 1, height: 1)
var squareNode = SKShapeNode(rect: testSquare)
println(testSquare.width)
println(testSquare.height)
println(squareNode.frame.width)
println(squareNode.frame.height)
self.addChild(squareNode)
Here is what gets printed from this:
1.0
1.0
513.5
385.5
If I change the testSquare's height and width both to 100 this is what gets printed:
100.0
100.0
612.5
484.5
For some reason the frame of the squarenode in the x adds 512.5 and in the y adds 384.5. The rectangle that is created is longer in the Y for some reason on the screen though. Maybe the frame is not the number I should be printing, but regardless the shape on the screen is NOT a square. Does anyone know why that is? Any help is greatly appreciated!

are you using the sks file to make your SKView? Or are you instantiating it in your viewcontroller? Important to get the dimensions for your scene correct. I know this isn't directly answering your question, but if you're getting weird stretching and stuff, maybe you should start with this and debug from there..
this gives me the correct scene dimensions.
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// Configure the view.
let skView = self.view as SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
let scene = GameScene(size: skView.bounds.size)
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}

Related

Xcode SpriteKit how to adapt label in GameScene with different screen sizes?

I have a label in GameScene, there is no auto layout to use here, how I can use CGpoint to make sure this label stay at the same position on different screens.
Below is the code where I put my score label
scoreLabel?.position = CGPoint(x: -size.width / 2 + 120, y: size.height / 2 - 150)
let scoreLabel = //insert functionality of scoreLabel here
scoreLabel?.position = CGPoint(x: size.width + //insert dimention,
y: size.height + //insert dimension)
self.addChild(scoreLabel)
seeing your previous comment, I would suggest you use the following functions, which change the dimensions when they are run on different screen sizes:
AspectFill
AspectFit
ResizeFill
Here is a link that could help you on how to use these functions: Sprite Kit Scene Editor GameScene.sks scene width and height
Hope this helps!
Here is what I put in my View Controller:
var h, w = 1000
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
w = view.frame.width / (view.frame.height / 1000)
let scene = GameScene(size: CGSize(width: w, height: h))
scene.scaleMode = .aspectFill
view.presentScene(scene)
}
}
}
It's handy to know that I make the height of any device 1000. It's much easier to make it universal.
Then I place everything percentage-wise on the screen.
label.position.x = w/2
label.position.y = h/2
label2.position.x = w/2
label2.position.y = h/2 - 50
These lines of code will place 2 labels in the center of the screen. Even if you rotate your device, and even if you use different sized devices. That - 50 I used will always stay the same.
My main takeaway: I create global states of the relative size of the screen.
Helpful tip: I always make my scene's anchor point to be .zero. That way, the min width and height are always 0, and the max height is always 1000.

SpriteKit: why does SKView disappear at one height but appears at another height? What's the maximum height for SKView?

The code below shows a SKView until you change skHeight to 2050 or some higher value like 5000.
1) How can you make the SKView appear even at heights of 2050 or 5000?
2) What's the maximum size for SKViews?
To reproduce:
1) Run the code below. You will see the SKView represented as a gray window.
2) Change skHeight to 2050 or 5000. The SKView no longer appears.
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Create SKView
let skHeight = CGFloat(2050)
let skFrame = CGRect(x: 0, y: 0, width: 100, height: skHeight)
let skView = SKView(frame: skFrame)
skView.backgroundColor = UIColor.red
// Add test view to <scrollView>
view.addSubview(skView)
}
}
OpenGL rendering does not support canvas sizes greater than 4096x4096 on many devices, your view is 2050x2050, so when you factor in retina mode, it really becomes 4100x4100.
Now the #3x devices are even crazier, because it needs to handle the hardware scaling and shrinking, so you only end up getting views of about 1501x1501.
Metal does not have this problem, and as of iOS 12, OpenGL is deprecated, so hopefully the Simulators will be using Metal as well. (It should since Mojave only supports metal devices)

How to prevent an object from leaving screen in swift?

I know there are a couple other questions regarding this but none of them seem to work or they all are incomplete or half complete. I need to know how to keep an object within the visible screen! So I have a spaceship sprite that uses the device's tilt and core motion to fly across the screen. If I tilt my device, the spaceship will at one point leave my screen and continue going in that direction. How do I keep the spaceship stay inside my screen so that even if I tilt to the left or right it will never leave the visible screen? Here is some of my code to the spaceship.
class GameScene: SKScene, SKPhysicsContactDelegate {
var ship = SKSpriteNode(imageNamed:"Spaceship")
let manager = CMMotionManager()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
ship.xScale = 1/5
ship.yScale = 1/5
ship.position = CGPoint(x: self.frame.width/(2), y: self.frame.height/1.5)
self.physicsWorld.contactDelegate = self
manager.startAccelerometerUpdates()
manager.accelerometerUpdateInterval = 0.1
manager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue()){
(data, error) in
self.physicsWorld.gravity = CGVectorMake(CGFloat((data?.acceleration.x)! * 15), CGFloat((data?.acceleration.y)! * 15))
}
ship.physicsBody = SKPhysicsBody()
ship.physicsBody?.affectedByGravity = true
self.addChild(ship)
}
I already tried self.view?.bounds and making it into a physics body but my spaceship never comes into contact with it.
All I had to do was meddle around with the size of the screen in the .sks file. I specifically made mine 800x640 because I am testing on an I-Pod 5. Then I added this bit of code:
let edgeFrame = CGRect(origin: CGPoint(x: ((self.view?.frame.minX)! + 200) ,y: (self.view?.frame.minY)!), size: CGSize(width: (self.view?.frame.width)! + 90, height: (self.view?.frame.height)! + 90))
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: edgeFrame)
This made a proper boundary around my screen and did not allow my spaceship to leave the screen. Best of all this was a perfect fit so if you are testing on a different device then just adjust the width and height numbers as needed. Remember to also adjust your .sks screen size.
Credits to 0x141E for hinting me in the right direction.

How to resize an SKEmitterNode?

I just wanted to do something that to me seems really simple, which is make an emitter into the entire background of a view... then I would want the view to be able to aspectFill and scale and so forth, allowing the emitter to look proper whatever I did...
I'm not looking to just scale the emitter. I want the objects to remain the right size, but i want the area of the emitter to change... think of it like the "canvas size" (without resizing) option in photoshop..
I would use this effect, for example, to add snow or rain to an entire scene, or make a snow globe sprite...
Thing is, maybe I'm just not looking in the right place, but it seems that any size properties on an SKEmitterNode are read only... what am I doing wrong?
here's some code, where the resulting emitter is just a small rectangle in the middle of the view.
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
if let emitter = SKEmitterNode(fileNamed: "Bokeh") {
emitter.position = view.center
scene.addChild(emitter)
}
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
I need to clarify a little more. For snow, I would want the particles to instantiate before they enter the scene, and then fall through the scene, and only die after they have left the scene, rather than randomly appear throughout the scene, and then continue to fall down...I would want the snow to take up the entire bounds of the scene.
What you are looking for is called particlePositionRange :
The range of allowed random values for a particle’s position.
You can change it like this:
emitterNode.particlePositionRange = CGVector(dx: dx, dy: dy)
dx should be the width of your emitter, and dy the height. So that might be the size of a scene (note that by default size of the scene is 1024x768 if not specified differently).
Same thing you can do using the Particle Editor by changing the values in Position Range section:

Getting actual view size in Swift / IOS

Even after reading several posts in frame vs view I still cannot get this working. I open Xcode (7.3), create a new game project for IOS. On the default scene file, right after addChild, I add the following code:
print(self.view!.bounds.width)
print(self.view!.bounds.height)
print(self.frame.size.width)
print(self.frame.size.height)
print(UIScreen.mainScreen().bounds.size.width)
print(UIScreen.mainScreen().bounds.size.height)
I get following results when I run it for iPhone 6s:
667.0
375.0
1024.0
768.0
667.0
375.0
I can guess that first two numbers are Retina Pixel size at x2. I am trying to understand why frame size reports 1024x768 ?
Then I add following code to resize a simple background image to fill the screen:
self.anchorPoint = CGPointMake(0.5,0.5)
let theTexture = SKTexture(imageNamed: "intro_screen_phone")
let theSizeFromBounds = CGSizeMake(self.view!.bounds.width, self.view!.bounds.height)
let theImage = SKSpriteNode(texture: theTexture, color: SKColor.clearColor(), size: theSizeFromBounds)
I get an image smaller than the screen size. Image is displayed even smaller if I choose landscape mode.
I tried multiplying bounds width/height with two, hoping to get actual screen size but then the image gets too big. I also tried frame size which makes the image slightly bigger than the screen.
Main reason for my confusion, besides lack of knowledge is the fact that I've seen this exact example on a lesson working perfectly. Either I am missing something obvious or ?
The frame is given as 1024x768 because it is defined in points, not pixels.
If you want your scene to be the same size as your screen, before the scene is presented in your GameViewController, before:
skView.presentScene(scene)
use this:
scene.size = self.view.frame.size
which will make the scene the exact size of the screen.
Then you could easily make an image fill the scene like so:
func addBackground() {
let bgTexture = SKTexture(imageNamed: "NAME")
let bgSprite = SKSpriteNode(texture: bgTexture, color: SKColor.clearColor(), size: scene.size)
bgSprite.anchorPoint = CGPoint(x: 0, y: 0)
bgSprite.position = self.frame.origin
self.addChild(bgSprite)
}
Also, you may want to read up on the difference between a view's bounds and it's frame.

Resources