3

I'm developing an os x / iOS crossword game which uses SKLabelNode to display the crossword letters as a child on a SKNode subclass.

So for each letter there is SKNode.SKLabelNode. Without using SKLabelNode the draw counts are in the range of 6-8. With using SKLabelNode they go up to the amount of children in the scene which can be almost 100.

I am now looking for a way to avoid that and came up with the idea to rasterize the SKLabelNode to a texture but this does not lower the draw counts since there are still lots of different textures.

My Idea is now to rasterize these SKNode-Subclasses and to put the textures into a texture atlas.

So question is, is it possible to create a texture atlas in runtime? What to do if a single texture changes? Is it possible to exchange a single texture in the atlas or so I have to rebuild it?

And maybe there is a "best way" to handle lot's of different SKLabelNodes!?

Simon Kemper
  • 635
  • 1
  • 7
  • 16
  • Is your app suffering performance issues due to a low FPS? – sangony Jun 25 '15 at 11:57
  • No. Absolutely not. But with **SKLabelNode** I have a node count of about **300 nodes** and a **draw count of 100**. I have now created textures for every possible case and letter and created a texture atlas which results in **80 Nodes** and less than **7 draws** ... Which I think is better, right?! – Simon Kemper Jun 25 '15 at 12:15
  • @SimonKemper you don't have to create an atlas in runtime just create it as usual. I just searched and here is the method which can be used to create texture atlas at runtime (if you really need that): https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKTextureAtlas/#//apple_ref/occ/clm/SKTextureAtlas/atlasWithDictionary: I agree with Sangony that premature optimization is what you should avoid, but using SKLabelNodes like this is not performant solution, and really unnecessary IMO. – Whirlwind Jun 25 '15 at 13:05

2 Answers2

0

I would go with a letter class which is subclass of SKSpriteNode and an atlas called letters. That way, you will draw all letters in single draw pass(100 draw passes are unnecessary in this situation). Or you don't even have to make a subclass of SKSpriteNode...You can just do this :

 SKSpriteNode *letterSprite = [SKSpriteNode spriteNodeWithTexture:[atlas textureNamed:[NSString stringWithFormat:@"%@",character]]];

The limitation of this approach is that letters would have pre-determined size. I doubt that you would need different sized letters in your crossword game. If you need to change size of letters you can still scale them, but I guess some quality loss will theoretically occur because of scaling bitmaps. I say theoretically because in most cases the quality loss is not noticeable.

Here is an example of my TextNode which parses given string (numbers in my case) and create sprites which are drawn in single pass (instead of using SKLabelNode for every single number). Images in atlases should be named like a@2x.png, b@2x.png, or if using numbers 1@2x.png, 2@2x.png etc.

static const float kCharacterDistance = 6.0f;

#import "TextNode.h"

@interface TextNode()


@property (nonatomic,strong) NSMutableArray* characters;

@end

@implementation TextNode

-(instancetype)initWithPosition:(CGPoint)position andText:(NSString*)text{

    if(self =[super init]){

        self.characters = [[NSMutableArray alloc] initWithCapacity:[text length]];

        SKTextureAtlas *atlas = [SKTextureAtlas atlasNamed:@"numbers"];



        for(NSUInteger i =0; i < [text length];i++){

            NSString *character = [text substringWithRange:NSMakeRange(i, 1)];

            SKSpriteNode *characterSprite = [SKSpriteNode spriteNodeWithTexture:[atlas textureNamed:[NSString stringWithFormat:@"%@",character]]];
            characterSprite.color = [SKColor yellowColor];
            characterSprite.colorBlendFactor = 1.0f;
            characterSprite.position = CGPointMake(i*kCharacterDistance,0);

            [self.characters addObject:characterSprite];

            [self addChild:characterSprite];
        }
   self.position = position;
    }

    return self;
}

@end

Hope this helps and will give you the basic idea of how to draw all letters in single draw pass. And note how I colorize letters. In texture atlas images are white, but I colorize them easily to desired color.

Whirlwind
  • 14,286
  • 11
  • 68
  • 157
  • That's something I have already done using **SSBitmapLabelNode** - a third-party **SKNode**-Subclass. This However reduces my draw count to something between 20-25. Problem is my node tree. I have a Subclass-**SKNode** which has the Label as a child. I am performing operations like Drag'N'Drop, Merging and Grouping of the **SKNode**-Subclasses - It's more of a puzzle than a crossword. Problem is that this subclassing interrupts the batch-drawing process so I always end up with high draw counts so i decided to merge every letter + background into single textures which gives me a draw count < 7 – Simon Kemper Jun 25 '15 at 12:32
  • To get back to my question. I tried this using a `+(void)load` method and to merge everything into a **texture atlas**. Problem is, that there is no way to rasterize a **SKTexture** into an UI- or NSImage which would be necessary for creating an atlas in runtime ... – Simon Kemper Jun 25 '15 at 12:38
  • @SimonKemper I just updated my comment on your question about "creating texture atlas at runtime", maybe that link will help a bit, but still I am not sure if that is really needed. I understand what you are saying about interrupting batch process, but 20-25 draws is still reasonable for this situation. Especially if you are not experiencing performance problems. – Whirlwind Jun 25 '15 at 12:47
  • Ok, thanks! I found a solution which lowers my Draw Count to **6** which is even better, isn't it? I want to have it as low as possible because when playing (not in debug mode) my MacBooks Fans speed up due to high GPU load I guess (cannot monitor GPU load). CPU is at 7%. I would like to lower FPS from a constant **60** to **30**. This would be enough for my game but `skView.frameInterval = 2` does not take effect at all. Read through all docs and questions but it won't affect the FPS. – Simon Kemper Jun 25 '15 at 13:12
  • @SimonKemper Glad you solved this one, and yes, 6 draws are even better :). What was the solution you used to achieve that ? About skView.frameInterval...That's another thing, and you should open a new question with relevant code (from view controller, or even from update: method). – Whirlwind Jun 25 '15 at 14:16
  • I simply combine Background + Letter + FX into one single texture and made approx. 300 textures which are now all inside a single atlas. Working fine. Lot's of work but a draw count of 6 :-). Concerning the frameInterval-Issue I've already created a new question which can be found here: [link](http://stackoverflow.com/questions/31052958/why-does-setting-frameinterval-on-a-skview-not-work) – Simon Kemper Jun 25 '15 at 14:19
0

As long as your FPS is good, don't worry about anything else. Also, remember that Xcode auto creates texture atlases at runtime so it's not something you have to do yourself.

You can set skView.ignoresSiblingOrder = YES; to improve your performance a bit.

sangony
  • 11,636
  • 4
  • 39
  • 55