SKTransition not working in Swift - ios

I have a very simple SpriteKit game written in Swift with two scenes that I am trying to transition between. This code works perfectly:
let skView = self.view as SKView
let scene = GameScene(size: self.scene.size)
scene.size = skView.bounds.size
scene.scaleMode = .AspectFill
//This line shows the new scene immediately (as expected)
skView.presentScene(scene)
The trouble comes when I replace the above line of code with this:
let sceneTransition = SKTransition.doorsCloseHorizontalWithDuration(2.0)
skView.presentScene(scene, transition: sceneTransition)
When I do this nothing happens (the current scene remains on screen). I have tried several different transitions, but always with the same result.
Any thoughts?

SOLVED.
It turns out that there is nothing wrong with the code above. The problem was that I was trying to execute it in the update function based on a specific SKPhysicsBody's position. The transition doesn't happen instantaneously so it gave time for update to get called repeatedly which re-triggered the transition over and over again. This resulted in the transition never happening.
The fix was to add a bool to check if I have started the transition, so I only attempt it once.

If anyone else experiences this, you might also want to pause the outgoing scene. Some of the logic in it might be changing the state or interfering with the transition.
try setting
transition.pausesOutgoingScene = YES;
to see if that help,
either use that as a solution, if that's your intent, or set the breakpoints (try update: after the transition is created) and see what's up.

Related

SCNAnimationPlayer to start playing animation on gesture

I am using Scenekit/ ARKit for the code and Blender for my 3D models. I have a 3D model with some keyframe animations - created in Blender, which has been exported easily into XCode. Nothing complicated - it's just a cat moving a bit up and then down.
I am using animation controls inside my tapGestureRecognizer. As soon as my view loads I have set the animation to be in paused state :-
nonGeometryObjectNode?.childNode(withName: "Armature", recursively: true)?.animationPlayer(forKey: "cat_animated-1")?.paused = true
Code for animation controls inside my tap gesture is as below.
let animationPlayer = hitResults.first?.node.parent?.childNode(withName: "Armature", recursively: true)?.animationPlayer(forKey: "cat_animated-1")
animationPlayer?.animation.autoreverses = true
animationPlayer?.animation.repeatCount = 1
animationPlayer?.animation.duration = 0.8
animationPlayer?.animation.blendInDuration = 0.2
animationPlayer?.animation.blendOutDuration = 0.2
animationPlayer?.paused = true
if (animationPlayer?.paused)! {
animationPlayer?.play()
}
It works fine, the only problem is it works just one time when tap is performed for the very first time. I tried using .paused, .stop() inside my tapGesture code, but animationPlayer doesn't replay as it should every time I am tapping on it. There is no bool var in SCNAnimationPlayer or else which I can use to detect if the animation has played itself out, so that I can use .stop() and then .play() again.
I did see two instance vars animationDidStart and animationDidStop which I thought probably would be useful to manage what I need. But I am at loss of ways to use these. What would be really helpful is what should be used to play and stop/ pause my animation whenever I tap on the object. Any pointer would be helpful.
I know it's an old question but in case anyone stumbles across it in future, the default action is for the animation to be removed once complete. Therefore to be able to play it again you need to set:
animationPlayer?.animation.isRemovedOnCompletion = false

Confusion with node locations in SpriteKit on iOS

I'm still very new to Swift, Spritekit & iOS. I've been doing some experimenting and cant manage to work out why my objects are moving when I change scenes.
Here is a gfycat of what is happening.
https://gfycat.com/FlatAdeptFrenchbulldog
Im using the white box in either scene to switch back to the other. The white box is positioned in the same location in each scene, but after the first switch everything shifts.
My basic scene change code:
let transition = SKTransition.fade(withDuration: 1)
let nextScene = MenuScene(size: self.size)
nextScene.scaleMode = .aspectFill
scene?.view?.presentScene(nextScene, transition: transition)
I must be missing something pretty basic. Any help would be appreciated :)
Edit: Ive done a little more experimenting and noticed that CGPoint(x: 0, y: 0) in my default scene is the very middle of the screen, and this changes to the bottom left after the scene changes. Im guessing this explains what is happening, but Im not sure why it happens?

How to correctly pause the movements of sprites in a SpriteKit game?

I have a simple SpriteKit game, and in the appDelegate file in theapplicationWillResignActive(application: UIApplication)function, I put the code:GameScene().physicsWorld.speed = 0.0. However, when I close and reopen the app, a SKSpriteNode with a physicsBody attached carries on falling. I have also tried adding both these lines of code:
GameScene().speed = 0,
GameScene().paused = true. None of this works though. Any help would be appreciated.
You need to pause the SKView
self.scene?.view?.paused = true
Add an observer in your view controller, holding the view, for the willResignActive notification and then pause the SKView.
Add the observer for willEnterForeground to start it.

SpriteNode/Shapes change size and disappear after returning to GameScene (Swift SpriteKit)

GIF of the issue in action:
http://i.imgur.com/hiF5lyw.gifv
As you can see, the main Player is a SKSpriteNode, and when you hit a falling block which is a SKShapeNode, the game switches to the GameOver scene. When you click restart in the GameOver scene and move back to the GameScene the falling blocks disappear and the dimensions of the Player sprite is changed along with its y position.
All the code contains pretty detailed comments but if you have any questions about it feel free to ask.
Since the size of my Player sprite is based on the size of the screen, It might have something to do with screen size changes when switching scenes maybe. Also, its strange the falling block enemies dont show up for a few seconds, but then the node count drastically increases and another GameOver scene transition is triggered.
Maybe I have to change something with the GameViewController?
Any help would be greatly appreciated, I've been working at it for hours and I cant seem to figure out why its messing up.
Figured it out.
I created two new variables:
var transition:SKTransition = SKTransition.fadeWithDuration(0.5)
and
var gameOver:SKScene = GameOver(size: self.size)
And then implemented both when calling the scene switch with:
self.view?.presentScene(gameOver, transition: transition)
I think the size: self.size is what fixed it.
If you have troubles with changing size of your nodes when you do transitions, instead of size: self.size, you should use fileNamed: nameOfYourScene. This worked for me.

Not working transitions in SpriteKit

I have a scene that is calling the next one using a transition like this:
SKTransition *transition = [SKTransition revealWithDirection:SKTransitionDirectionDown duration:0.5];
SKView *skView = (SKView *)self.view;
SKScene * scene = [[GameOverScene alloc] initWithSize:self.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:scene transition:transition];
The elements that compose GameOverScene (buttons, images, etc.) are added on its init method.
The problem is that the transition is not seen. One scene immediately cuts to the other one.
I guess that transition happens before the next scene has a chance to build its elements.
I have tried to move the creation of the next scene to didMoveToView without success.
For test purposes I have tried to delay the presentScene line in times even bigger than 2 seconds. When I do that I barely see the end frames of the transition.
How do I do that? What is the correct way of building the next scene and doing a transition that works.
If the new scene is particular resource heavy, i.e., requires a lot of texture loading, that will delay any frame rendering. If the texture loading takes longer than your transition time, you will miss all of it because the first frame that will be displayed has been rendered after your transition is finished.
I ran into this as well, although I was better able to determine the root cause because I had a transition of 2 seconds of which only the last 0.5 seconds were shown.
How to fix this? Preload your textures. See Apple Docs.
+ (void)preloadTextures:(NSArray *)textures withCompletionHandler:(void (^)(void))completionHandler
I should emphasize the differences between SKSprite, SKTexture and your "image.png".
SKSprite draws itself based on its texture property (or background color), and size. You can use one SKTexture for many sprites. In turn, an image file ("image.png") can supply multiple SKTexture objects.
Important: you want to use textures from the array of SKTexture objects passed to the method above to actually benefit from the texture loading. This will require some form of texture management.
If your problem is indeed texture related, let me know if you need me to expand on the texture management. A related post (which may be a bit dry) can be found here: SO sktexture-preloading.
I wrote exactly the same code in my app to show the game over scene with one minor difference. My code is as follows:
- (void)showGameOverScene
{
SKTransition* reveal = [SKTransition doorsCloseVerticalWithDuration:0.5];
SKScene* gameOverScene = [[RSGameOverScene alloc] initWithSize:self.size];
[self.view presentScene:gameOverScene transition: reveal];
}
And it's been working like a charm. The only difference is that I am not calling:
scene.scaleMode = SKSceneScaleModeAspectFill;
In my RSGameOverScene I set up nodes in -(id)initWithSize:(CGSize)size, and again there is no problems. Everything just works.
I can't see a reason why this line could be a source of a problem but you might want to try commenting it out to see if it helps. However your code looks ok so the source of the problem can be somewhere else.

Resources