13

I have a sprite sheet with the animation images for my sprite. How would I make put these images in to a SKTextureAtlas (so I can animate in an SKAction) without splitting up the individual files? I have seen and done this before, but not using SpriteKit and/or SKTextures.

Is this even possible without splitting up the files? Worst case scenario: I would need to make the sheet separate images.

In the past I have just make a method with sprite size and number of total sprites which were separated the images in code then animated through.


Thanks in advance for any help!

P.S. I'm new to SpriteKit but not to game programming or iOS

Keely
  • 366
  • 1
  • 4
  • 19

2 Answers2

13

Thanks to @LearnCocos2D guy for pointing me toward using textureWithRect:inTexture: I made a custom init method within my custom Entity : SKSpriteNode class which will allow the use of a sprite sheet with all images on it. I tried to include some comments to explain the code. I have this version scan the x as my animation is horizontal. Y can be changed only with the method called. Not in the method with this version.

Make sure to put it in the interface!

Example when alloc-initing:

Entity *cookie = [[Entity alloc] initWithSpriteSheetNamed:@"cookie_sheet" withinNode:map sourceRect:CGRectMake(0, 0, 32, 32) andNumberOfSprites:6];

The method in the Entity : SKSpriteNode

- (id) initWithSpriteSheetNamed: (NSString *) spriteSheet withinNode: (SKSpriteNode *) scene sourceRect: (CGRect) source andNumberOfSprites: (int) numberOfSprites {

    // @param numberOfSprites - the number of sprite images to the left
    // @param scene - I add my sprite to a map node. Change it to a SKScene
    // if [self addChild:] is used.

    NSMutableArray *mAnimatingFrames = [NSMutableArray array];

    SKTexture  *ssTexture = [SKTexture textureWithImageNamed:spriteSheet];
    // Makes the sprite (ssTexture) stay pixelated:
    ssTexture.filteringMode = SKTextureFilteringNearest;

    float sx = source.origin.x;
    float sy = source.origin.y;
    float sWidth = source.size.width;
    float sHeight = source.size.height;

    // IMPORTANT: textureWithRect: uses 1 as 100% of the sprite.
    // This is why division from the original sprite is necessary.
    // Also why sx is incremented by a fraction.

    for (int i = 0; i < numberOfSprites; i++) {
        CGRect cutter = CGRectMake(sx, sy/ssTexture.size.width, sWidth/ssTexture.size.width, sHeight/ssTexture.size.height);
        SKTexture *temp = [SKTexture textureWithRect:cutter inTexture:ssTexture];
        [mAnimatingFrames addObject:temp];
        sx+=sWidth/ssTexture.size.width;
    }

    self = [Entity spriteNodeWithTexture:mAnimatingFrames[0]];

    animatingFrames = mAnimatingFrames;

    [scene addChild:self];

    return self;
}
Keely
  • 366
  • 1
  • 4
  • 19
  • Cool, I'll try out this code. However, I was mainly trying to avoid having to chop up some spritesheets I have manually. Shoebox solved my problem as I now can make a texture atlas with the extracted sprites. Is textureWithRect pretty quick ? I believe that TextureAtlas also optimizes the drawing as it's pulling everything from the same texture. So seems preferable to me. – prototypical Nov 29 '13 at 02:07
  • I am not sure on the speed, but as far as I know, all a .atlas does is put the multiple images together its self. I believe all images still need to be stored in an array to use later as the [SKAction animateWithTextures:] takes an array. I may also use that program in the future. The good news is textureWithRect only needs to occur once per launch. But if it's written by Apple, it probably works better. – Keely Nov 29 '13 at 02:17
  • 1
    In the reference materials it says "Sprite Kit uses texture atlases to improve rendering performance." Also - "If each texture is treated as a separate object, then Sprite Kit and the graphics hardware must work harder to render scenes—and your game’s performance might suffer. Specifically, Sprite Kit must make at least one drawing pass per texture. " – prototypical Nov 29 '13 at 02:30
  • So being that the texture atlas is only 1 texture and it's likely just storing the location and dimensions of a given frame on that texture, it could be significantly faster, especially if you have a large amount of textures. The ShoeBox solution is a pretty minimal one time extraction and it allows me to get the rendering optimization of a single texture. – prototypical Nov 29 '13 at 02:38
  • 1
    I was also going throughout that doc. See Listing 2-8. The atlas makes the one image to store in memory itself. Then looks through it and stores each textures in the atlas into an array. (For later use). Right after that: "If you already have an SKTexture object, you can create new textures that reference a portion of it. This approach is efficient because the new texture objects reference the same texture data in memory. (This behavior is similar to that of the texture atlas.)" Really though, to me it comes down to the personal workflow of the artists and programers. – Keely Nov 29 '13 at 13:34
  • That's good to know. So with textureWithRect, it's potentially the same as having the texture atlas. And, yeah, I agree it does come down to the personal workflow. I have a number of assets in spritesheet form, and if they were potentially being updated regularly during development by someone I'd likely go the route you have chosen to remove the step of extracting those frames each time. – prototypical Nov 29 '13 at 17:42
  • One downside with this is that unless your number of rows and columns are powers of 2, the fractions are not exact, and consequently it will not correspond to exact integer pixel positions. – user102008 Jun 05 '15 at 02:45
5

You need to have each animation frame as a separate image. Then you can use SKAction to animate the images, optionally using SKTextureAtlas.

So yes, you have to split your existing sheet unless you write a custom animation handler that changes the texture rect of the animated sprite manually. You can create a smaller texture from the spritesheet texture by using textureWithRect:inTexture:.

CodeSmile
  • 64,284
  • 20
  • 132
  • 217
  • Thanks, just what I needed to know. – Keely Nov 28 '13 at 17:46
  • @Keely if you create a class for this purpose, I'd be very much interested in it. I have that on my list to solve in the next week. Was thinking there might be a solution out there already. I have a few spritesheets that I'd like to utilize without having to break them apart. Although there might be a tool for that too! heading to google... – prototypical Nov 28 '13 at 18:30
  • 1
    http://renderhjs.net/shoebox/extractSprites.htm - pretty cool, if you need to go from spritesheet to individual images. – prototypical Nov 28 '13 at 18:39
  • @prototypical Cool link. But I decided to make the class before. Anyway, it works good. I think the code is relatively clean. I am open to any suggestions. (Is the new 'accepted answer') – Keely Nov 29 '13 at 01:51