Is there a way in Sprite Kit that I can capture the screen (all the current SKScene rendered nodes) to an SKTexture so that I can apply a CIFilter and then assign the SKTexture back to a new SKSpriteNode?
I know that I can set an SKEffectNode, as the parent of my node tree, apply a filter etc and get the result that way but I really need to have a filtered SKTexture (or SKSpriteNode) that I can reuse later?
EDIT:
Possible solution:
textureFromNode:
Renders and returns a Sprite Kit texture that contains the node’s contents.
Yup that works:
SKTexture *texture = [[self view] textureFromNode:[self scene]];
[blurSprite setTexture:texture];
From Apple docs:
textureFromNode: Renders and returns a Sprite Kit texture that
contains the node’s contents.
Code example:
SKTexture *texture = [[self view] textureFromNode:[self scene]];
[blurSprite setTexture:texture];
You should try with snapshotViewAfterScreenUpdates: method from UIView.
Your SKScene is inside an SKView that has this method.
After that you can extract the image from the view, and create a SKNode with it.
Related
I'm using texture atlases in my Sprite Kit game. I'm creating SKTextureAtlas object and store it's textures in array for each animation. So when I need some animation on my hero I call animateWithTextures sending it the corresponding array. There are some lags when I start animations. Is there some way to start animation smoothly?
I am sure there are few ways to get around this. What you need to do is to preload an atlases before your gameplay actually start. Just show a loading screen at the beginning of the game and preload your atlases.
You may try with + preloadTextureAtlases:withCompletionHandler:
[SKTextureAtlas preloadTextureAtlases:textureAtlasesArray withCompletionHandler:^{ /*Game Start*/}];
Another way to implement resource loading before everything else (and keep everything in memory) is described here in Adventure game example
For more details about loading assets asynchronously take a peek into code which can be downloaded from the link above.
had the same problem and I solved it in my game by not using atlases. So try this example:
-(void)makePlayerAnimation:(SKSpriteNode *)player
{
SKTexture *texture1 = [SKTexture textureWithImageNamed:#"texture1.png"];
SKTexture *texture2 = [SKTexture textureWithImageNamed:#"texture2.png"];
SKTexture *texture3 = [SKTexture textureWithImageNamed:#"texture3.png"];
SKAction *animationTextures = [SKAction animateWithTextures:#[texture1, texture2, texture3] timePerFrame:0.1];
[player runAction:animationTextures];
}
When you wish to activate animation do this:
[self makePlayerAnimation:myNode];
or
[self makePlayerAnimation:self.myNode];
Just depends how you declared it.
If you need to run animation forever, you can just add line at the end of previous method:
SKAction *repeat = [SKAction repeatActionForever: animationTextures];
Hope this helps.
The reason for the question is -- is it better to composite a texture at runtime (and make a sprite on that), or just use multiple sprites?
Example: Say you have a small image source for a repeating pattern, where you need many sprites to fill the view (as in a background).
A SKTexture is just image data. A SKSpriteNode is a display object.
The quick answer is no, you cannot draw a SKTexture to the screen without SKSpriteNode.
This answer goes over that limitation : How do you set a texture to tile in Sprite Kit
However, I wanted to answer to give you an option to achieve your ultimate goal.
What you could do is use an SKNode as a container for however many SKSpriteNodes you need to create your background. Then using the SKView method textureFromNode you can create one single SKTexture from that SKNode, that you can use to create a single SKSpriteNode for your background.
Hopefully upcoming version of SpriteKit for iOS 8 has some better tiling options.
Update
Also, in doing some research tonight, since I had a need for this same functionality, I found this :
http://spritekitlessons.wordpress.com/2014/02/07/tile-a-background-image-with-sprite-kit/
Which is doing similar to what I was pondering doing. Gonna copy the code here, in case that page ends up gone :
CGSize coverageSize = CGSizeMake(2000,2000); //the size of the entire image you want tiled
CGRect textureSize = CGRectMake(0, 0, 100, 100); //the size of the tile.
CGImageRef backgroundCGImage = [UIImage imageNamed:#"image_to_tile"].CGImage; //change the string to your image name
UIGraphicsBeginImageContext(CGSizeMake(coverageSize.width, coverageSize.height));
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawTiledImage(context, textureSize, backgroundCGImage);
UIImage *tiledBackground = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
SKTexture *backgroundTexture = [SKTexture textureWithCGImage:tiledBackground.CGImage];
SKSpriteNode *backgroundTiles = [SKSpriteNode spriteNodeWithTexture:backgroundTexture];
backgroundTiles.yScale = -1; //upon closer inspection, I noticed my source tile was flipped vertically, so this just flipped it back.
backgroundTiles.position = CGPointMake(0,0);
[self addChild:backgroundTiles];
I'm learning ios game programming and I need to create my own tile background - we're migrating an infinite-map style game from java.
I have the following code rendering our background tile graphics inside a loop:
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"shallow_1.png"];
sprite.position = CGPointMake(x,y);
[self addChild:sprite];
However, I'm trying to find out how to properly batch render in spritekit. In java/opengl we'd apply the texture, add all the render positions, and then render everything at once - saving performance.
How can I properly batch render these tiles using spritekit? I can't find any information.
If you're planning to work with tilemaps, your best bet is KoboldKit.
It's a game engine for SpriteKit, and adds several features to the framework.
In includes support for time maps (Tiled Map Editor).
Load your texture once:
SKTexture *texture = [SKTexture textureWithImageNamed:#"shallow_1.png"];
Then, make all of your SKSpriteNode objects referencing that texture:
// inside loop
SKSpriteNode *node = [SKSpriteNode spriteNodeWithTexture:texture];
I am subclassing SKSpriteNode class to use on a custom sprite. I would like to keep everything as self contained as possible.
Is there a way for a SKSpriteNode know when it is being used on a scene? I mean, suppose another class does this:
MySprite *sprite = [[MySprite alloc] init];
and ages later does this
[self addChild:sprite];
Can the sprite know by its own when it is added as a child of some scene or another node?
SKNode has an property called scene. If this property returns nil it means it isn't in any scene. You can do the following to check that.
if(!MyNode.scene){
//Do something
}
You can also check that at SKNode Doc.
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKNode_Ref/Reference/Reference.html
I have a sprite with a given texture using:
joyStickRight = [SKSpriteNode spriteNodeWithImageNamed:#"joyStick.png"];
And I'd like to change it when the user touches and holds the sprite. When I detect the touch, I try changing the sprite texture by calling the same function with a different image:
joyStickRight = [SKSpriteNode spriteNodeWithImageNamed:#"joyStick_rollOver.png"];
But this does not seem to work. Nothing changes.
This for an iPad application. I am creating the on screen elements with SKSpriteNodes.
This is likely to be related to variable scope. I'm guessing you are trying to modify another instance of 'joyStickRight' SKSPriteNode. This second instance would not have been added to the scene and therefore not taking any effect.
The only way i found out to change textures ist to swap them via action like this:
SKTexture *texture1 = [SKTexture textureWithImageNamed:#"texture1"];
SKTexture *texture2 = [SKTexture textureWithImageNamed:#"texture2"];
SKAction *swapTextures = [SKAction repeatAction:[SKAction animateWithTextures:#[texture1,texture2] timePerFrame:0.1] count:3];
[node (in your case joyStickRight) runAction:swapTextures];
I still hope this is not the only solution, and also wonder why it does not work when i run it once!!!