How to preload textures in Sprite Kit? - ios

I have a few frames of animations on my node, and the first time I play the animation it lags, fps drops. Each next time is fine and dandy.
How do I preload the textures to make it work smooth?
I have this method to run when I load the game:
- (void)load
{
self.animationFrames = #[[SKTexture textureWithImageNamed:#"exp1"], [SKTexture textureWithImageNamed:#"exp2"],
[SKTexture textureWithImageNamed:#"exp3"], [SKTexture textureWithImageNamed:#"exp4"], [SKTexture textureWithImageNamed:#"exp5"], [SKTexture textureWithImageNamed:#"exp6"], [SKTexture textureWithImageNamed:#"exp7"], [SKTexture textureWithImageNamed:#"exp8"], [SKTexture textureWithImageNamed:#"exp9"]];
}
And this method to play animation:
-(void)playExplosionAnimation
{
self.size = CGSizeMake(250, 250);
SKAction *animation = [SKAction animateWithTextures:self.animationFrames timePerFrame:0.1];
[self runAction:animation completion:^{
self.hidden = YES;
}];
}

You should create a texture atlas and use SKTextureAtlas methods:
– preloadWithCompletionHandler:
+ preloadTextureAtlases:withCompletionHandler:
Here is what documentation says about this:
Sprite Kit creates a background task that loads the texture data from
the atlas. Then, Sprite Kit returns control to your game. After the
texture atlas is loaded, your completion handler is called.
If you need to preload multiple texture atlases at once, use the
preloadTextureAtlases:withCompletionHandler: method instead.

Related

Sprite Kit animateWithTextures lags

I'm using texture atlases in my Sprite Kit game. I'm creating SKTextureAtlas object and store it's textures in array for each animation. So when I need some animation on my hero I call animateWithTextures sending it the corresponding array. There are some lags when I start animations. Is there some way to start animation smoothly?
I am sure there are few ways to get around this. What you need to do is to preload an atlases before your gameplay actually start. Just show a loading screen at the beginning of the game and preload your atlases.
You may try with + preloadTextureAtlases:withCompletionHandler:
[SKTextureAtlas preloadTextureAtlases:textureAtlasesArray withCompletionHandler:^{ /*Game Start*/}];
Another way to implement resource loading before everything else (and keep everything in memory) is described here in Adventure game example
For more details about loading assets asynchronously take a peek into code which can be downloaded from the link above.
had the same problem and I solved it in my game by not using atlases. So try this example:
-(void)makePlayerAnimation:(SKSpriteNode *)player
{
SKTexture *texture1 = [SKTexture textureWithImageNamed:#"texture1.png"];
SKTexture *texture2 = [SKTexture textureWithImageNamed:#"texture2.png"];
SKTexture *texture3 = [SKTexture textureWithImageNamed:#"texture3.png"];
SKAction *animationTextures = [SKAction animateWithTextures:#[texture1, texture2, texture3] timePerFrame:0.1];
[player runAction:animationTextures];
}
When you wish to activate animation do this:
[self makePlayerAnimation:myNode];
or
[self makePlayerAnimation:self.myNode];
Just depends how you declared it.
If you need to run animation forever, you can just add line at the end of previous method:
SKAction *repeat = [SKAction repeatActionForever: animationTextures];
Hope this helps.

SpriteKit scaleTo action scaling issue

I'm trying to make a node scale down to a small size, move down, and then once all that happens animate it repeatedly (at the same scaled down size with a different texture).
Here is my method that scales the node down and moves it. That part is working correctly. However once it finishes doing that I need to change the sprites texture and animate it.
- (void)shrinkAndMoveToPosition:(CGPoint)position {
SKAction *move = [SKAction moveTo:position duration:.5];
SKAction *scale = [SKAction scaleTo:.3 duration:.5];
SKAction *moveAndScale = [SKAction group:#[move, scale]];
[self runAction:moveAndScale completion:^{
NSArray *textures = #[[SKTexture textureWithImageNamed:#"ship-small_01"],
[SKTexture textureWithImageNamed:#"ship-small_02"],
[SKTexture textureWithImageNamed:#"ship-small_03"],
[SKTexture textureWithImageNamed:#"ship-small_04"]];
SKAction *animate = [SKAction animateWithTextures:textures timePerFrame:0.5];
[self runAction:[SKAction repeatActionForever:animate]];
}];
}
The problem is that whenever my completion block runs the sprite jumps back up to the size of the texture. How can I maintain my scaled down size?
I just needed to call a different animate method and pass in YES to the resize parameter:
SKAction *animate = [SKAction animateWithTextures:textures timePerFrame:0.5 resize:YES restore:NO];

Fade between two different SKTextures on SKSpriteNode

Does anyone know if there is a way to fade (over time) between two different SKTextures on an SKSpriteNode. I am assuming that you can't do this directly and plan to use a duplicate child sprite with a higher ZPosition to realise the fade, but I just wanted to check that there was not some method using SKAction(s) that I had over looked.
The following code should address this issue assuming the new texture fits overtop of the old one (it doesn't fade out the previous texture, but simply fades in the new one on top). I've left out minor implementation details such as timing mode.
-(void) fadeTexture:(SKTexture *)newTexture ontoSpriteNode:(SKSpriteNode *)referenceSpriteNode withDuration:(CFTimeInterval)duration {
SKSpriteNode * fadeInSprite = [self fadeInSpriteWithTexture:newTexture referenceSpriteNode:referenceSpriteNode];
[[referenceSpriteNode parent] addChild:fadeInSprite];
[fadeInSprite runAction:[SKAction sequence:#[
[SKAction fadeAlphaTo:1 duration:duration],
[SKAction runBlock:^{
[fadeInSprite removeFromParent];
[referenceSpriteNode setTexture:newTexture];
}]
]]];
}
-(SKSpriteNode *) fadeInSpriteWithTexture:(SKTexture *)newTexture referenceSpriteNode:(SKSpriteNode *)referenceSpriteNode {
SKSpriteNode * fadeInSprite = [SKSpriteNode spriteNodeWithTexture:newTexture size:[referenceSpriteNode size]];
[fadeInSprite setAlpha:0];
[fadeInSprite setAnchorPoint:[referenceSpriteNode anchorPoint]];
[fadeInSprite setPosition:[referenceSpriteNode position]];
return fadeInSprite;
}

How to change a characters image in 'touchbegan' in spritekit?

I'm currently using Sprite Kit and Xcode to design a game. My character is usually in the state of running which consists of two images - code below:
bobSKTexture* Texture1 = [SKTexture textureWithImageNamed:#"bob1"];
bobTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture* bobTexture2 = [SKTexture textureWithImageNamed:#"bob2"];
bobTexture2.filteringMode = SKTextureFilteringNearest;
SKAction* run = [SKAction repeatActionForever:[SKAction animateWithTextures:#[birdTexture1, birdTexture2] timePerFrame:0.2]];
_bob = [SKSpriteNode spriteNodeWithTexture:birdTexture1];
[_bob setScale:2.0];
_bob.position = CGPointMake(self.frame.size.width / 4, CGRectGetMidY(self.frame));
[_bob runAction:run];
I want the image to change when a touch to the screen happens, only for a short amount of time and then I want it to return to the above code. How can I accomplish this?
As Jānis K said, it would be even easier to do like this when you want the image to change:
[_bob removeAllActions];
_bob.texture = [SKTexture textureWithImageNamed:NEW_TEXTURE];
[self performSelector:#selector(resetAnimation) withObject:nil afterDelay:2.0f];
NEW_TEXTURE is the name of the texture/image you want it to change to.
self would be the scene or wherever you call the code to make _bob
This is the resetAnimation method, defined in your scene or wherever you call the code to make _bob:
- (void)resetAnimation
{
SKAction* run = [SKAction repeatActionForever:[SKAction animateWithTextures:#[birdTexture1, birdTexture2] timePerFrame:0.2]];
[_bob runAction:run];
}

Set size for SKTexture

How do I set a size for a SKTexture Ive gone through the documentation Class Reference I don't see anything about being able to set the size. I know the size method is a return method but just to make it clear what I'm trying to do its in my code below.
_bomb = [SKSpriteNode spriteNodeWithImageNamed:#"Bomb5.gif"];
SKTexture *Bomb5 = [SKTexture textureWithImageNamed:#"Bomb5.gif"];
Bomb5.size = CGSizeMake(40, 40);
SKTexture *Bomb4 = [SKTexture textureWithImageNamed:#"Bomb4.gif"];
Bomb4.size = CGSizeMake(40, 40);
SKTexture *Bomb3 = [SKTexture textureWithImageNamed:#"Bomb3.gif"];
Bomb3.size = CGSizeMake(40, 40);
SKTexture *Bomb2 = [SKTexture textureWithImageNamed:#"Bomb2.gif"];
Bomb2.size = CGSizeMake(40, 40);
SKTexture *Bomb1 = [SKTexture textureWithImageNamed:#"Bomb1.gif"];
Bomb1.size = CGSizeMake(40, 40);
SKTexture *explostion = [SKTexture textureWithImageNamed:#"explosionnn.gif"];
explostion.size = CGSizeMake(90, 90);
//5 second countdown and the bomb explodes
countdown = [SKAction animateWithTextures:#[Bomb5,Bomb4, Bomb3, Bomb2, Bomb1, explostion] timePerFrame:1];
Another solution?: Maybe I could add actions in sequence where after the 5 second countdown I can change the size of the spriteNode instead when it reaches the last animation image. But if I were to do it this way how do I change the size of the image from the centre origin of where the bomb is?
You are right: You cannot change the size of the texture, as the texture essentially is the image: "An SKTexture object is an image that can be applied to SKSpriteNode objects or particles created by a SKEmitterNode object." (from the documentation).
Have you considered having a separate sprite for the explosion? Then you can simply replace the countdown-sprite with this when the countdown has reached zero. If you put a factory-class to create your sprites this will also save you a lot of hassle if you want to use the same explosion elsewhere in your game...

Resources