Change texture on SKSpriteNode - ios7

I have an SKSpriteNode and I want to change the texture on it when the user touches the screen. But cannot work out how to do so.
Creating and adding the head. (Declared in header).
head = [SKSpriteNode spriteNodeWithImageNamed:[NSString stringWithFormat:#"%#",face]];
head.position = CGPointMake(size.width / 2, size.height / 2);
[self addChild:head];
When a touch is detected, the below is run, but I cannot work out how to apply it to the SKSpritenode?!
SKAction* changeFace = [SKAction setTexture:[SKTexture textureWithImageNamed:[NSString stringWithFormat:#"%#",face]]];
[self runAction:changeFace];
I have tried the below also, but it does not seem to work...
head.texture = [SKTexture textureWithImageNamed:[NSString stringWithFormat:#"%#",face]];
Hope somebody is able to point me in the correct direction!

It looks like you are trying to run the action on the scene (or any other object than your sprite.) The second code should work however, but using the SKAction try this instead.
[head runAction:changeFace];

I have it working here, look at the code below:
1 - Create the SKSpriteNode
self.ninja = [SKSpriteNode spriteNodeWithImageNamed:#"ninja1"];
self.ninja.position = CGPointMake(self.ninja.size.width/2, self.frame.size.height/2);
[self addChild:self.ninja];
2 - Change the texture:
self.ninja.texture = [SKTexture textureWithImageNamed:#"ninja2"];
Obs: I change the texture in touchesBegan event, but this should work in any way you want to do.

I also experiencing this one, I was able to change texture but the the texture is stretch.
What is causing this?

Try This
SKAction*animation=[SKAction animateWithTextures:actions timePerFrame:0.1 resize:NO restore:YES];

Related

SKLabelNode not following performSelector (setHidden) with 64 bit iPhone

I am using sprite kit and I have an SKLabelNode that I set up in my initWithSize method. It is declared as..
#property (nonatomic, strong) SKLabelNode *chargesToShowUponCompletion;
In the initWithSize method I create it this way..
_chargesToShowUponCompletion = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
_chargesToShowUponCompletion.fontSize = 40;
_chargesToShowUponCompletion.fontColor = [SKColor whiteColor];
_chargesToShowUponCompletion.position = CGPointMake(self.size.width/2, 1.5/3.0 * self.size.height);
_chargesToShowUponCompletion.zPosition = 20;
[self addChild:_chargesToShowUponCompletion];
[_chargesToShowUponCompletion setHidden:YES];
As you can see, I hide the LabelNode right after I have added it to the scene. I actually have several that I create like this, and I reveal them a different times. This one in particular needs to be shown for a bit, then disappear. So to accomplish that I have used the following.
[_chargesToShowUponCompletion performSelector:#selector(setHidden:) withObject:#YES afterDelay:3];
This works perfectly in iPhone 5, iPhone 4, and the simulator. However, when I run it on a 64 bit machine, or simulator, it does not work. It actually works as if I had setHidden withObject:#NO. I am figuring it has something to do with the 64 vs 32 bit iPhones, but I can not figure it out. I read Apple's doc about supporting both and I have my Architectures set for Standard. Is there some way I can get this selector to run on both types of machines? Also, the other LabelNodes that I create are hidden and shown at various times, they all work, but none of them deals with the selector. Any ideas? Thanks for the help!
So if any one is following this, or having the same problem, I solved it two different ways. The first one was to keep the #selector(setHidden) line of code. I replaced the #YES with #(1l) and this solved the problem of running on 5s.
[_chargesToShowUponCompletion performSelector:#selector(setHidden:) withObject:#(1l) afterDelay:3]; //change #YES to #(1l)
[_substanceToShowUponCompletion performSelector:#selector(setHidden:) withObject:#(1l) afterDelay:3];
UPDATE: I have heard it is bad to use performSelector in this manner. I would just go with the run block.
The other method was to make an SKAction run Block like this.
SKAction *action1 = [SKAction waitForDuration:3.0];
SKAction *action2 = [SKAction runBlock:^{
_chargesToShowUponCompletion.hidden = YES;}];
SKAction *action3 = [SKAction runBlock:^{
_substanceToShowUponCompletion.hidden = YES;}];
SKAction *seq1 = [SKAction sequence:#[action1, action2]];
SKAction *seq2 = [SKAction sequence:#[action1, action3]];
[_chargesToShowUponCompletion runAction:seq1];
[_substanceToShowUponCompletion runAction:seq2];
I ran that twice as well since I had two SKLabelnodes to hide. Hope this helps someone!!!!

Custom animation when switching from one UICollectionViewLayout to another?

As a test I made one layout that displays cells in a vertical line and another that displays them in a horizontal layout. When I call [collectionView setCollectionViewLayout:layout animated:YES]; it animates between the two positions very cleanly.
Now that I'd like to do is have all the views do a few spins, warps and flips around the screen (probably using CAKeyframeAnimations) before finally arriving at their destination, but I can't find a good place to hook this in.
I tried subclassing UICollectionViewLayoutAttributes to contain an animation property, then setting those animations in an overridden applyLayoutAttributes: method of the UICollectionViewCell I'm using. This works... EXCEPT it appears to happen only after the layout transition is complete. If I wanted to use this, I'd have to have the layout not change the current positions of the objects right away, only after the it reaches this apply attributes part of the code, and that seems like a lot of work...
Or I could subclass UICollectionView and override setCollectionViewLayout:animated:, but that also seems like a lot of state to keep around between layouts. Neither of these optins seems right, because there's such an easy way to animate additions/deletions of cells within a layout. I feel like there should be something similar for hooking into the animations between layouts.
Does anyone know the best way to get what I'm looking to accomplish?
#define degreesToRadians(x) (M_PI * (x) / 180.0)
UICollectionView *collectionView = self.viewController.collectionView;
HorizontalCollectionViewLayout *horizontalLayout = [HorizontalCollectionViewLayout new];
NSTimeInterval duration = 2;
[collectionView.visibleCells enumerateObjectsUsingBlock:^(UICollectionViewCell *cell, NSUInteger index, BOOL *stop)
{
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
rotationAnimation.toValue = #(degreesToRadians(360));
rotationAnimation.duration = duration;
rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[cell.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
}];
[UIView animateWithDuration:duration
animations:^
{
collectionView.collectionViewLayout = horizontalLayout;
}];

Why is CATransform3D not working when triggered from viewWillAppear?

In viewWillAppear I want to adjust some of my views using the following transformation (Monotouch code):
CATransform3D oTransform3D = CATransform3D.Identity;
oTransform3D.m34 = 1.0f / -400;
oTransform3D = oTransform3D.Translate( 110, 0, 0);
oTransform3D = oTransform3D.Rotate( (-70f) * (float)Math.PI / 180f, 0, 1, 0);
However, this causes the view to be rendered far left of the screen.
If I put the very same code in viewDidAppear, it is working.
I have already checked that all views have valid sizes.
Did you ever figure this out? I've run into a similar situation where a 3D transform refuses to behave when applied to an immediate subview of a UIViewController's root view. A workaround for me was to wrap the subview in another container UIView.
Something along these lines:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height)];
container.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoResizingFlexibleHeight;
[self.view addSubview:container];
self.myCrazyFlippingSpinningView = [[CrazyFlippingSpinningView alloc] init];
[container addSubview:self.myCrazyFlippingSpinningView];
}
Of course, I'd prefer to not have to do that. It's such a hack. If anyone's encountered this and has a better solution, I'd love to see it.
Thanks!
Not sure I fully understand the question but I am going to take a swag.
CATransform3D has a vanishing point that transforms refer to. The vanishing point is always the x,y center point of whatever view you place the transformed view onto. When I was struggling through the 3D transform issues and getting unexpected results, it was always solved by simply making sure I knew the size and center point of the container view.
You say your code works in ViewDidLoad...does that mean you are adding a transformed view to the base view? like: this.View.AddSubview(yourTransformedView); ??
I mean, I don't think your problem is a matter of ViewWillAppear vs. ViewDidLoad; I am going to guess that if I saw the code where you apply your transform to the views and then add your views to a base view or some other superview, I would be able to quickly tell you what is happening.

Checking if a CCSprite exists, adding and removing from a CCScene, and looping until a condition is met

I'm currently working on a game in which I add a CCSprite to a scene (an animated ghost), and have it move from a randomly generated point to another randomly generated point, fade out, and then be removed from the scene. When the player taps the ghost, 1 is added to the score.
Everything is working beautifully, but I want this action to be repeated until a condition is met (specifically, when the score reaches 50). I can't figure out the best way to do this. Every time I try to run a loop, it repeats infinitely and crashes the game.
What is the best way to achieve such a loop? I've heard mention of creating arrays for sprites, but there will only ever be one iteration of the ghost on the scene at one time. Are there any other ways of achieving this?
Ideally, what I want to achieve is: run the actions, wait for them to finish, and then run them again until the score is 50.
Here's my code:
-(void) ghostSpawner {
CGPoint endPoint = [self randomPointGenerator];
[self birthGhost];
CCSprite * Ghost = (CCSprite *) [self getChildByTag:2];
//...then fade him in
CCFiniteTimeAction *ghostBegin = [CCSequence actions:[CCDelayTime actionWithDuration:1],[CCTintTo actionWithDuration:0 red:0 green:0 blue:0],[CCTintTo actionWithDuration:.5 red:255 green:255 blue:255], nil];
//...move him over a given amount of time, then fade him out and DIE
CCFiniteTimeAction *ghostEnd = [CCSequence actions:[CCMoveTo actionWithDuration:2 position:endPoint],[CCTintTo actionWithDuration:1 red:0 green:0 blue:0],[CCCallFunc actionWithTarget:self selector:#selector(killGhost)], nil] ;
[Ghost runAction:[CCRepeatForever actionWithAction:[CCSequence actions:ghostBegin,ghostEnd, nil]]];
}
For generating the sprite:
-(void) birthGhost {
CGPoint spawnPoint = [self randomPointGenerator];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"ghostidle.plist"];
CCSprite * Ghost = [CCSprite spriteWithSpriteFrameName:#"Ghost0001.png"];
[Ghost setColor:ccc3(0,0,0)];
Ghost.anchorPoint = ccp(.5, .5);
Ghost.position = spawnPoint;
Ghost.anchorPoint = ccp(0.5f,0.5f);
Ghost.scale = .4;
Ghost.tag = 2;
[Ghost runAction:ghostIdleAnimation()];
[self addChild:Ghost z:0];
}
The 'ghostSpawner' function is called in my 'init' on the scene
[self ghostSpawner];
I hope this is enough. Any feedback would be awesome, since this is one of my first real iPhone projects. Thanks in advance!
Alright, here's how I would do it:
Send ghostSpawner to self on init (as you do now).
In ghostSpawner, take your sprite, reset it (change the opacity to 0, reset the position, whatever), then send it 2 runAction messages: one to fade in, the other a sequence to move to the end position, then send a killGhost message to self.
In killGhost, check whether the score is less than 50. If it is, send ghostSpawner to self again, otherwise do whatever you do when they have reached 50 points.
Your sprite creation code should be in init. You can just reuse the same sprite. Also, there's CCFadeIn you can use instead of CCTintTo.

How to position a UIView?

I spent a couple of hours trying to position a UIView and eventually figured out that I needed to modify the views frame. So I added a 'setPosition' method to the UIViewController subclass like this
- (void) setPosition:(CGPoint)position
{
CGRect newFrame = self.view.frame;
newFrame.origin.x = position.x;
newFrame.origin.y = position.y;
self.view.frame = newFrame;
}
However, that seems so simple that I don't understand why UIViews don't have this method already, which makes me think this might not be the right way to do it. So that's my question...
Is this method OK or am I doing something I shouldn't be doing... for some reason?
Copying, Modifying and setting the frame again like you have done here is how this is generally done. This can also be done by creating a rect and setting it directly:
UIView.frame = CGRectMake(50,50,50,50);//x,y,w,h
Doing this in an animation block will animate these changes.
Alternitively you can set a views Center point with :
UIView.center = CGPointMake(50,50);
Other way:
CGPoint position = CGPointMake(100,30);
[self setFrame:(CGRect){.origin = position,.size = self.frame.size}];
This i save size parameters and change origin only.