SpriteKit Nodes with Gravity False tilted in place - swift

I'm building a menu Game Scene where I have a Logo and 4 buttons, all of those have a SKPhysicsBody with the rectangle size of the image.
All of those I only set:
logo.physicsBody = SKPhysicsBody(rectangleOf: logo.size)
logo.physicsBody?.affectedByGravity = false
Full code of creating the objects (all are the same)
logo = SKSpriteNode(imageNamed: "logo")
logo.anchorPoint = CGPoint.zero
logo.name = "logo"
logo.zPosition = 2
logo.size = CGSize(width: 309, height: 272)
logo.setScale(Generic.utilities.getScale())
self.addChild(logo)
logo.position = CGPoint(x: self.frame.width/2 - logo.size.width/2, y: self.frame.height - logo.size.height - 40)
logo.physicsBody = SKPhysicsBody(rectangleOf: logo.size)
logo.physicsBody?.affectedByGravity = false
and during a button press I change all the nodes affectedByGravity to true so they can fall of the screen and new elements can come in.
The problem I'm having is that when I run the app, the elements are all shifted/tilted on the screen and not in their original position
All I wanted is for them to stay in their position until I click one of the buttons and then they can fall off the screen.
Any reason why when I set a physics body and affectedByGravity they all tilt like this?

Well, Thanks to #RonMyschuk and #Knight0fDragon I found out (didn't know before) that I could add to my Scene loading the following:
skView.showsPhysics = true
Which add border lines around the physicsBody of your nodes, that way you can see them interacting. And by doing that I saw that the physicsBody of one of my nodes was completely in a different position then it should
By taking care of this issue, everything went back to normal

Related

SpriteKit 'pinned' property allows y-axis change in position

I have a SpriteKit game in which balls bounce around, interacting with other objects.
One of those other objects is a spinner that should rotate around its center, but NOT change its x/y position. It should be stationary except for the rotation.
According to Apple's documentation, node.physicsBody.pinned = true should do exactly what I want, making it so that:
"the node’s position is fixed relative to its parent. The node’s position cannot be changed by actions or physics forces. The node can freely rotate around its position in response to collisions or other forces."
However, that's not what's happening. What's happening is that the spinner's y-axis position changes when a ball hits it squarely -- briefly moving down and then popping back into the correct position.
My code for the spinner (please assume all variables are defined):
for i in 0..<spinners.count {
let spinnerNode = SKSpriteNode(texture: texture)
spinnerNode.position = CGPoint(x: spinners[i].minX, y: spinners[i].minY)
spinnerNode.size = CGSize(width: spinners[i].width, height: spinners[i].height)
spinnerNode.physicsBody = SKPhysicsBody(texture: spinnerNode.texture!, size: CGSize(width: spinners[i].width, height: spinners[i].height))
spinnerNode.physicsBody?.isDynamic = true
spinnerNode.physicsBody?.affectedByGravity = false
spinnerNode.physicsBody?.pinned = true
addChild(spinnerNode)
}
Why on earth is my spinner node moving vertically when a ball collides with it? Why isn't .pinned working as advertised?
Thank you for your help!
I solved the problem by setting the spinner node's mass to a value slightly greater than that of the ball nodes.
node.physicsBody?.mass = 6.0

Why do my camera child nodes not appear?

I have a camera node that follows the player but when the player moves my hud moves offscreen. After doing some research I found that they won't move if I make them a child of the camera but with I implement the following code my hud completely disappears. I tried making the zPosition and size very large so I couldn't miss them if they were there. But even then they are still not visible.
self.camera = cam
cam.addChild(dpadButton)
dpadButton.position = CGPoint(x: 0, y: 0)
dpadButton.zPosition = 1000
dpadButton.size = CGSize(width: 4000, height: 4000)
dpadButton.xScale = 1
dpadButton.yScale = 1.3
dpadButton.name = "DpadButton"
dpadButton.alpha = 1
Also when I have self.addChild(dpadButton) 8 nodes appear at the bottom right but when I have cam.addChild(dpadButton) only 7 appear.
I just forgot to actually add the camera node to the scene with self.addChild(cam) thanks #KnightOfDragon for helping

Why are the positions of my nodes different on every device?

Ok, so I've been trying to position a line on my iPhone 6s+ and my iPad 2. The line was added in an SKCamera (I don't know if that effects it or not). Here is the code for the line:
var leftWall = SKSpriteNode()
leftWall.size = CGSize(width: 1, height: 10000)
leftWall.position = CGPoint(x: 0, y: 0)
leftWall.color = UIColor.red
leftWall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: leftWall.size.width, height: leftWall.size.height))
leftWall.physicsBody?.affectedByGravity = false
leftWall.physicsBody?.isDynamic = false
leftWall.physicsBody?.categoryBitMask = groundGroup
leftWall.physicsBody?.collisionBitMask = ballGroup
leftWall.physicsBody?.contactTestBitMask = 0
theCamera.addChild(leftWall)
Okay, so the x position is 0,0 right? Here is how it looks on my iPhone 6s+:
As you see, the line is in the middle?
Here is how it looks on my iPad 2:
I just don't understand why the position of the line is completely different.. Please help me!
You are not taking into account the cropping that happens on the iPad, or your scaleMode is all wrong
What you need to do is set all nodes anchor points (Including the scene) to (0.5,0.5), make sure your GameScene has a static size and not based on view.frame (750,1334 is a good one) and that your scale mode is set to .aspectFill. This makes (0,0) the center of the screen, and you will notice everything line up on all devices

SpriteKit: using SKView in UIView instead of initializing a Game project

Completely new to SpriteKit. Currently I have a UIView, and I want to add a sprite node to it (like a small UIImageView, but I want animation for it so using SpriteKit). Therefore I didn't initialize my project to be a game project, as found in almost all of tutorials for SpriteKit. I've found a note here: link and what I have now is sth like:
func initializeImage() {
let imageView = SKView()
imageView.frame = CGRect(x: self.frame.width / 2 - Constants.imageWidth / 2, y: self.frame.height - Constants.imageHeight, width: Constants.imageWidth, height: Constants.imageHeight)
// so place it somewhere in the bottom middle of the whole frame
let sheet = SpriteSheet(texture: ...)
let sprite = SKSpriteNode(texture: sheet.itemFor(column: 0, row: 0))
sprite.position = imageView.center //basically the same position as the imageView.frame's x and y value
let scene = SKScene(size: imageView.frame.size)
scene.backgroundColor = SKColor.clear
scene.addChild(sprite)
imageView.presentScene(scene)
self.frame.addSubview(imageView)
}
The SpriteSheet is similar to this: sprite sheet; it's essentially cutting an image atlas and divide it into smaller images. I tracked the process and this step is indeed giving the smaller image (the var 'sprite'). But if running I only have a black square now (should be the size as defined by Constants). If I set scene.backgroundColor to be white then it's white. May I know how I should proceed from here, as how should I make the sprite showing up?
All of your code looks good except for this:
sprite.position = imageView.center // basically the same position as the imageView.frame's x and y value
That is basically not the position you think it is. The coordinate system in SpriteKit is a) relative to the (SK)scene, not to whatever view the SKView is contained in, and b) flipped vertically relative to the UIKit coordinate system. If you want a sprite centered in the scene, you probably want to set its position based on the scene's size:
sprite.position = CGPoint(x: scene.size.width / 2, y: scene.size.height / 2)
By the way, the external SpriteSheet code might not be needed (and you're more likely to benefit from Apple's optimizations) if you slice up your sprite sheet and put it in an Xcode asset catalog.

Background image in gamescene.swift

What do I need to code in order to have an image (that is already in the assets.xcassets) displayed as the background of the GameScene.swift?
First of all you could call you scene with the scaleMode .resizeFill that modify the SKScene's actual size to exactly match the SKView :
scene.scaleMode = .resizeFill
.resizeFill – The scene is not scaled. It is simply resized so that its fits the view. Because the scene is not scaled, the images will all remain at their original size and aspect ratio. The content will all remain relative to the scene origin (lower left).
By default, a scene’s origin is placed in the lower-left corner of the view. So, a scene is initialized with a height of 1024 and a width of 768, has the origin (0,0) in the lower-left corner, and the (1024,768) coordinate in the upper-right corner. The frame property holds (0,0)-(1024,768).The default value for the anchor point is CGPointZero (so you don't need to change it), which places it at the lower-left corner.
Finally, you can use the code below to add your background image (called of example bg.jpg):
// Set background
let txt = SKTexture(imageNamed: "bg.jpg")
let backgroundNode = SKSpriteNode(texture: txt, size:size)
self.addChild(backgroundNode)
backgroundNode.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
Although this might not be the best way, but it's what I always do, and it works.
Assuming that you have an image that is exactly the same aize as your scene, you can do this:
// please declare bg as a class level variable
bg = SKSpriteNode(imageNamed: "name of your texture")
// the below two lines of code is my preference only. I want the
// background's anchor point to be the bottom left of the screen
// because IMO it's easier to add other sprites as children of the background.
bg.anchorPoint = CGPoint.zero
bg.position = CGPoint(x: self.frame.width / -2, y: self.frame.height / -2)
self.addChild(bg)
Alernatively, just do this in an sks file. It's much easier.
After that, add all your game sprites as children of bg instead of self because it is easier to manage.