SpriteKit: Node On Top Of Another Node - swift

I want to create my player on top of the background. Somehow this doesn't work. The node counter goes up by one but it isn't visible. I do add the player after I add the background so it should be on top. The code I use to create the player:
var player = SKSpriteNode()
func addPlayer(gameScene: GameScene, xPos: CGFloat, yPos: CGFloat){
player = SKSpriteNode(imageNamed: "player")
player.size = CGSize(width: fieldsWidth, height: fieldsWidth)
player.position = CGPoint(x: xPos, y: yPos)
gameScene.addChild(player)
}
And I access the function from another class like this:
person().addPlayer(self, xPos: fieldsWidth/2, yPos: fieldsWidth/2)
Hopefully you can help me.

If you are sure that you have added node at desired position, which is probably the situation here because node count is incremented, you can set player's and background's zPosition to make player above the background:
var player = SKSpriteNode()
//1. somewhere in your code set background's zPosition to 1 before you add it to the scene
func addPlayer(gameScene: GameScene, xPos: CGFloat, yPos: CGFloat){
player = SKSpriteNode(imageNamed: "player")
//2. Set player's zPosition to be higher than background's zPosition
player.zPosition = 2
player.size = CGSize(width: fieldsWidth, height: fieldsWidth)
player.position = CGPoint(x: xPos, y: yPos)
gameScene.addChild(player)
}
About ignoresSiblingOrder...I would recommend you to leave that to true because it can help a lot when it comes to performance . The only "thing" about this kind of optimization is that you have to explicitly set zPosition for nodes at same zPosition which can overlap, but you don't want to let them overlap in random order (which ignoresSiblingsOrder does when set to true), but rather to overlap in determined order (controlled by you). This is from docs:
When this property is set to YES, the position of the nodes in the
tree is ignored when determining the rendering order. The rendering
order of nodes at the same z position is arbitrary and may change
every time a new frame is rendered.
So, just set zPosition explicitly and you will be fine.

In the View Controller of your scene, set skView.ignoresSiblingOrder = false.

Related

Swift: SKEmitterNode affected by PhysicsWorld gravity?

I'm trying to have a rain particles which are affected by wind aka physicsWorld gravity.
I can see that the gravity does has an affect on my SKSpriteNodes but I can't achieve the same affect on an SKEmitterNode.
I'm just wondering if it's possible.
Here's what I've been trying...
override func didMove(to view: SKView) {
if let rainParticles = SKEmitterNode(fileNamed: "Rain.sks") {
rainParticles.position = CGPoint(x: size.width/2, y: size.height)
rainParticles.name = "rainParticle"
rainParticles.targetNode = scene
rainParticles.particlePositionRange =
CGVector(dx: frame.size.width, dy: frame.size.height)
rainParticles.zPosition = -1
// I don't think this is right
rainParticles.physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
rainParticles.physicsBody?.affectedByGravity = true
addChild(rainParticles)
}
physicsWorld.contactDelegate = self
// gravity is pushing to the right here
physicsWorld.gravity = CGVector(dx: 20, dy: 0)
physicsWorld.speed = 0.85
}
Yes I have added SKPhysicsContactDelegate.
Obviously I want to ignore collisions so I haven't set collisionBitMask, also I don't want to have rain bouncing off anything with contactTestBitMask. I don't believe I need to set a categoryBitMask.
Particles are not represented by objects in SpriteKit. This means you cannot perform node-related tasks on particles, nor can you associate physics bodies with particles to make them interact with other content. Although there is no visible class representing particles added by the emitter node, you can think of a particle as having properties like any other object.
This is straight from SKEmitterNode documentation. Particles won't get any gravity acceleration from the physicsWorld of the scene.
Also rainParticles.physicsBody refers to the SKEmitterNode physicsBody, not its particles.
If you simply want the particles to simulate the current physicsWorld's gravity:
rainParticles.xAcceleration = self.physicsWorld.gravity.dx
rainParticles.yAcceleration = self.physicsWorld.gravity.dy

set SKEmitterNode to background

I am setting up a scene with a fixed background (a simple rectangle), an SKEmitterNode, and a backgroundLayer (a SKNode including many SKShapeNodes) : this is the hierarchy of my scene. But, even if I am adding the emitter node as a child of the scene just after the fixed background and just before the background layer, the particles emitted are at the foreground, which is not what I am expecting. indeed, I would like them to be rendered between the fixed background and the background layer, which is not happening.
I am running this app on ios 10. I've tried to add childs in a different order, or to change the target node, but it didn't change anything...
All of the following code comes from 'init(size: CGSize) {}' of a scene class which inherits from SKScene.This is all the scene set up process (when childs are added).
anchorPoint = CGPoint(x: 0, y: 0)
background.position = CGPoint(x: sceneSize.width / 2, y: sceneSize.height / 2)
background.fillColor = UIColor(white: 0.5, alpha: 1)
background.strokeColor = UIColor.lightGray
addChild(background)
setUpParticleEmitter() // it sets up the particle emitter which is an attribute of the class
addChild(backgroundEmitter) // This is the name of the emitter
backgroundEmitter.targetNode = scene
backgroundLayer.position = CGPoint(x: 0, y: 0)
addChild(backgroundLayer)
gameLayer.position = CGPoint(x: -1 * sceneSize.width, y: 0) // another layer for futur nodes
addChild(gameLayer)
I expect the particles to be rendered between 'background' and 'backgroundLayer', but they are rendered at the foreground (=over the backgroundLayer's childs), is it possible to change that ?
You can chose which nodes to be in front and which in back by setting them the zPosition.
first_node.zPosition = 1
second_node.zPosition = 2 // displayed over first node

SpriteKit - Nodes not adding to SKCameraNode - Swift

Im trying to pin the game pad controller to the bottom left on my camera node but when i add the node as a child of my camera it doesnt show up?
let gameCamera = SKCameraNode()
var joypadBackground : SKSpriteNode = SKSpriteNode(imageNamed: "a")
override func didMove(to view: SKView) {
//Set game camera
self.camera = gameCamera
joypadBackground.position = convert(CGPoint(x: 0, y: 0), to: gameCamera)
joypadBackground.size = CGSize(width: 50, height: 50)
joypadBackground.zPosition = 1000
gameCamera.addChild(joypadBackground)
}
I had a hard time with this same problem the first time I was working with SKCameraNode and creating a heads up display.
Basically you have to remember that there are two parts to the camera. Running its functionality and rendering its children. By setting the scene's camera to gameCamera you've setup the functionality, but your camera isn't in the node tree for rendering. So, if you ever have a camera that needs to render its children don't forget to add it to the scene as a child, then the camera's children will be displayed.
self.camera = gameCamera
self.addChild(gameCamera)
Hope that helps someone avoid a very common error with a very simple solution.
You don't need
convert(CGPoint(x: 0, y: 0), to: gameCamera)
You can just set the CGPoint position to (0,0) and it should be at that point relative to the camera's space.
Not sure if this helps, at all, but what I do is (generally) position a child node AFTER I've added it to its parent. This is mainly a mental reminder, to me, that the child's position is within the coordinate space of the parent. So I'd do something like this:
gameCamera.addChild(joypadBackground)
joypadBackground.position = CGPoint(x: 0, y: 0)
If you're using a mid screen origin in your SKScene, this should be in the middle of the screen.
Bottom left will be a negative x and negative y value, size of which is relative to your frame size.

Why does the physics joint stop working if the object will move out of the view?

I have a construction crane that has a joint with an object. The joint works fine if the crane is stationary.
But I added the ability for the crane to move left to right and whenever the crane is about to leave the view with the object, the object hits the edge of the screen and gets stuck while the crane keeps going. The joint also stops working even after the crane has come back to the view.
Here's an image. The yellow line at the top represents the path the hook follows forever. It goes left to right. The gold block that's around the blue rectangle is what's getting stuck. That's the block that's originally jointed with the crane's gray hook. But when the hook moves to right side of the view, the gold block hits the edge of the screen and stays there forever.
What doesn't make sense to me is that the edge of the view has a category bit mask of 0 while the gold block is at 64. In other levels, the gold blocks NEVER collide with the edge. But here when the crane moves to the edge, the gold block collides with the edge and gets stuck. As you can see, the joints are still there based on the light blue physic lines.
This is the code of the joint being called from the scene and configuring it. Like I said, joint works fine if the hook is stationary.
craneBase.alpha = 0
let path = CGMutablePath()
path.move(to: CGPoint(x: craneBase.position.x, y: craneBase.position.y))
path.addLine(to: CGPoint(x: craneBase.size.width - 100, y: craneBase.position.y + 10))
path.addLine(to: CGPoint(x: craneBase.position.x, y: craneBase.position.y))
craneHook.initializeMovingJoint(withObject: childNode(withName: "HookedObject") as! SKSpriteNode)
hook.run(SKAction.repeatForever(SKAction.follow(path, asOffset: false, orientToPath: false, duration: 10)))
physicsWorld.add(craneHook.joint)
This is the code of the crane's hook class that adds the joint to the block.
private func initHook() {
self.hookSize = CGSize(width: 10, height: 10)
self.physicsBody = SKPhysicsBody(rectangleOf: hookSize)
self.physicsBody?.categoryBitMask = PhysicsBit.none
self.physicsBody?.isDynamic = false
self.physicsBody?.affectedByGravity = false
}
//Creates the joint between the object and the hook
func initializeJoint(withObject object: SKSpriteNode) {
initHook()
hookedObject = object
jointAnchor = CGPoint(x: self.anchorPoint.x, y: self.anchorPoint.y)
joint = SKPhysicsJointFixed.joint(withBodyA: physicsBody!, bodyB: hookedObject.physicsBody!, anchor: jointAnchor)
doesHaveHookedObject = true
}
And the gold block's settings are assigned from the scene editor to a category mask of 64. I've tested the gold blocks and they don't collide with the edge. I'm not sure why it collides with the edge while moving.
I realized it was not working because the joint and the physicsWorld had the same category bit mask. Even though the gold block had a different bit mask, it was part of the joint and thus collided with the same category bit mask as none.

Swift: spritenode is still dynamic during collision even after setting dynamic to false?

Alright, so my sprite node savior needs to be static so that when it collides with my other node chicken1, it doesn't get flipped upside down or onto its side. It needs to stay right side up.
I set up savior here:
var saviorTexture = SKTexture(imageNamed: "1.png")
saviorTexture.filteringMode = SKTextureFilteringMode.Nearest
savior = SKSpriteNode(texture: saviorTexture)
savior.setScale(0.2)
savior.position = CGPoint(x: self.frame.size.width * 0.5, y: self.frame.size.height * 0.2)
//Savior physics
savior.physicsBody?.allowsRotation = false
savior.physicsBody?.dynamic = false
savior.physicsBody?.affectedByGravity = false
savior.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(savior.size.width, savior.size.height))
savior.physicsBody?.categoryBitMask = ColliderType.Savior.toRaw()
savior.physicsBody?.contactTestBitMask = ColliderType.Chicken1.toRaw()
savior.physicsBody?.collisionBitMask = ColliderType.Chicken1.toRaw()
self.addChild(savior)
As you can see, I have allowsRotation set to false and dynamic set to false, and yet rotation is still being allowed and the node is still dynamic.
Also When I turn on viewing the physics bodies, my 2 other static nodes have green physics bodies while savior has a dark blue physics body, leading me to believe that green is for static and blue is for dynamic. That makes savior definitely dynamic.
What am I doing wrong?
You are setting physics body properties before you are actually declaring one. First declare it like you already do
savior.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(savior.size.width, savior.size.height))
then set all the properties.