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.
Related
I'm trying to learn SceneKit development and try to add a skybox in the background.
To store the cube map textures I found that XCAssets has a type Cube Texture Set which seems to fit the bill perfectly.
However. I've not found any way to access the texture in code (e.g. as an image set where you call UIImage(named: "asset_name") ). I've tried creating an SKTexture, MDLTexture og MTKTexture from the asset but without success. Do anyone know how I can use the cube texture set?
You can load the cube texture from the asset catalog easily using MetalKit.
import MetalKit
at the top of your file. These two lines do the business:
let textureLoader = MTKTextureLoader(device: scnView.device!)
scene.background.contents = try! textureLoader.newTexture(name: textureName,
scaleFactor: 1.0,
bundle: .main, options: nil)
I tried this in a project created from the default SceneKit game template, and placed these two lines in GameViewController.swift after setting the view's background color
(I expect you can do it using the other technologies too, but this is how you can load a cube texture using Metal)
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)
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)
For my game I am trying to create custom textures from two other textures. This is to allow for a varietly of colours, etc in my sprites.
To do this, I'm creating a sprite by adding both textures together, then applying this to a new SKTexture by using
SKTexture *texture = [self.view textureFromNode:newSprite];
This works great on the whole and I get a nice custom texture. Except when trying my game on Retina devices, where the texture is the correct size on the screen, but clearly a lower resolution.
The textures are all there and properly named so I don't believe that that is an issue.
Has anyone encountered this, or know how I can create the proper #2x texture?
I finally (accidentally) figured out how to fix this. The node which you are creating a texture from has to be added to the scene. Otherwise you will get a non-retina size for your texture.
It's not ideal as it would be nice to create textures without having to add them onto the screen.
I've discovered another way of improving the fidelity of textures created from ShapeNodes, not quite related to this question - but useful intel.
Create your shape at x2 its size and width.
Create all the fonts and other shapes at the same oversized ratio.
Make sure your positioning is relative to this overall size, (e.g. don't use absolute sizes, use relative sizes to the container.)
When you create the texture as a sprite it'll be huge - but then apply
sprite.scale = 0.5; // if you were using 2x
I've found this makes it look much higher resolution, no graininess, no fuzziness on fonts, sharp corners.
I also used tex.filteringMode = SKTextureFilteringNearest;
Thus: it doesn't have to be added to the scene and then removed.
Ok so I am conceptualizing for a game I want to make and have played a little bit but ran in to a problem. I am learning iOS programming and learning cocos2d also.
I have about 20 images. They are all the same size 64x64. Basically they are grouped as 5 different shapes (square, circle, etc.) each in 5 different colors. What I want to be able to do, is randomly drop them down the screen in portrait. There will only ever be one per column falling. There can be duplicates across the screen, I actually want that. While it is random, I want to control the introduction of a new color.
I have been really racking my brain to figure out how to do this. As I said, I am pretty new to this so any help would be really great.
If I could be so bold, can any answers be in a format to "Explain it like I'm 5 I have only been doing this for about 1 year"
First, have a look at this tutorial about sprites sheets: it will show you how to create your sprite sheets and load them into memory.
(Since the tutorial aims at building an animation, you can skip points 4 and 5).
Now, if you have your sprite sheets in place, what you do to create a sprite is:
CCSprite* sprite = [CCSprite spriteWithSpriteFrameName:#"frameName.png"];
and then you can add it to your layer/scene.
About the random selection: you can keep a list of image names (say, an NSArray) and then randomly (i.e., use srand to generate a random index) select the #"frameName.png" value from it.
When using Cocos2D the best way to use sprites is with Sprite Sheets. That is, a combination of two type of files:
A plist file with all the coordinates of N sprites and all different data about the sprites, size, name, etc.
A png file with all the sprites
You can make a sprite sheet with different types of software, like:
Zwoptex
Sprite Helper
Sprite Packer
Sprite Master
Texture Packer
About performance, when using a sprite sheet you will need to use CCSpriteBatchNode, as the API documentation states (read here):
A CCSpriteBatchNode can reference one and only one texture (one image file, one texture atlas). Only the CCSprites that are contained in that texture can be added to the CCSpriteBatchNode. All CCSprites added to a CCSpriteBatchNode are drawn in one OpenGL ES draw call. If the CCSprites are not added to a CCSpriteBatchNode then an OpenGL ES draw call will be needed for each one, which is less efficient.
THe code to use Sprite Sheet:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"sprite.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"sprite.png"];
[self addChild:spriteSheet];
Hope this helps.