SpriteKit, where to load texture atlases for thousands of sprites

In my game, I have thousands of tile nodes that make up the map of the game (I think something like simcity), I wonder what would be the most efficient frame / memory for texturing and animating each node? There are several unique “types” of tiles, each of which has its own atlas / texture animation, so when reused, the textures become key.

All my tile nodes are children of the same map node, if the map node processes tile type recognition and loads the necessary atlases and animations (for example, by loading texture and satin names from plist?)

Alternatively, each type of tile is a specific subclass. It would be better if each SKSpriteNode tile processed its own sprite atlas, for example. [tileInstance texturise]; (how to deal with this set of sprites, this method will lead to the fact that the same texture atlas will be loaded into memory for each instance of a certain type of tile?)

I searched for documents for a deeper explanation of atlases and texture reuse, but I don't know what the typical procedure is for such a scenario. Any help would be appreciated, thanks.

+7
iphone cocoa ios7 sprite-kit
source share
1 answer

Memory : there will be no noticeable difference. You must load the texture of the tile, the texture will comprise at least 99% of the Map + Tiles memory and what it is.

Texture reuse: textures are reused / cached automatically. Two sprites that use the same texture will refer to the same texture, not to each copy of their texture.

Framerate / Batching: This is all about batch processing. Sprite Kit approaches node children by rendering them in the order in which they are added to the child array. As long as the next child node uses the same texture as the previous one, they will all be combined into a single call to call. Perhaps the worst thing you could do is add a sprite, shortcut, sprite, shortcut and so on. You will want to add as many sprites using the same texture in sequential order as possible.

Using Atlas : here where you can win the most. Typically, developers try to classify their atlases, which is the wrong way. Instead of creating one atlas for each fragment (and its animation), you want to create as few texture atlases as possible, each of which contains as many fragments as possible. On all iOS 7 devices, the texture atlas can be 2048x2048 and, with the exception of the iPhone 4 and iPad 1, all other devices can use textures up to 4096x4096 pixels in size.

There are exceptions to this rule, say, if you have such a large number of textures that you cannot load them all at once into memory on all devices. In this case, use your best solution to find a good compromise regarding memory usage and batch processing efficiency. For example, one solution could be to create one or two texture atlases for each unique scene or, rather, “scenery”, even if this means duplicating some fragments in other texture atlases for another scene. If you have tiles that almost always appear in any landscape, it makes sense to put them in a “general” atlas.

Regarding subclasses , I am a strong proponent to avoid subclassing node classes. Especially if the main reason for their subclass is simply a change in the texture that they use / animate. The sprite is already a texture container, so you can also change the texture of the sprite and animate it from the outside.

To add data or additional code to a node, you can examine its userData property by creating your own NSMutableDictionary and adding any object to it. A typical component approach would look like this:

 SKSpriteNode* sprite = [SKSpriteNode spriteWithWhatever..]; [self addChild:sprite]; // create the controller object sprite.userData = [NSMutableDictionary dictionary]; MyTileController* controller = [MyTileController controllerWithSprite:sprite]; [sprite.userData setObject: forKey:@"controller"]; 

This controller object then executes any custom code needed for your snippets. It can be an animation of tiles and everything else. The only important bit is to make a link to the node-owning (here: sprite) weak link:

 @interface MySpriteController @property (weak) sprite; // weak is important to avoid retain cycle! @end 

Because the sprite saves the dictionary. The controller is saved in the dictionary. If the controller saved the sprite, the sprite could not be freed up, because the link to it would still be saved - therefore, it would continue to save the dictionary, which saved the controller, which saved the sprite.

Benefits of using the component approach (also approved and implemented in the Kobold Kit ):

  • If properly designed, it works with any or several nodes. If what if some day you need a label, effect, shape, node tile?
  • You do not need a subclass for each tile. Some tiles may be simple static sprites. Therefore, for this, use a simple static SKSpriteNode.
  • It allows you to start / stop or add / remove individual aspects as needed. Even on tiles, you initially did not expect to have or not need a certain aspect.
  • The components allow you to create a repertoire of functionality that you will need often and possibly even in other projects.
  • Components provide better architecture. A classic OOP design error is to have Player and Enemy classes, and then realize that they must shoot arrows and equip armor. This way, you move the code to the root class of the GameObject , making the code available to all subclasses. With components, you simply have equipment, and the component for shooting is added to those objects that need it.
  • The big advantage of component-based development is that you start developing individual aspects separately from other things so that you can reuse and add them as needed. You almost naturally write the best code because you are approaching things with a different mindset.
  • And from your own experience, as soon as you modulate the game into components, you get much fewer errors, and they are easier to solve, because you do not need to look at another component code or consider it if it is not used by a component, but even then when one component launches another, you have a clear border, i.e. is the passed value still correct when the other component takes over? If not, the error should be in the first component.

This is a good introduction to component design . A hybrid approach is definitely the way to go. There are more resources for component-based development , but I highly recommend looking out of the way and looking at FRP as the “accepted answer author” suggests - FRP is an interesting concept, but it doesn't have a real application (for now) in game development.

+21
source share

All Articles