SKReferenceNode issue in iOS 11 - ios

I have found a strange issue starting with iOS 11 where my app will not update sprites on my SKReferenceNode. Basically, I have an "overlay" node (an SKS file that I use for multiple scenes) that has a header and footer. In the header, there is a score bar that expands when the player scores points. I am animating the score bar with an SKAction. Since iOS 11, the score bar does not appear. It happens both on the simulator and device. What's interesting is if I pause/resume the app through the debugger, the score bar immediately appears. I was able to strip down the issue to the following code.
import SpriteKit
var overlayNode:SKNode?
var header = SKSpriteNode()
var scoreBar:SKSpriteNode?
var scaleX:CGFloat = 0.3
class GameScene: SKScene {
override func didMove(to view: SKView) {
let path = Bundle.main.path(forResource: "SceneOverlay", ofType: "sks")
let overlay = SKReferenceNode(url: URL(fileURLWithPath: path!))
self.addChild(overlay)
overlayNode = overlay.children.first
header = overlayNode!.childNode(withName: "header") as! SKSpriteNode
scoreBar = SKSpriteNode(imageNamed: "redBar")
scoreBar?.position = CGPoint(x: 0, y: 0)
scoreBar?.zPosition = 10
scoreBar?.anchorPoint = CGPoint(x: 0, y: 0.5)
scoreBar?.xScale = 0
header.addChild(scoreBar!)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
scaleX += 0.1
let scoreBarExpand = SKAction.scaleX(to: scaleX, duration: 0.8)
scoreBar!.run(scoreBarExpand)
print ("Expanding score bar to \(scaleX)")
}
}
The overlay SKS file is simply this:
SKS Overlay Image
If you run this code on an iOS 11 simulator or device, and tap on the screen, the score bar should appear and scale up by 10% each time you tap. However, you won't see anything. You can then pause and resume the app within the Xcode debugger, and the bar will immediately appear and expand. Again, this issue does not appear on an iOS 10 device/simulator.
Any thoughts on what's going on here?
Thanks in advance.

As I've pointed in some other scenarios, but they all point to the same issue. I've discovered a serious issue with iOS11 and SpriteKit. The Game Scene during the transition is getting paused automatically (but only in iOS11).
Knight0fDragon has also pointed out that it looks like the objects are getting paused on creation not during transition. Same result though.
After transitioning to the scene run
myScene.isPaused = false
and it should fix the issue, and should have no adverse affects when the game is run on iOS10

I was having the same problem in my ObjC project.
Knight0fDragons answer didn't work directly for me, but it inspired me.
Strangely first pausing and then unpausing worked for me.
myScene.paused = YES;
myScene.paused = NO;
Hope this helps someone.

Related

SpriteKit Particles — how to set state to have already fallen when scene loads?

I'm using SpriteKit particle emitter nodes for snow:
let snowEmitterNode = SKEmitterNode(fileNamed: "Snow.sks")
guard let snowEmitterNode = snowEmitterNode else { return }
snowEmitterNode.particleSize = CGSize(width: 4, height: 4)
snowEmitterNode.particleLifetime = 10
snowEmitterNode.particleBirthRate = 10
snowEmitterNode.xAcceleration = -gameSpeed
snowEmitterNode.particleLifetimeRange = 10
snowEmitterNode.position = CGPoint(x: (self.size.width/2), y: 800)
snowEmitterNode.zPosition = 80
addChild(snowEmitterNode)
It looks great, but it only begins once the scene loads, which means there is no snow at the start, and then it all suddenly starts to fall, which obviously looks very fake. Is it possible to somehow set the state of it to have already fallen partly on scene load to avoid this?
It is.
Use the advanceSimulationTime(_:) method to advance the emission of particles by the number of seconds you specify.
E.g. snowEmitterNode.advanceSimulationTime(5)
See https://developer.apple.com/documentation/spritekit/skemitternode/1398027-advancesimulationtime for details

SKEmitterNode does not appear when running on device

I've been learning to use Spritekit in Swift these past few days using the Hacking With Swift Tutorials (Specifically Project 23). I was learning how to use SKEmitterNodes however they don't ever seem to appear. On runtime, the screen is a blank grey color that runs at 1 fps with 1 node on screen (supposedly the SKEmitterNode). I've tried changing the zPosition of the node but it doesn't seem to have any effect. Oddly, SKSpriteNodes and SKLabelNodes appear just fine. I cleaned the project and got rid of the "Hello World" in the GameScene.sks so I know its not anything from the default project. I've googled around extensively for an answer to this question but I've come up blank. Below is the offending code.
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
//backgroundColor = UIColor.black
if let starfield = SKEmitterNode(fileNamed: "Starfield.sks") {
starfield.position = CGPoint(x: 1024, y: 384)
starfield.advanceSimulationTime(10)
addChild(starfield)
starfield.zPosition = 0
}
edit: Alright guys I've solved it. It turns out Xcode doesn't like it when you try and add an SKEmitterNode exclusively programmatically (as opposed to SKSpriteNodes which it seems to handle fine). What I had to go and do was go into the GameScene.sks and manually add in my Starfield.sks file and then rename it to Starfield. Then I had to go inside the GameScene.swift file and hook the SKEmitterNode I had placed in the GameScene.sks editor by using this code inside the didMove function:
starfield = childNode(withName: "Starfield") as?
SKEmitterNode
Now the new code looks like this:
var starfield: SKEmitterNode!
override func didMove(to view: SKView) {
backgroundColor = UIColor.black
starfield = childNode(withName: "Starfield") as?
SKEmitterNode
starfield?.position = CGPoint(x: 1024, y: 384)
starfield?.advanceSimulationTime(10)
starfield?.zPosition = -1
}
I found this solution with the help of this tutorial
Looking at the code and trying it inside of Xcode, it seems it might be fine.... It runs fine on mine, but my particle emitter has a wide spread and barely visible...
Try changing the x position to the middle of the screen : 512.
It's a positioning of the emitter to a part of the screen that isn't to visible that's causing the problem.
You could also adjust the spread to make it visible from that position, but you said you are new and the most likely cause is the x position placement.
EDIT:
Ok it seems that you simply forgot to create the particles.
Just click File < New < File then click on SpriteKit Particle Emmitter, select a template you want, and finally make sure the name you gave the particle is the same as you named it in the code or vice versa.

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.

Swift Collision Not Working After SKReferenceNode Scale

I have a weird problem where my SKReferenceNode does not collide properly after being scaled up. It collides well in the center, but it ignores collisions and contacts on the edges.
Here is the first photo of the scene. The SKReferenceNode was scaled up significantly, and as seen in this photo, does not collide correctly on the edges. The PhysicsBody appears to be correct (with showPhysics on), yet the ball refuses to collide.
The SKReferenceNode is using an alpha collision mask, because I need to change it to a larger sprite in the future to do animations and such. Additionally, non-scaled objects work completely fine. Finally, after the ball collides with the center, which does work, and the block is reset, collisions start working as expected again. The code to fix it is probably in the reset function, but I reset everything before the level is loaded, so this wouldn't make sense. Here is a part of my reset code:
func reset() {
physicsWorld.gravity = CGVectorMake(0, -9.8)
for grav in gravityBlock {
grav.reset()
}
gravity = -9.8
//Resets blocks
for blocks in destroyedBlocks { //the blocks are stored in destroyedBlocks when collided with
blocks.reset()
}
destroyedBlocks = []
/*Irrelevant code removed*/
}
Here's my blocks.reset() function:
override func reset() {
super.reset()
self.physicsBody?.categoryBitMask = 1
removeAllActions()
self.texture = self.text
shadow.hidden = false
self.alpha = 0
shadow.alpha = 0
let appear = SKAction(named: "Appear")!
self.runAction(appear)
shadow.runAction(SKAction.fadeInWithDuration(0.25))
}
Here is super.reset()
func reset() {
self.hidden = false
state = .NotBroken
}
Thanks so much!
When you scale the sprite you are just changing it's visual representation. The Physics Body stays the same. That's why you are getting the "strange" during the collisions.
You can see that showing the physics uses in your scene, just open GameViewController and add this line
skView.showsPhysics = true
inside viewDidLoad, just after this line
skView.showsNodeCount = true
Then run again your game, the physics boundaries now will be highlighted.

creating sprite kit game in swift. adding a character to the scene

I am creating a game and I set up the background already. Now when I put in the code to add the character to the scene it does not appear. This is the code I used to try and get the character to appear on the screen. Apparently my character shows up behind the background. Is there another way to get the character to show in front of the background?
func createPlayer() -> SKNode {
let playerNode = SKNode()
playerNode.position = CGPoint(x: self.size.width / 2, y: 80.0)
let sprite = SKSpriteNode(imageNamed: "Pig")
playerNode.addChild(sprite) return playerNode }
In order to make the character appear in front of the background image you need to use zposition:
//You need to choose a high zposition number to make sure it shows up in front.
playerNode.zPosition = 1
// you should set the background images z position using the same method to something below 1
I would also recommend using this to add your character to the scene:
self.addChild(playerNode)

Resources