How safe is it to call a runAction from within a CCCallBlock?
My code is:
CCSprite *sprite1;
CCSprite *sprite2;
...Some sprite init stuff...
[sprite1 runAction:
[CCSequence actions:
[CCScaleTo actionWithDuration:1.0 scale:2.0],
[CCCallBlock actionWithBlock:^{
[sprite2 runAction:[CCScaleTo actionWithDuration:5.0 scale:2.0]
}],
[CCScaleTo actionWithDuration:1.0 scale:1.0],
nil
]
];
Note that the sprite1 animation will end before the sprite2 animation fired in the CCCallBlock will end. It crashes but I don't understand why. So, is it safe to make such a call (seems not, at least doing like that)? Why? How would it be safe(r)?
Btw, having my sprite2 animation shorter than sprite1 one do not crash, so I suspect my second animation to be release somewhere but I don't enter its dealloc()...
Thx.
The code you wrote doesn't compile FYI.
What you are doing works fine. I just tried it after fixing the code you posting with the compile error. Here is what I used:
CCSprite *sprite1 = [CCSprite spriteWithFile:#"temp.png"];
CCSprite *sprite2 = [CCSprite spriteWithFile:#"temp.png"];
[sprite1 runAction:[CCSequence actions:
[CCScaleTo actionWithDuration:1.0 scale:2.0],
[CCCallBlock actionWithBlock:^
{
[sprite2 runAction:[CCScaleTo actionWithDuration:5.0 scale:2.0]];
}],
[CCScaleTo actionWithDuration:1.0 scale:1.0],
nil]
];
[self addChild:sprite1];
[self addChild:sprite2];
Related
Im pretty new to coding but I'm starting to get the hang of the basics.
how to make an image stay in its new position after an animation?
Example:
I'm giving an animating object a random position, however, the animation causes the object not to animate at the random position, but instead animate at the position it was given in the view controller. This also happens when I animate a completly different object.
Code I used:
int Random1x;
int Random1y;
IBOutlet UIButton *Start;
IBOutlet UIImageview *Object2;
-(void)ObjectMoving;
-(void)Object2Animate;
-(IBAction)Start:(id)sender{
[self ObjectMoving];
[self Object2Animate];
}
-(void)Object2Animate {
Object2.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"2.png"],
[UIImage imageNamed: #"3.png"],
[UIImage imageNamed: #"4.png"],
[UIImage imageNamed: #"1.png"], nil];
Object2.animationDuration = .5
[Object2 setanimationRepeatCount: 0]
[Object2 startAnimating];
}
-(void)ObjectMoving {
Random1y = arc4random() % 466;
Random1y = Random1y + 60;
Random1x = arc4random() % 288;
Object2.center = CGPointMake(Random1x, Random1y);
}
I'd greatly appreciate help, thank you!
If you go to your story board file and click on the View Controller and then file inspector you will see a box for Auto Layout
Post back if that worked.
If you do need to use auto layout then you would have to figure out a different way of moving the image.
My app situation:
I am now having an app displaying a dynamic picture. And I want to capture its current image and save it to disc.
My goal:
I am working on an animated visual effect: add an image view on top, and then animates it from its origin frame into CGRecrZero. Finally, remove this image view from vie hierarchy after animation is done.
Now problem:
First time triggerring the action, it seems work. But when trigger it at the second time, the image view does not animate and stay atop of all subviews.
Codes of the view controller(with ARC):
Firstly, this is the IBAction to trigger the snapshot:
- (IBAction)actionSaveSnapshot:(id)sender
{
... // Save the image file. This works everytime.
NSThread *tempThread = [[NSThread alloc] initWithTarget:self
selector:#selector(threadSimulateScreenCapture:)
object:nil];
if (tempThread)
{
[tempThread start];
tempThread = nil; // Simply release it
}
}
And here is the thread:
- (void)threadSimulateScreenCapture:(id)arg
{#autoreleasepool {
NSImageView __strong *subImageView;
subImageView = [[NSImageView alloc] init];
NSRect subRect = [_imageView frame]; // the dynamic image view which I want to snapshot with
const CGFloat animationTime = 0.4;
[subImageView setWantsLayer:YES];
[subImageView setImageScaling:NSImageScaleAxesIndependently];
[subImageView setImage:[_imageView image]];
dispatch_async(dispatch_get_main_queue(), ^{
[CATransaction begin];
[self.view addSubview:subImageView];
[subImageView setFrame:subRect];
[subImageView setNeedsDisplay:YES];
[CATransaction commit];
});
[NSThread sleepForTimeInterval:0.01];
dispatch_async(dispatch_get_main_queue(), ^{
[CATransaction begin];
[CATransaction setAnimationDuration:animationTime];
[subImageView.layer setFrame:CGRectZero];
[CATransaction commit];
});
[NSThread sleepForTimeInterval:(NSTimeInterval)animationTime]; // wait for end of animation
dispatch_async(dispatch_get_main_queue(), ^{
[subImageView removeFromSuperview];
});
NSLog(#"SubView Count: %#\n%#", self.view.subviews, subImageView); // MARK. described below
subImageView = nil; // release the image
}} // ENDS: thread and aotureleasepool
Every time the program runs to the "MARK" line and print the subview array, it is clear thar the subImageView is not removed from superview then. And every time the thread ends, a "deleted thread with uncommitted CATransaction ...." raised.
What wrong did I do?
I may solved my own problem: At the line with [subImageView setFrame:subRect];, insert another line with: [subImageView.layer setFrame:subRect];
Meanwhile, add another line before the "addSubview:" method: [self.view setWantsLayer:YES];.
It seemed work. But I do not know why. Why I should set both view and layer's frame?
Is there any way you could send 2 parameters with RunAction?
You see im trying to move a sprite with a label on top, and I have made separate functions for each. Similar to this.
[sprite runAction:
[CCSequence actions:actionMove, actionMoveDone, nil]];
id actionMoveDone = [CCCallFuncN actionWithTarget:self
selector:#selector(spriteLabelMoveFinished:)];
Now, I've got 2 questions,
1-Is there any way to send 2 or more parameters????
2-I was wondering if there's any way to save some memory and do both with one action?
- (void) spriteMoveFinished:(id)sender
{
CCLOG(#"Sprite move finished");
Sprites *sprite = (Sprites *)sender;
[self animateSprite:sprite];
}
- (void) animateSprite:(Sprites *)zprite
{
CCLOG(#"We're animating sprite"):
Sprites *sprite = nil;
sprite = zprite;
int actualDuration = sprite.speed; //property of sprite
// Create the actions
id actionMove = [CCMoveBy actionWithDuration:actualDuration
position:ccpMult(ccpNormalize(ccpSub(_player.position,sprite.position)), 10)];
id actionMoveDone = [CCCallFuncN actionWithTarget:self
selector:#selector(spriteMoveFinished:)];
[sprite runAction:
[CCSequence actions:actionMove, actionMoveDone, nil]];
}
- (void) spriteLabelMoveFinished:(CCLabelTTF *)sender
{
[self animateSpriteLabel:sender];
}
-(void)animateEnemyHP:(CCLabelTTF *)zpriteLabel
{
CCLabelTTF *spriteLabel = nil;
spriteLabel = zpriteLabel;
int actualDuration = spriteSpeed; //another property
id actionMove = [CCMoveBy actionWithDuration:actualDuration
position:ccpMult(ccpNormalize(ccpSub(_player.position,spriteLabel.position)), 10)];
id actionMoveDone = [CCCallFuncN actionWithTarget:self
selector:#selector(spriteLabelMoveFinished:)];
[spriteLabel runAction:
[CCSequence actions:actionMove, actionMoveDone, nil]];
}
Now, this 4 functions are kind of obvious.
1-Move Sprite if sprite ended moving, we move it again.
2-Move Label towards the same position with the same speed, if the label finishes moving, we move it again.
They both go to the same place.
Is there a way to mix this 4 functions into 2?
If so, how can I send 2 parameters when the action finishes?
Thanks for your help and your time, have a great day!
To send more than one parameter, instead CCCallFunc you can use CCCallBlock and call a block of code. Inside the block, you can call your method (selector) with any parameters you want.
A simple example:
CCAction *actionMoveDone = [CCCallBlock actionWithBlock:^()
{
[self spriteMoveFinished:param1 withParam2:param2 andParam3:param3];
}];
And a better way is use "self" as block parameter, to prevent memory allocation:
CCAction *actionMoveDone = [CCCallBlockN actionWithBlock:^(CCNode *myNode)
{
[(MyClass*)myNode spriteMoveFinished:param1 withParam2:param2 andParam3:param3];
}];
try to simplify, that will undoubtedly save 'memory' ... I normally do this with a class that extends CCNode, for example my SoldierMapLayout class does. In the SoldierMapLayout node, i put in the soldier stance animation (idle, walking right-left-up-down), a health bar, an optional label, a 'HP hit' animation when appropriate, an 'XP gained' animation when appropriate, a 'poison cloud' over the head, etc... and i move the node when the whole thing needs to move. A single animation, with a single call back on move completion.
-(void)createSprite{
CCSprite *shotV = [CCSprite spriteWithFile:#"green.png"];
[self addChild:shotV z:1];
[shotVArray addObject:shotV];
NSlog(#"%#",shotV);
}
-(void)aSpecialCase{
[self removeChild:[shotVArray lastObject] cleanup:YES];
}
I'm not getting this to work.
The function "createSprite" spams out sprites. "In aSpecialCase" I want to remove the last sprite that was created. Also hoping that removing it will end the current CCSequence for that instance.
-(void)aSpecialCase{
[self removeChild:[shotVArray lastObject] cleanup:YES];
}
This only removes the sprite from layer.. Doesn't remove it from array itself...
So better way is..
-(void)aSpecialCase{
CCSprite *sprite = [pshotVArray lastObject];
[self removeChild:sprite cleanup:YES];
[pshotVArray removeObject:sprite];
}
Hope this helps.. :)
I have a cocos2d based iphone app with a problem. When the user pauses the game and then hits the resume button, some CCSprites will disappear from the screen.
This behavior is random, no pattern followed. I just know that this only happens when the user resumes the game.
Here is the code, what am I doing wrong?
I first thought it might be a memory management problem, but I never get any EXC_BAD_ACCESS when the user hits resume... So the sprites probably still exist.
The sprites are a property within an object I'll call "myobject".
In myObject.h I have:
#interface myObject : CCNode{
CCSprite *_sprite1,*_sprite2;
// some other code
}
#property (nonatomic,retain) CCSprite *sprite1,*sprite2;
And in myObject.m file:
#synthesize sprite1=_sprite1;
#synthesize sprite2=_sprite2;
+(id)create:(CCLayer*)scene{
myobject.sprite1 = [CCSprite spriteWithFile:spriteFile];
[scene addChild:myobject.sprite1];
// same for sprite2
}
-(void) move:(ccTime)dt{
//SOMECODE
self.sprite1.position=ccp(self.x,self.y); // same for sprite2
}
then they get moved around with a function called on the myobject.
In the main scene, here is how these objects are created and moved around:
myObject *myObject;
NSMutableArray *_myObjects;
#implementation HelloWorld
+(id) scene
{
CCScene *scene = [CCScene node];
HelloWorld *layer = [HelloWorld node];
[scene addChild:layer z:0 tag:33];
return scene;
}
-(id) init
{
if( (self=[super init] )) {
_myObjects = [[NSMutableArray alloc] init];
// some other code
}
}
-(void) addObject(){
myObject=[myObject create:self];
[_fallingObjects addObject:fallingObject];
}
-(void) nextFrame:(ccTime) dt{
for(myObject *theObject in _myObjects){
[theObject move:dt];
}
}
// And here is the function that does the pause/unpause, here it is:
- (void) pauseGame{
if(pauseStatus==0){
[[CCDirector sharedDirector] pause];
pauseStatus=1;
// some code to display menu etc... such as:
[self addChild:pauseMenu z:10];
}
else{
[self removeChild:pauseMenu cleanup:YES];
[self removeChild:scoreLabel cleanup:YES];
[self removeChild:highscoreLabel cleanup:YES];
[self removeChild:titleLabel cleanup:YES];
[self removeChild:pauseLayer cleanup:YES];
[[CCDirector sharedDirector] resume];
pauseStatus=0;
}
}
==EDIT===
I have discovered that this problem is also true for sprites that I add directly to my scene, such as the sprite clown added as shown below:
CCSprite *clown;
#implementation HelloWorld
-(id) init
{
if( (self=[super init] )) {
// some code
clown = [[CCSprite spriteWithFile:#"clown.png"] retain];
[self addChild:clown z:2];
// some more code
}
}
#end
==END OF EDIT===
There's something odd about your setup. FallingObject is a CCNode that contains two sprites. Yet instead of adding the sprites to the FallingObject class (self), and then adding FallingObject to the scene, you are adding the sprites directly to the scene. This leaves the FallingObject node outside of the scene hierarchy.
That's also why you have to do things like updating sprite positions manually which would otherwise require no code at all:
self.sprite1.position=ccp(self.x,self.y);
For what it's worth, I suppose that due to the nonconformity of this setup you may be accidentally moving the sprites outside the screen, or the node either isn't properly paused or resumed.