Sprites disappears sometimes [Sprite Kit] - ios

Main menu looks like this.
When i press start code below changes the scene.
let newScene = LevelScene(size: self.scene!.size)
let transition = SKTransition.revealWithDirection(SKTransitionDirection.Up, duration: 1)
newScene.scaleMode = SKSceneScaleMode.AspectFill
self.scene!.view!.presentScene(newScene, transition: nil)
And after I return to the main menu it looks like this
or this
So, as you can see, sprites just disappears. I can't come up with a reason for this to happen.

Are you doing,
self.addChild(...)
self.addChild(...)
in your didMoveToView method?
Another possible problem might be that you are setting the zPosition of the node to less than your background, and / or other nodes.

I had the same problem with sprites disappearing and after changing the zPositions of all my sprites and background I still wasn't able to solve it.
I ended up just redeclaring the scene inside my restartButton:
var scene = SKScene(fileNamed: "GameScene")
scene?.scaleMode = .aspectFill
view!.presentScene(scene)
And that worked for me.

Related

Scene transition troubleshoot in spritekit in swift

I'm Trying to make a Start Scene Where you press a "Start" Button, and it goes to the main game scene. Here's the code:
if startBTN.containsPoint(location) {
let gameSceneTemp = GameScene(fileNamed: "GameScene")
self.scene?.view?.presentScene(gameSceneTemp, transition: SKTransition.fadeWithDuration(0.01))
}
But for some reason, whenever the scene loads up, it displays a squished-like scene in the GameScene. Can anyone help solve this problem by displaying a normal size (portarit) scene? Here's the Screen shots between the expected and actual scene:
The Expected Scene
The Actual Scene (The Problem)
It turns out, I didn't set the scene mode. I looked this problem up and I finally found the solution. Here's the code:
if startBTN.containsPoint(location) {
var gameScene = GameScene(size: self.size)
var transition = SKTransition.fadeWithDuration(0.8)
gameScene.scaleMode = SKSceneScaleMode.AspectFill
self.scene!.view?.presentScene(gameScene, transition: transition)
}
I had put the gamescene file and transition, but didn't add the scalemode to it.

SceneKit load node with animation from separate scn file

I have a view that creates SCNView dynamically. It's scene is empty, but when I press a button I would like to add a node from separate scn file. This file contains animation, and I would like it to animate in main scene. The problem is that after adding object to the scene it's not animating. When I use this file as SCNView scene it works. isPlaying and loops are enabled. What else do I need to do to import such node with animation? Sample code below:
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene()
let sceneView = SCNView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
sceneView.scene = scene
sceneView.loops = true
sceneView.isPlaying = true
sceneView.autoenablesDefaultLighting = true
view.addSubview(sceneView)
let subNodeScene = SCNScene(named: "Serah_Animated.scn")!
let serah = subNodeScene.rootNode.childNode(withName: "main", recursively: false)!
scene.rootNode.addChildNode(serah)
}
You need to fetch the animation from your scene Serah_Animated.scn, which will be a CAAnimation object. You then add that animation object to the rootNode of your main scene.
let animScene = SCNSceneSource(url:<<URL to your scene file", options:<<Scene Loading Options>>)
let animation:CAAnimation = animScene.entryWithIdentifier(<<animID>>, withClass:CAAnimation.self)
You can find the animID from the .scn file using scene editor in Xcode, as shown below.
Now you can add the animation object to your root node.
scene.rootNode.addAnimation(animation, forKey:<<animID>>)
Note that we are reusing animID, that will allow you to also remove the animation from the node.
scene.rootNode.removeAnimation(forKey:<<animId>>)
My solution above assumes your animation is a single animation. If you see a bunch of animations, you need to add all the animation nodes. In my workflow, I have files in Blender which I export to Collada format and then use the Automated Collada Converter to ensure I have single animation nodes.
Related SO answer
You can also fetch the animID programmatically using entriesWithIdentifiersOfClass(CAAnimation.self), useful when you have a bunch of animations instead of a single animation as above or if you just want to add the animation without bothering about the animID before hand.
Apple Sample Code for scene kit animations, note the sample code is in ObjC but translation to Swift should be straight forward.
All you need is retrieving animations:
[childNode enumerateChildNodesUsingBlock:^(SCNNode *child, BOOL *stop) {
for(NSString *key in child.animationKeys) { // for every animation key
CAAnimation *animation = [child animationForKey:key]; // get the animation
animation.usesSceneTimeBase = NO; // make it system time based
animation.repeatCount = FLT_MAX; // make it repeat forever
[child addAnimation:animation forKey:key]; // animations are copied upon addition, so we have to replace the previous animation
}
}];

SpriteKit SKView no longer transitions between scenes in iOS 9

We have a code base that was written and released in 2013, but in iOS 9 the app no longer transitions visibly between SKScenes when the presentScene:transition: message is sent to our SKView. The SKScene receives the didMoveToView: message but the scene itself never shows on screen.
Here's what we tried:
Disabling Metal via the Info.plist
Using [SKTransition transitionWithCIFilter:duration:] instead of the predefined animations
Tweaking zPosition
Switching to regular UIView animations (this made our SKNodes disappear)
Making sure the transition method doesn't get called more than once
Explicitly setting pausesOutgoingScene to YES on the SKTransition
None of the above attempts fixed the issue. Note that everything transitions properly in iOS 8
Here is some sample code that doesn't work:
-(BOOL)presentTitle {
if (!titleSceneLoaded) {
return NO;
}
[skView presentScene:titleScene transition:[SKTransition fadeWithDuration:1.0]];
return YES;
}
Changing it to this makes it work again (without the transitions):
-(BOOL)presentTitle {
if (!titleSceneLoaded) {
return NO;
}
[skView presentScene:titleScene];
return YES;
}
Any suggestions on how to locate/fix/workaround the bug?
There is a bug in iOS9 with presenting a scene with a transition in a subview.
I had the same issue and in my controller I was able to fix the issue by changing the following code in viewWillLayoutSubviews from this:
[self.subview presentScene:self.scene];
[self.view addSubview:self.subview];
to this:
[((SKView *)self.view) presentScene:self.scene];
Another workaround if the scene has to remain in a subview approach is to use presentScene without the transition, which does work.
We were unable to get SKTransition to work. We talked to the developer support team, posted on the developer forums, and tried everything posted here.
It is SpriteKit bug exclusive to iOS 9. If anyone else runs into this bug on iOS 9, we switched to custom transitions utilizing UIView animateWithDuration:animations:completion:.
It is a bit late but my own wrestling with what seems to be this bug may be worth exploring.
As I found out, I had a UIViewController floating around without having being fully deallocated, which had an SKView on it.
The only side-effect from that SKView still being somewhat live was the presentScene(transition) not working, as you have experienced.
It will work if you drag and drop the SKView in the storyboard instead of creating it manually.
Then in the ViewController, create an outlet:
#IBOutlet var skView: SKView!
Then present the scene as:
homeScene = HomeScene(size: self.view.frame.size)
homeScene.scaleMode = .aspectFill
homeScene.backgroundColor = .clear
skView.presentScene(homeScene)
And from this SKScene, you can simply use the navigation with the transition as:
let gameScene = GameScene(size: self.size)
self.view?.presentScene(gameScene, transition: SKTransition.doorsOpenHorizontal(withDuration: 1))

SKShapeNode Circle not appearing circular

I am working on an app where there should be a red circle on the screen. At first when I tested with only one scene, the circle appeared circular just fine. However, I have added in another scene and now the circle appears like this:
The circle appears to be much longer in the x direction.
Here is the code I use to make the circle (All of this in an skscene):
let ball = Ball(circleOfRadius: 500)//ball is a subclass of SKShapeNode
ball.setScale((self.frame.height / 6) / 1000)
ball.position = CGPointMake(self.frame.width / 4, self.frame.height / 2)
ball.fillColor = UIColor.redColor()
ball.strokeColor = UIColor.clearColor()
self.addChild(ball)
Here is the code I use to transition to the scene with the ball (A different skscene):
func transitionToGame(){
let reveal = SKTransition.crossFadeWithDuration(1.0)
let gameOverScene = GameScene(size: self.size)
self.view?.presentScene(gameOverScene, transition: reveal)
}
Any ideas on why the ball would no longer appear as a circle? I a worried this is also going to throw off other nodes I attempt to add, and possible means the coordinate system is incorrect. I am happy to hear any ideas. Thank you!
"All of this in an skview" are you sure you aren't doing that in a SKScene?
If the ball is indeed getting added directly to the scene I would look at the scene.scaleMode in your View Controller. Most of the time people set it to scene.scaleMode = .AspectFill but when you create a new scene you aren't setting the scaleMode. This might be as easy as making sure both scenes have the same scaleMode.
I am guessing this will fix your issue.
func transitionToGame(){
let reveal = SKTransition.crossFadeWithDuration(1.0)
let gameOverScene = GameScene(size: self.size)
gameOverScene.scaleMode = scene.scaleMode
self.view?.presentScene(gameOverScene, transition: reveal)
}
Hopefully that helps.

SKView Flashing Gray before Scene

I have a main view of type UIView. This view has two skview's (left and right) in landscape only orientation. All views and scenes are set with a black background.
I start out with the two skviews hidden. The main screen shows questions on either side of the screen. If the answer is incorrect I set a scene to an effect and then enable that effect and unhide the skview (left or right). The effect is shown for several seconds after which I disable it with [self.leftView presentScene:nil]; I do the same with the rightView. I then hide the views again.
The issue is that when the scenes are shown there is a brief flash of the view in a white color before the scene is rendered appropriately with the black background. In fact this is why I took to hide the skviews to begin with as if I don't they will show with a lighter background despite my setting it to black.
How can I show these scenes without the flash of a lighter background color?
Some representative code:
self.fireSceneLeft = [FireScene sceneWithSize:self.leftView.bounds.size];
self.fireSceneLeft.scaleMode = SKSceneScaleModeAspectFill;
self.fireSceneLeft.backgroundColor = [UIColor blackColor];
[self.leftView presentScene:self.fireSceneLeft];
[self.leftView setHidden:NO];
As for the scene effect itself:
#implementation FireScene
-(SKEmitterNode *)fireEffect
{
SKEmitterNode *emitter = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"FireEffect" ofType:#"sks"]];
emitter.position = CGPointMake(150,80);
emitter.name = #"fire";
emitter.targetNode = self.scene;
emitter.numParticlesToEmit = 2;
return emitter;
}
-(void)update:(CFTimeInterval)currentTime {
[self addChild:[self fireEffect]];
}
#end
I solved this issue by creating another scene that is simply black. Instead of setting the scene I want to move away from to nil, I simply substitute in the black scene. A scene with nothing on it (blank SKView) has a gray color which is the cause of my problem. By starting the app with the black scene and using it when no scene should display, my problem was solved. An important point here as well is pausing the scene. Even a black scene with no movement will use up CPU if not paused. I found the best place to pause the black scene was in the following method of the scene controller:
-(void)update:(CFTimeInterval)currentTime {
[self addChild:[self blackEffect]];
[self.view setPaused:YES];
}
This was also tricky because pausing the scene elsewhere actually paused the older scene. This is a timing problem because even if you call a new scene and then pause, it won't have had the time to get the new scene in place before the pause. The method above was the only place I could reliably call the pause and have it take effect on the correct scene.
On iOS you can't have 2 (or more) SKView instances at the same time. While it works technically and on first sight it is actually unusable because one view will dominate the other(s), leaving very little time for the other view(s) to update and render their contents as well as receiving touch and other events. I suspect the brief flash is related to this.

Resources