How to Force Sktextureatlas Created from a Dictionary to Not Modify Textures Size

How to force SKTextureAtlas created from a dictionary to not modify textures size?

The problem come from the use of SKTextureAtlas(dictionary:) to initialize atlas.

SKTexture created using this method does not embed data related to image's scale property. So during the creation of SKSpriteNode by init(texture:) the lack of scale information in texture leads to choose texture's size in place of image's size.

One way to correct it is to provide node's size during SKSpriteNode creation: init(texture:size:)

SKTexture and the scale property of a UIImage

I've found this too and believe it's a bug. Not sure how Apple are going to rectify this as it could break existing code.

My workaround is to read the scale of the UIImage and then set the scale of the SKSpriteNode to 1.0 / scale. You need to do this for both the x and y scales of the SKSpriteNode.

SKTileMapNode Pixel to Pixel Aligning

It turns out that SpriteKit's SKTileMapNode really likes assets to be optimized for all resolutions. This fixed my pixel alignment problem entirely. While this may seem obvious, I originally added @1x files in order to use an optimized texture atlas. It took more research to discover how to add different resolutions to a texture atlas.

Since it is different than normal atlases (appending ".atlas" to a folder of images), I will describe how to do so here

Go to the assets.xcassets folder and click "New Sprite Atlas." In here drag in all @2x and @3x images. Delete the [asset-name].atlas folder if you had one before, as this will not support different resolutions natively.

From here on, the atlas can be accessed just as the original [asset-name].atlas folder was accessed in code.

Loading SKTexutre Crash mutated while being enumerated

Its a bug

This definitely looks like a bug in SpriteKit. You could try to preload the image first, it may end up down a separate code path. Its possible preloading will help prevent this from occurring.

https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Sprites/Sprites.html#//apple_ref/doc/uid/TP40013043-CH9-SW21

Atlas

If you aren't already, consider using a texture atlas and preloading all your assets in there.

https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Sprites/Sprites.html#//apple_ref/doc/uid/TP40013043-CH9-SW15

Locking

A side note, and I don't think this will solve your crash at all, but using @synchronized for locking is more semantic.

Instead of having an NSLock, just wrap the current lock code in an @synchronized(self). If you are having contention with other parts of the code that also lock on self then you can create a separate id textureLockObject property.

Apple can and will optimize @synchronized code more so than using NSLocks.

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW3

Correct way to import and create many large sprites: SpriteKit

So let me see if I can reasonably answer your question. Please keep in mind that while I have used SpriteKit, I'm not a big fan of it. And when I did use it, I did not use any of the tools Apple provided (eg. SKTextureAtlas, sks files, etc). Nevertheless, this should still be applicable. Best practices as per Apple? I have no idea.

Question: Import folder, or images?

If you use Xcode's means of generating atlases, you Add Files... to your project a folder named XYZ.atlas, where XYZ will be the texture name. This folder contains all of your textures which will be in the atlas.

Question: Import to WHERE within the project?

Just has to be in your project. But you should have some organization Group hierarchy in your project file.

Question: Load and reference them, how?

An example of loading would be something like (yeah, I know boo, Obj-C):

self.myTextureAtlas = [SKTextureAtlas atlasNamed:@"MyTexture"];

Inevitably, you will want access to the actual textures and you'll need to do something like:

self.tex0 = [self.myTextureAtlas textureNamed:@"tex0"];

A good tutorial is here: https://www.raywenderlich.com/45152/sprite-kit-tutorial-animations-and-texture-atlases

Here is also a screenshot that shows the .atlas folder. It also shows a couple of tool generated atlas files.

Sample Image

So here you can see I have 2 Groups which are references to folder MyTexture.atlas and ProgressBar.atlas. In game, they will be called MyTexture and ProgressBar.

I have also included, just as an example the same atlases, but pre-built. You would not have both in your project, I only used it to show what including a pre-built would loo like. I generated them using TexturePacker. I'll get into why this may be a better option later on. TexturePacker also can create SpriteKit atlases instead of the atlas PNG.

In reality, an atlas is really just a texture with sub-textures are associated with it. The sub-textures are X/Y/W/H sections of the texture. The actual memory for the texture is "held" by the atlas. Understanding what an atlas is is a useful thing, because it allows you to think through how you would support it if you had to implement it yourself. Or enhance it.

Now let's go over how you would use this altogether to do something useful.And for this you're going to need a texture manager (aka TextureManager), sprite manager (aka SpriteManager) and a manifest of some sort.

The manifest is really some form of association between "sprite name" to atlas:sub texture pair. For example:

 {
"progressbar": {
"atlas": "ProgressBar",
"subtexture": progressbarbacking"
},
"progressbarfillr": {
"atlas": "ProgressBar",
"subtexture": progressbarfillr"
}
}

In this case it is some JSON, but you can have whatever format you want. When I build my games, I have a build assets phase which generates all my textures and from that, builds a manifest. This manifest tells me not only what textures exist, but is used later on to find the correct association of a "sprite name" to the actual atlas and sub texture. "sprite name" is just some name you have associated meaning. It could be "zombie" for example.

You use a TextureManager as your asynchronous loader. In addition, it is your inventory manager of all your textures. As an inventory manager, it will prevent you from double loading textures and also give you the correct reference to textures/atlases when requested (if they exist).

You would use the SpriteManager to create a new SKSpriteNode using the data from the manifest. For example:

SKSpriteNode *progressBar = [[SpriteManager sharedInstance] createSprite:@"progressbar"];

Here I've made it a singleton. Use whatever implementation you want. If you hate singletons, that is fine, this is just an example. You'll note that it returns a SKSpriteNode. I see a lot of people making subclasses from SKSpriteNodes. I never do this. To me, the graphic component is always a "has a". However, if you are doing an "is a", you can still do this. You just need to feed in the class you need. I'm considering the way of handling that out of scope for this question.

Now if you look at the manifest, you'll notice that progressbar is associated with an atlas named ProgressBar and a sub texture named progressbarbacking. To get the texture you need, you'd have some implementation code in SpriteManager like:

// NOTE the literal names would be variables which contained the unpacked association from progressbar
// literal strings are used to make the idea easier to follow
SKTextureAtlas *atlas = [[TextureMaanger sharedInstance] findAtlasNamed:@"ProgressBar"];
//Error check of course

SKTexture *tex = [atlas textureNamed:@"progressbarbacking"];
// Error check of course

SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:tex];

Or perhaps you would just have a call:

SKTexture *tex = [[TextureManager] sharedInstance] texNamed:@"progressbarbacking" atlas:@"ProgressBar"];
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:tex];

And there you have it. A means to get sprites from atlases.

Best practices as per Apple? Dunno, but it is along the lines of what I do.

Some people will complain that there will be dictionaries involved and some of this will be "slow". And yes, there is a penalty for this. And reality, I don't use a dictionary for this either, but it is easier to get the idea across as the dictionary. Also keep in mind that I consider the usage of this to occur during loading phases and very little during game play, if at all. One of the tricks to performant games is pre-loading all or as much of the data you need prior to actual game play.

Oh, going to why I pre-build the atlases. So part of your question was organization of textures to atlas. And that is why. I like to see the generated atlas and understand what the size is and what is in it. Additionally it makes downloadable atlases easier.

As an optimization, you would want to try and put textures in which are all drawn relatively the same time. For example, it would make sense to have all HUD items in the same atlas versus mixing HUD with background.



Related Topics



Leave a reply



Submit