Batch render sprites with Spritekit - ios

I'm learning ios game programming and I need to create my own tile background - we're migrating an infinite-map style game from java.
I have the following code rendering our background tile graphics inside a loop:
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"shallow_1.png"];
sprite.position = CGPointMake(x,y);
[self addChild:sprite];
However, I'm trying to find out how to properly batch render in spritekit. In java/opengl we'd apply the texture, add all the render positions, and then render everything at once - saving performance.
How can I properly batch render these tiles using spritekit? I can't find any information.

If you're planning to work with tilemaps, your best bet is KoboldKit.
It's a game engine for SpriteKit, and adds several features to the framework.
In includes support for time maps (Tiled Map Editor).

Load your texture once:
SKTexture *texture = [SKTexture textureWithImageNamed:#"shallow_1.png"];
Then, make all of your SKSpriteNode objects referencing that texture:
// inside loop
SKSpriteNode *node = [SKSpriteNode spriteNodeWithTexture:texture];

Related

IOS SpriteKit Blurred Sprite with TexturePacker

I'm doing a project with IOS Sprite kit (Objective-C), I use TexturePacker to make my Sheet/Atlas ,In my scene view I only display a Character animation nothing more but I have an issue when I display my Sprite it is blurry.
The resolution of the image sprite are 512*512 (I think it's good), and the size of Sheets are 2048*2048.
I try many things on texturePacker to increase the quality but noting work, when I display my animation on TexturePacker directly the quality is good, but when I try on XCode, the result are blurry, I have try to play my character animation in a array animation and the result are not blurry (but this technique use to much memory) so I think the problem is TexturePacker
Has anyone had the same problem is would have a solution ?
Here is my code :
[self.scene setBackgroundColor:[UIColor clearColor]];
self.view.allowsTransparency = YES;
// load the atlas explicitly, to avoid frame rate drop when starting a new animation
self.atlas = [SKTextureAtlas atlasNamed:CHARACTER_ATLAS_NAME];
SKAction *walk = [SKAction animateWithTextures:CHARACTER_ANIM_IDLE timePerFrame:0.033];
self.sequence = [SKAction repeatActionForever:walk];
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:CHARACTER_TEX_HELLO_0];
sprite.size = CGSizeMake(self.view.frame.size.width, self.view.frame.size.height);
sprite.position = CGPointMake(sprite.size.width/2, sprite.size.height/2);
[sprite runAction:sequence];
[self addChild:sprite];
Here is a comparison on the top sprite when I use the image array and that below with SpriteKit and Texturepacker ( you can see the difference especially at eye level )
Thanks for your help

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.

iOS Sprite Kit- SKSpriteNode seems to participate in simulation without physics body

I am working on an iOS game using Sprite Kit.
I recently added an SKSpriteNode into my scene and did NOT create a physics body for it. However, when I build and run, when the player's character(which does have a physics body) moves into the SKSpriteNode, it spins and moves away, like it has a physics body -but I didn't create one for the SKSpriteNode.
Even if I type
sprite.physicsBody = nil;
it still behaves like it's part of the simulation.
I wondered if a physics body is created automatically when you make an SKSpriteNode, but I looked at the documentation and searched it on Google and couldn't find anything to suggest that is the case.
Any help or suggestions would be much appreciated.
This is the code I used to create the sprite (the one that should not be affected by the simulation)
-(void)addSprite
{
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"image"];
sprite.position = CGPointMake(40, 30);
sprite.zPosition = 30;
sprite.name = #"spriteName";
[self addChild:sprite];
}
In another part of the project I have
[self addSprite];
to call the method.
Yes every SKSpriteNode comes with a physics body. Its best to change the physics body's properties instead of getting rid of it altogether. If you want your sprite to be on screen and not interact with any other sprite nodes, do the following:
sprite.physicsBody.dynamic = NO;
sprite.physicsBody.collisionBitMask = 0x0;
sprite.physicsBody.contactTestBitMask = 0x0;

SpriteKit conveyor belt

I am trying to create a conveyor belt effect using SpriteKit like so
MY first reflex would be to create a conveyor belt image bigger than the screen and then move it repeatedly forever with actions. But this does not seem ok because it is dependent on the screen size.
Is there any better way to do this ?
Also obviously I want to put things (which would move independently) on the conveyor belt so the node is an SKNode with a the child sprite node that is moving.
Update : I would like the conveyor belt to move "visually"; so the lines move in a direction giving the impression of movement.
Apply physicsBody to all those sprites which you need to move on the conveyor belt and set the affectedByGravity property as NO.
In this example, I am assuming that the spriteNode representing your conveyor belt is called conveyor. Also, all the sprite nodes which need to be moved are have the string "moveable" as their name property.
Then, in your -update: method,
-(void)update:(CFTimeInterval)currentTime
{
[self enumerateChildNodesWithName:#"moveable" usingBlock:^(SKNode *node, BOOL *stop{
if ([node intersectsNode:conveyor])
{
[node.physicsBody applyForce:CGVectorMake(-1, 0)];
//edit the vector to get the right force. This will be too fast.
}
}];
}
After this, just add the desired sprites on the correct positions and you will see them moving by themselves.
For the animation, it would be better to use an array of textures which you can loop on the sprite.
Alternatively, you can add and remove a series of small sprites with a sectional image and move them like you do the sprites which are travelling on the conveyor.
#akashg has pointed out a solution for moving objects across the conveyor belt, I am giving my answer as how to make the conveyor belt look as if it is moving
One suggestion and my initial intuition was to place a larger rectangle than the screen on the scene and move this repeatedly. Upon reflecting I think this is not a nice solution because if we would want to place a conveyor belt on the middle, in a way we see both it ends this would not be possible without an extra clipping mask.
The ideal solution would be to tile the SKTexture on the SKSpriteNode and just offset this texture; but this does not seem to be possible with Sprite Kit (no tile mechanisms).
So basically what I'm doing is creating subtextures from a texture that is like so [tile][tile](2 times a repeatable tile) and I just show these subtextures one after the other to create an animation.
Here is the code :
- (SKSpriteNode *) newConveyor
{
SKTexture *conveyorTexture = [SKTexture textureWithImageNamed:#"testTextureDouble"];
SKTexture *halfConveyorTexture = [SKTexture textureWithRect:CGRectMake(0.5, 0.0, 0.5, 1.0) inTexture:conveyorTexture];
SKSpriteNode *conveyor = [SKSpriteNode spriteNodeWithTexture:halfConveyorTexture size:CGSizeMake(conveyorTexture.size.width/2, conveyorTexture.size.height)];
NSArray *textureArray = [self horizontalTextureArrayForTxture:conveyorTexture];
SKAction *moveAction = [SKAction animateWithTextures:textureArray timePerFrame:0.01 resize:NO restore:YES];
[conveyor runAction:[SKAction repeatActionForever:moveAction]];
return conveyor;
}
- (NSArray *) horizontalTextureArrayForTxture : (SKTexture *) texture
{
CGFloat deltaOnePixel = 1.0 / texture.size.width;
int countSubtextures = texture.size.width / 2;
NSMutableArray *textureArray = [[NSMutableArray alloc] initWithCapacity:countSubtextures];
CGFloat offset = 0;
for (int i = 0; i < countSubtextures; i++)
{
offset = i * deltaOnePixel;
SKTexture *subTexture = [SKTexture textureWithRect:CGRectMake(offset, 0.0, 0.5, 1.0) inTexture:texture];
[textureArray addObject:subTexture];
}
return [NSArray arrayWithArray:textureArray];
}
Now this is still not ideal because it is necessary to make an image with 2 tiles manually. We can also edit a SKTexture with a CIFilter transform that could potentially be used to create this texture with 2 tiles.
Apart from this I think this solution is better because it does not depend on the size of the screen and is memory efficient; but in order for it to be used on the whole screen I would have to create more SKSpriteNode objects that share the same moveAction that I have used, since tiling is not possible with Sprite Kit according to this source :
How do you set a texture to tile in Sprite Kit.
I will try to update the code to make it possible to tile by using multiple SKSpriteNode objects.

Changing SK Textures for "MouseOver" in Xcode 5.0

I have a sprite with a given texture using:
joyStickRight = [SKSpriteNode spriteNodeWithImageNamed:#"joyStick.png"];
And I'd like to change it when the user touches and holds the sprite. When I detect the touch, I try changing the sprite texture by calling the same function with a different image:
joyStickRight = [SKSpriteNode spriteNodeWithImageNamed:#"joyStick_rollOver.png"];
But this does not seem to work. Nothing changes.
This for an iPad application. I am creating the on screen elements with SKSpriteNodes.
This is likely to be related to variable scope. I'm guessing you are trying to modify another instance of 'joyStickRight' SKSPriteNode. This second instance would not have been added to the scene and therefore not taking any effect.
The only way i found out to change textures ist to swap them via action like this:
SKTexture *texture1 = [SKTexture textureWithImageNamed:#"texture1"];
SKTexture *texture2 = [SKTexture textureWithImageNamed:#"texture2"];
SKAction *swapTextures = [SKAction repeatAction:[SKAction animateWithTextures:#[texture1,texture2] timePerFrame:0.1] count:3];
[node (in your case joyStickRight) runAction:swapTextures];
I still hope this is not the only solution, and also wonder why it does not work when i run it once!!!

Resources