SpriteKit SKTexture is not passed as reference to SKSpriteNode - ios

Apple recommends to use one SKTexture object if it will be used multiple times (https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKTexture_Ref/).
So I wanted to test if it is really passed as reference:
var fireSpriteTexture:SKTexture = SKTexture(imageNamed: "fire")
let fireSprite = SKSpriteNode(texture: fireSpriteTexture)
fireSprite.position = CGPointMake(50,50)
fireSprite.zPosition = 100
// Change texture -> We should see no fire
fireSpriteTexture = SKTexture(imageNamed: "water")
self.addChild(fireSprite)
In my understanding this swift code should make the water texture visible in my app BUT in fact the fire texture is placed on the screen.
Why? Is it a bug?
To make it more clear I would also like to know what happens if I init my node with SKSpriteNode(imageNamed: "fire") and then copy it with its copy() method, do both objects share the same "fire" resource?

I do not think you are understanding pass by reference correctly, you pass the reference of fire into your sprite, the sprite will then retain a reference of this texture.
You are changing the fire texture to a new texture with a new reference. This does not mean the sprite with the old reference is going to automatically change to the new reference.
If you want to check for reference, you want to compare the fire texture to the sprite texture, not create a whole new texture.
If you want to change the texture, then you need to edit the data of the texture without creating a new reference.

Related

iOS: Can I use Texture Atlas already created with Adobe Animate?

I know that I can add sequences of individual images in Xcode, and let it create the Texture Atlas. This process is well-described in easily-searched venues. (Like this one.)
I am getting atlases from designers, created with Adobe Animate (v18.0) already combined into the full sheet, and moreover with the accompanying XML file describing the animation. (In which sub-images and display frames do not match 1:1, so it's hard to see how Xcode would figure that out.)
It's not clear to me from the SpriteKit docs whether/how to use these pre-defined Texture Atlases. Is this possible?
If you're getting pre-baked texture atlases, with externally-generated descriptions of where the sprites should get their textures, you'll probably have to create your sprites using SKTextures that you create using the init(rect:in:) initializer of a SKTexture.
You'll need to read the sprite's extents out of the XML file, and then create a texture out of the atlas. Then you can create a new SKTexture object that represents a part of the larger texture to act as your sprite's texture.
This is untested pseudocode, but it shows the process:
let spriteRect = (get the rect from the XML)
let atlas = SKTexture( imageNamed: "myTextureAtlas" )
let spriteTexture = SKTexture( rect:spriteRect, in:atlas )
let sprite = SKSpriteNode( texture:spriteTexture )
Once you have this process in place, you can animate the sprites using the usual methods, like setting up SKActions with a list of textures out of the texture atlas.

Use image as tileset in Spritekit

I don't find a way to use this sort of images as tile set for my games.
Is there a way to do it, or should I add all object I want one by one ?
I tried to add a new Grid Tile Set then a Single Tile Group, but if I try to use this in a Tile Map Node, it will use all the image as texture for one block.
I don't know if it's possible, but if you can help me, I would be grateful.
Unfortunately you will have to separate the image into smaller images manually..
The functionality you want just doesn't exist.
You can use your image as a texture atlas, but you will not be able to use SKTextureAtlas, you will have to break them out your self.
If you look at SKTexture, you have an init called init(rect: CGRect, in: SKTexture). This will create a new SKTexture object for you, while still referencing the texture memory space of the original texture. You are just going to have to use something like a plist to load in all of the CGRRect info to create this atlas.
Example:
let textureAtlas = SKTexture(imageNamed:"CEm72.png") //I convert your jpg to png somehow
let rectDarkKnight = CGRect(x:0,y:96,width:32,height:32)
let texDarkKnight = SKTexture.init(rect: rectDarkKnight, in: textureAtlas)

Preloading textures in SpriteKit

I've done some research and I can't seem to find anything that clearly explains how to go about preloading both single textures and textures within animations. I'm currently using Atlas's in Assets.xcassets to group related animation images. Does having my images in the Atlas mean that they are preloaded? As far as single images, does it make sense to declare the texture before GameScene like this: let laserImage = SKTexture(imageNamed: "Sprites/laser.jpg") and then (for example) within one of my SKSpriteNode subclass I can just pass laserImage through?
I ultimately wanted to know if there was a well defined way of going about this or if I should just store each texture as a constant before GameScene. Any advice on the proper (and most efficient) way of going about this would be great.
I tried to implement appzYourLife answer but it increased the time to load every texture. So I ended up putting all the most used Sprites in one single atlas and puting it in a singleton.
class Assets {
static let sharedInstance = Assets()
let sprites = SKTextureAtlas(named: "Sprites")
func preloadAssets() {
sprites.preloadWithCompletionHandler {
print("Sprites preloaded")
}
}
}
I call Assets.sharedInstance.preloadAssets() in menuScene. And:
let bg1Texture = Assets.sharedInstance.sprites.textureNamed("background1")
bg1 = SKSpriteNode(texture: bg1Texture)
to reference a texture already loaded in memory.
One Single Texture Atlas
Put all your assets into a single Sprite Atlas. If they don't fit, try at least to put all the assets of a single scene into a single Sprite Atlas
Preloading
If you want you can preload the texture atlas in memory with this code
SKTextureAtlas(named: "YourTextureAtlasName").preloadWithCompletionHandler {
// Now everything you put into the texture atlas has been loaded in memory
}
Automatic caching
You don't need to save a reference to the texture atlas, SpriteKit has an internal caching system. Let it do it's job.
Which name should I use to reference my image?
Forget the name of the file image, the name you assign to the image into Asset Catalog is the only name you will need.
How can I create a sprite from an image into a texture atlas?
let texture = SKTextureAtlas(named:"croc").textureNamed("croc_walk01")
let croc = SKSpriteNode(texture: texture)

How to release memory created by SKTexture

I have an application that creates scene kit textures using the following:
let texture = SKTexture(image: image)
The problem is that after several calls, the application crashes due to low memory.
I really only need the most recently created texture so when creating a new one, the preview ones' memory can be released, however, Scene Kit seems to be holding on to these textures even when they aren't being used anymore (the nodes are no longer part of the scene).
How can I manually tell Scene Kit to release the memory and cache of the texture so I don't get into the low memory crashing situation?
Also, I know that the SceneKit documentation recommends removing all references to the texture by removing nodes and such, but that should be happening and yet the Texture is not being released. Is there a good way of debugging and seeing what's holding on to the reference?
In re-reviewing the code, I'm creating a scene to hold the image to be rendered in the primary scene (whose node doesn't change). Basically, there is an object in the scene that can display an image or a video and that gets changed out by returning a Scene which is used as the material diffuse contents.
let texture = SKTexture(image: image)
let sprite = SKSpriteNode(texture: texture)
let scene = SKScene(size: sprite.size)
sprite.position = CGPointMake(CGRectGetMidX(scene.frame), CGRectGetMidY(scene.frame));
scene.addChild(sprite)
And then being used here:
primaryObjectMaterial.diffuse.contents = scene
I would expect once the scene's node material contents are changed, this scene (and the texture and image) would be released as there would no longer be any references to it but perhaps the caching system is hanging onto something or I'm doing something wrong?
It's hard to give you an exact answer with out seeing your code or knowing how the app works but you need to take the node that the texture is on and remove it
nameOfNode.removeFromParentNode
This will remove it from memory and you need to add a new node with the new texture.

Attaching arbitrary information to an SKSpriteNode

I'm making a game with Sprite Kit in which I generate many instances of an SKSpriteNode. When I detect a collision between two sprites, I can easily get a lot of data about the colliding sprites (position, rotation, scale, etc.).
My question: Is there a way to embed arbitrary information, like a string or integer, into the sprite? Say I wanted to timestamp each sprite with the time it was generated, and then display that when it collides with something.
UIButton come to mind, where you can define button.tag, and use that information later.
SKNode has a userData dictionary where you can store data.
Subclass SKSPriteNode and add properties. This will allow you to attach whatever information you would like to your sprite while retaining the built in SKSpriteNode functionality.

Resources