Is it possible to end an SKAction mid-action? - ios

I have a subclass of SKSpriteNode (monsterNode). It automatically runs around the screen using vectors to follow the player. I am currently using the following action to make it run around:
SKAction *actionMove = [SKAction moveTo:actualDistance duration:time];
[self runAction:actionMove completion:^ {
_currentState = SVGMonsterStateIdle;
}];
I am wondering if its possible to make it so the monsterNode actually STOPS running the action if it hits the boundary of the iOS device screen. I currently have SKSpriteNode boundaries on the edges of the screen, linked with a contact delegate to notify if the monster and walls make contact. However, that means nothing if I can't actually stop the monster's actionMove action from going to completion. The monster needs to stop at the boundaries of the screen. If it is not possible to stop an SKAction mid-execution, is there a roundabout way to do so?

Look at the SKNode.h header file - it has two functions listed:
- (void)removeActionForKey:(NSString *)key;
- (void)removeAllActions;
The latter will work: [monsterNode removeAllActions];

Related

Presenting previous scene makes it partillay unresponsive

A SpriteKit game presents MainMenuScene then LevelSelectionScene and finally the GamePlayScene. Once the game is over, in GamePlayScene, the user is transferred back to MainMenuScene like so:
MainMenuScene *newScene = [MainMenuScene sceneWithSize:self.view.bounds.size];
[self.view presentScene:newScene transition:[SKTransition fadeWithDuration:0.5]];
It takes me to the scene but the buttons, that worked previously, don't work anymore. I press them, they seem to do their little animation as if they're being pressed but they don't present me appropriate scenes they're supposed to.
I have a very specific bundle of code activated every time a scene is about to be left and I don't actually know if it influences this whole conundrum:
-(void)willMoveFromView:(SKView *)view
{
/* Remove anything stuck in memory. */
[self removeAllActions];
[self removeAllChildren];
[self removeFromParent];
[self.scene removeAllActions];
[self.scene removeAllChildren];
[self.scene removeFromParent];
//[self.scene.view removeFromSuperview]; <--TOTALLY DESTROYS ALLS SCENES IN THE GAME. Don't use it.
}
Has any had previously working scenes presented as partially working duds?
From what you said looks like the button is triggering the animation but not the action, still not much information to be sure how to exactly fix it, probably the creation of the actions of the button is working. Double check if the part of the code that creates the SKActions is actually running and active.
So I poked around and found out the culprit is in the class I used to create buttons. It's called AGSpriteButton and I highly recommend it (You can find it on github). It has built-in SELECTORS, ACTIONS and BLOCKS functionality offered by default that is very easy to tap into. I'm not sure as to why, but SELECTORS stopped functioning correctly. I just switched to ACTIONS calling my methods & everything works perfectly.

Cocos2d V3 and Spritebuilder - Timeline animation not running subsequent times

I have a CCB file with a timeline animation in it. I load the file like this.
CCSprite *spriteAnimation = (CCSprite*)[CCBReader load:#"MyGreatAnimation"];
spriteAnimation.paused = TRUE;
At some point later, I add it to the scene and run the animation
[MyScene addChild:spriteAnimation];
CCAnimationManager* animationManager = _deletionAnimaion.userObject;
[animationManager runAnimationsForSequenceNamed:#"Default Timeline"];
This is great. My animation runs. I then remove spriteAnimation from the scene until I need it again.
[spriteAnimation removeFromParent];
.
The problem
I can't figure out how to get the animation to run the next time I add it to the scene.
I've tried:
[animationManager jumpToSequenceNamed:#"Default Timeline" time:0];
And also..
[animationManager runAnimationsForSequenceNamed:#"Default Timeline"];
But the animation doesn't seem to run. If at this point I call:
spriteAnimation.userObject.runningSequenceName
to see the running sequence, it returns NULL.
.
My Question
How do I arbitrarily run a timeline animation repeatedly?
To be clear, I'm not asking about looping the animation. I want to start it from frame 1 whenever I need.
If you need it repeatedly, you shouldn't remove it from the scene:
[spriteAnimation removeFromParent];
Instead, just make the animation invisible for the time being:
spriteAnimation.visible = NO;
Later just make it visible again.
After all when you remove a node it is gone from the scene hierarchy unless you addChild: it back in which I can't see in your code above.

How can setKey for value be used with SpriteKit?

I'm making a platform game with SpriteKit, and I'm trying to use the method setValue:forKey:
inside of the touchesMoved:withEvent method because I have a mess: I need to remove an animation of type SKAction. I run this SKAction animation in touchesMoved:withEvent with an if, checking all the needed conditions. One of these conditions is this:
if(NO ==[[self childNodeWithName:#"hero"] hasActions])
{
//code to run animation
}
I check if it has no actions, because I don't want to create more animations than needed.
The animation runs forever while I touch the screen with my finger, and it will remove only if I remove my finger from the screen, in the touchesEnded:withEvent method
So until I'm touching the screen, the hero animation will loop forever. The problem I have is if I move my finger from the right side of the screen to the left side of the screen, the animation is still the same: the hero running to the right, even if the sprite is moving to the left, because the if check if there is actions, and don't create the new animation to the left, and of course don't remove the right animation.
I think I can fix it setting a key for the animation action, and removing it in the same method with an if and using the location of the finger. The problem is that I don't know how to use the removeActionForKey method. I've tried reading documentation but I didn't find it very clear. Can anyone post me some code example of how to use it?
You need to use runAction:withKey: method:
// if direction == right
[node removeActionForKey:#"left"];
SKAction *animationRight = [SKAction animateWithTextures:......];
[node runAction:animationRight withKey:#"right"];
...........
// if direction == left
[node removeActionForKey:#"right"];
SKAction *animationLeft = [SKAction animateWithTextures:......];
[node runAction:animationLeft withKey:#"left"];

CCSpriteBatchNode not being retained?

I have an app that uses sprite sheets and is a Cocos2D/UIKit app. In the init method of my scene I do this:
spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"TheSpriteSheet.pvr.ccz"];
[self addChild:spriteSheet z:1];
Now when the game is over, I do this:
[[[CCDirector sharedDirector] actionManager] removeAllActions];
[self unscheduleAllSelectors];
This causes the CCSprites that are currently still as a child to remain there so once I finish my UIKit view transition to my other UIViewController I try to call a method back in my CCLayer class (the class the scene is in) to remove all the children that were in my CCSpriteBatchNode, the app crashes with EXC_BAD_ACCESS.
Now I HAVE to call the method at this point because I want to achieve the effect of the enemies still being on screen while I am doing my custom view transition so they don't magically disappear before I finish switching views. Also I do not do replaceScene anywhere or release my batch node explicitly so I don't know why this is happening.
Anyway how would I fix this issue?
Thanks!

Make CCSprite follow another CCSprite when touched?

What I want to accomplish is to make my CCSprite follow another CCSprite when it is touching it. Now what I mean by follow is, lets say there is an animation of another CCSprite moving up the screen. So if this sprite hits my main sprite, my main sprite should move up the screen with it. This other sprite will be a platform, so technically in the end I would want the sprite to be on top of the other sprite but it would be moving along the top of the platform CCSprite as if the platform was carrying the main sprite.
Now I know how to do the collision detection part and make the other sprite animate but how would I just make my platform 'carry' my main CCSprite also kind of like how an elevator works?
I think you can place flag to check the condition "when to follow" in update: method you can reposition the second sprite according to position of first Sprite.
-(void)update:(CCTime)delta
{
if(condition == YES)
{
secondSprite.position = ccp ( firstSprite.position.x + xOffSet , firstSprite.position.y + yOffSet);
}
}
Try this according to your code.. Basic logic is here..
You can use the CCFollow action to have any node follow any other node.
You can move the first sprite without using action, by manually updating his position in a nextFrame:(ccTime) dt kinda function, doing so would be easy to update another sprite position based on your sprite position plus an offset to position it on top.
Another solution could be removing the follower sprite from its parent and adding as a child of the moving node, i think this can be done without flickering and performance loss.
edit:
another solution (maybe the best one)
a good approach, that works great with Actions so you dont need to manually schedule movements
You can add a property to the moving sprite (you need to subclass from CCSprite, if you havent already done so), to keep a reference to the follower
#property (readwrite, nonatomic, assign) CCSprite *follower;
#property (readwrite, nonatomic) BOOL followerActive;
then in your game inits, you can create both sprites, and add them as childs of your main layer, then you add the weak reference from your object to the follower
platform.follower = followerSprite;
and when you need to enable the follow platform.followerActive = YES;
at this point in the moving sprite, you can override the setPosition property
-(void) setPosition:(CGPoint)position {
if(self.followerActive) {
self.follower.position = ccpAdd(position, offset);
}
[super setPosition:position];
}

Resources