Change a sprite texture, with image from a sprite-sheet - xcode

This is how I create a sprite with texture form a sprite-sheet. But how do I change the sprites texture later on?
I have tried using setTexture but I can't get i working.
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"gameSpriteSheet.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"gameSpriteSheet.png"];
[self addChild:spriteSheet];
// Add hangman graphic sprite to Scene
playerSprite = [CCSprite spriteWithSpriteFrameName:#"playerX"];
playerSprite.position = ccp(580, 400);
[self addChild:playerSprite];

You can't.
A CCSpriteBatchNode uses a single texture. All of the sprites added to the CCSpriteBatchNode must use the same texture - that of the CCSpriteBatchNode.
I'm sure there's a different solution to the problem you're facing, give it some thought from a different angle. For example you could achieve the same effect by simply creating a new sprite.
If you're looking to change just the CCSpriteFrame of the sprite, use setDisplayedFrame.

Related

SceneKit - Crossfade material property textures

The documentation for SCNMaterialProperty.contents states that it is an animatable property and indeed I can perform a crossfade between two colors. However I’m unable to crossfade between two images.
So I’m starting to wonder if this is possible at all or if I need to create a custom shader for this?
I’ve tried an implicit animation, in which case it immediately shows the ‘after’ image:
node.geometry.firstMaterial.diffuse.contents = [UIImage imageNamed:#"before"];
[SCNTransaction begin];
[SCNTransaction setAnimationDuration:5];
node.geometry.firstMaterial.diffuse.contents = [UIImage imageNamed:#"after"];
[SCNTransaction commit];
An explicit animation, which does nothing:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"contents"];
animation.fromValue = (__bridge id)[UIImage imageNamed:#"before"].CGImage;
animation.toValue = (__bridge id)[UIImage imageNamed:#"after"].CGImage;
animation.duration = 5;
[node.geometry.firstMaterial.diffuse addAnimation:animation forKey:nil];
As well as through a CALayer, which does nothing:
CALayer *textureLayer = [CALayer layer];
textureLayer.frame = CGRectMake(0, 0, 793, 1006);
textureLayer.contents = (__bridge id)[UIImage imageNamed:#"before"].CGImage;
node.geometry.firstMaterial.diffuse.contents = textureLayer;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"contents"];
animation.fromValue = (__bridge id)[UIImage imageNamed:#"before"].CGImage;
animation.toValue = (__bridge id)[UIImage imageNamed:#"after"].CGImage;
animation.duration = 5;
[textureLayer addAnimation:animation forKey:nil];
From my own testing, it doesn't look like this property is actually animatable when texture values are involved (rather than solid color values). Either that's a bug in SceneKit (i.e. it's intended to be animatable but that's not working) or it's a bug in Apple's docs (i.e. it's not intended to be animatable but they say it is). Either way, you should file that bug so you get notified when Apple fixes it.
(It also doesn't look like this is a tvOS-specific issue -- I see it on OS X as well.)
I can understand why animated texture transitions might not be there... from a GL/Metal perspective, that requires binding an extra texture unit and having two texture lookups per pixel (instead of one) during the transition.
I can think of a couple of decent potential workarounds:
Use a shader modifier. Write a GLSL(ish) snippet that looks something like this:
uniform sampler2D otherTexture;
uniform float fadeFactor;
#pragma body
vec4 otherTexel = texture2D(otherTexture, _surface.diffuseTexcoord);
_surface.diffuse = mix(_surface.diffuse, otherTexel, fadeFactor);
Set it on the material you want to animate using the SCNShaderModifierEntryPointSurface entry point. Then use setValue:forKey: to associate a SCNMaterialProperty with the otherTexture and a CABasicAnimation to animate the fadeFactor from 0 to 1.
Use something more animated (like a SpriteKit scene) as your material property contents, and animate that to perform the transition. (As a bonus, when you do it this way, you can use other transition styles.)
Your animation isn't happening because "contents" property is animatable only when set to a color not for image. You can read it on the apple's documentation about contents property.

How to animate a CALayer to have empty contents

I have a CALayer with an image in it, and it has several sublayers. I want to animate it to have no contents (no image), but continue showing the sublayers. This code does not work:
CABasicAnimation *backgroundOut = [CABasicAnimation animationWithKeyPath:#"contents"];
backgroundOut.toValue = [NSNull null];
backgroundOut.fillMode = kCAFillModeForwards;
backgroundOut.removedOnCompletion = NO;
backgroundOut.duration = 3.0;
[_backgroundLayer addAnimation:backgroundOut forKey:#"contents"];
Here is the only way I could get this to work:
backgroundOut.toValue = (__bridge id)([UIImage imageNamed:#"blankImage"].CGImage);
Note that I don't want to mess with the opacity or anything because this layer has sublayers that need to still be visible.
What is the proper way to animate to empty contents?
I decided this was the cleanest approach, since there was no other answer forthcoming:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), NO, 0);
UIImage *blank = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CABasicAnimation *backgroundOut = [CABasicAnimation animationWithKeyPath:#"contents"];
backgroundOut.toValue = (__bridge id)(blank.CGImage);
backgroundOut.fillMode = kCAFillModeForwards;
backgroundOut.removedOnCompletion = NO;
backgroundOut.duration = 3.0;
[_backgroundLayer addAnimation:backgroundOut forKey:#"contents"];
Another approach might be to create a separate layer to contain your image that is a peer of your other layers, but just behind the other layers (you can use zposition to place it behind the other layers explicitly)--all contained within a root layer. Then, you can animate the image layer's alpha for a fade and won't have the overhead of creating an image on the fly and it won't then fade the other layers.

How to create excellent characters animation in Cocos2D?

This is my implementation to create an animation to an enemy that is inserted in the scene.
CCSpriteFrameCache *cache = [CCSpriteFrameCache sharedSpriteFrameCache];
CCAnimation *animation = [CCAnimation animation];
[animation addSpriteFrame:[cache spriteFrameByName:#"enemy_1.png"]];
[animation addSpriteFrame:[cache spriteFrameByName:#"enemy_2.png"]];
animation.delayPerUnit = 0.1;
[_enemy runAction:
[CCRepeatForever actionWithAction:
[CCAnimate actionWithAnimation:animation]]];
This is just one example of how to realize an animation.
For example, if I have a "Boss" made ​​up of several parts, I would like to make it very quaint with animations in different parts of the body.
Is there a way to create animations smoother than the continuous replacement of images?
Computationally, this is the best you can do, or are there more efficient techniques?
Thanks for the help
Loading multiple images, one after another is the only way to 'animate' within cocos2d. Of course for various effects that can be mathematically defined you can use functions that are provided within the framework (like rotating, resizing, moving or physics based animation such as falling or bouncing)
If your artists provide a clean spritesheet with fluid frame by frame animation you'd be surprised by how smooth your animation can look.
//if you are using spritesheet then use this...for loading or sprites in your game...
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"walkcycle.plist"] ;
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"walkcycle.png"];
[heroWorldLayer addChild:spriteSheet];
NSMutableArray *enemyFrames = [NSMutableArray array];
for(int i = 1; i <= 11; ++i) {
CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"Run_Anim00%02d.png", i]];
[enemyFrames addObject:frame];
}
id enemyAnim = [CCAnimation animationWithFrames:enemyFrames delay:1.0/22.0];
id enemyAnimate = [CCAnimate actionWithAnimation:enemyAnim restoreOriginalFrame:NO];
id _runAction = [CCRepeatForever actionWithAction:enemyAnimate];
[_enemy runAction:_runAction];

What happened to TextureMgr?

In cocos2d, there used to be a TextureMgr method (Async) that let you load images for later use. Now when I use TextureMgr, it says it's undeclared. Is it deprecated? I'm on .99.5. If it's no longer usable, what replaces it? Is there anything that can do the same as this line?
[[TextureMgr sharedTextureMgr] addImageAsync:...NSString goes here... target:self selector:#selector(method:)];
take a look at CCTextureCache
http://www.cocos2d-iphone.org/api-ref/0.99.5/interface_c_c_texture_cache.html
maybe it is you are looking for.
This cache is used when your are creating any object weith a texture: sprite for example. And you can use it for precaching your images.
EDIT:
The CCTextureCache is used when your creating ang object with a texture, as i said - and so if the texture is already in cache the element creation is much more faster then if you would loading the texture first and then creating an object.
For example if you are writing the code like this:
id sprite = [CCSprite spriteWithFile: #"my-file.png"]
and the #"my-file.png" texture is not in cache it will be loaded first and it will take some time (much more then just creating an object).
If you are writing code like this:
id sprite1 = [CCSprite spriteWithFile: #"my-file.png"];
id sprite2 = [CCSprite spriteWithFile: #"my-file.png"];
Then sprite1 will be created slow and sprite2 much more faster because the texture is already in cache.
You can manuale add texture to cache
[[CCTextureCache sharedTextureCache] addImage: #"my-file.png"];
and the creating of all objects with this texture will be fast.
The common place in code when you have to precache textures is game loading or level package loading or level loading.
Also You can precache sounds if you need using them SimpleAudioEngine singleton
Andrew pretty much answered your question, I just want to give you a code segment about how to use CCTextureCache and CCSpriteFrameCache. Texture Cache load real textures/images and sprite frame cache loads the information regarding textures (if you are loading a Sprite sheet.)
Okay here's the sample code.
Here latestBurp1-1.pvr.ccz and burpParticles1-1.png are my sprite sheets and their information is in (same name).plist files.
In the below function I am loading textures and also spriteFrames (the info about textures).
Also take a look at pvr files and pvr.ccz file they load much faster than png.
-(void) loadBurpAnimation {
NSString* burpFile;
NSString* burpFilePVR;
NSString* burpParticlesFile;
NSString* burpParticlesFilePVR;
burpFile = #"latestBurp1-1.plist";
burpFilePVR = #"latestBurp1-1.pvr.ccz";
burpParticlesFile = #"burpParticles1-1.plist";
burpParticlesFilePVR = #"burpParticles1-1.png";
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:burpFile texture:[[CCTextureCache sharedTextureCache] addImage:burpFilePVR]];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:burpParticlesFile texture:[[CCTextureCache sharedTextureCache] addImage:burpParticlesFilePVR]];
NSMutableArray *burpFrames = [NSMutableArray array];
for(int i = 231; i <= 268; ++i) {
[burpFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"burp.%d.png", i]]];
}
burpAnim = [[CCAnimation alloc] initWithFrames:burpFrames delay:0.04f];
[burpFrames removeAllObjects];
//Burp Particles
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:burpParticlesFile];
NSMutableArray *burpParticlesFrames = [NSMutableArray array];
for(int i = 3; i <= 37; ++i) {
[burpParticlesFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"Burp_%05d.png", i]]];
}
burpParticlesAnim = [[CCAnimation alloc] initWithFrames:burpParticlesFrames delay:0.04f];
[burpParticlesFrames removeAllObjects];
}
I just gave you a lot of information so you might need to google some terms.
I think you are looking for [CCSpriteFrameCache sharedSpriteFrameCache]
http://www.cocos2d-iphone.org/api-ref/0.99.5/interface_c_c_sprite_frame_cache.html

How to animate the drawing of a CGPath?

I am wondering if there is a way to do this using Core Animation. Specifically, I am adding a sub-layer to a layer-backed custom NSView and setting its delegate to another custom NSView. That class's drawInRect method draws a single CGPath:
- (void)drawInRect:(CGRect)rect inContext:(CGContextRef)context
{
CGContextSaveGState(context);
CGContextSetLineWidth(context, 12);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathAddLineToPoint(path, NULL, rect.size.width, rect.size.height);
CGContextBeginPath(context);
CGContextAddPath(context, path);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
My desired effect would be to animate the drawing of this line. That is, I'd like for the line to actually "stretch" in an animated way. It seems like there would be a simple way to do this using Core Animation, but I haven't been able to come across any.
Do you have any suggestions as to how I could accomplish this goal?
I found this animated paths example and wanted to share it for anyone else looking for how to do this with some code examples.
You will be using CAShapeLayer's strokeStart and strokeEnd which requires sdk 4.2, so if you are looking to support older iOS SDKs unfortunately this isn't what you want.
The really nice thing about these properties is that they are animatable. By animating strokeEnd from 0.0 to 1.0 over a duration of a few seconds, we can easily display the path as it is being drawn:
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 10.0;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[self.pathLayer addAnimation:pathAnimation forKey:#"strokeEndAnimation"];
Finally, add a second layer containing the image of a pen and use a
CAKeyframeAnimation to animate it along the path with the same speed
to make the illusion perfect:
CAKeyframeAnimation *penAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
penAnimation.duration = 10.0;
penAnimation.path = self.pathLayer.path;
penAnimation.calculationMode = kCAAnimationPaced;
[self.penLayer addAnimation:penAnimation forKey:#"penAnimation"];
Which the source can be viewed here and a demo video here. Read the creators blog for more information.
Sure—don't draw the line yourself. Add a 12-pixel-high sublayer with a flat background color, starting with a zero-width frame and animating out to your view's width. If you need the ends to be rounded, set the layer's cornerRadius to half its height.

Resources