SpriteKit iOS 7 Sknode not being removed - sprite-kit

I am developing a game for iOS 7, and it uses Spritekit.
The main skspritenode is a helicopter, and when this helicopter makes contact with a shrink bonus pickup, the helicopter shrinks to make navigation easier for the user. Here is the code for shrinking the helicopter that works as expected (self refers to the helicopter skspritenode):
SKAction *scaleHeli = [SKAction scaleBy:.70 duration:1];
SKAction *setPhysicsBody = [SKAction runBlock:^{
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(self.size.width, self.size.height)];
self.physicsBody.mass = 0.080944441;
self.physicsBody.restitution = 1.0f;
self.physicsBody.allowsRotation = FALSE;
self.physicsBody.categoryBitMask = APAColliderTypeShip;
self.physicsBody.collisionBitMask = APAColliderTypeCavePiece;
self.physicsBody.contactTestBitMask = APAColliderTypeCavePiece | APAColliderTypeBonusPickup;
}];
SKAction *seq = [SKAction sequence:#[scaleHeli, setPhysicsBody]];
[self runAction:seq];
_shrinkEnabled = true;
I have a timer setup for the shrink bonus that lasts for a predetermined amount of time:
-(void)startShrinkTimer
{
//Creating the timer for the shield
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countDownShrinkDuration) userInfo:Nil repeats:true];
_timerSeconds = 15;
}
-(void)countDownShrinkDuration
{
if(_timerSeconds > 0)
{
--_timerSeconds;
}
else if(_timerSeconds == 0)
{
[_sceneRef disableShrinkBonus];
[_timer invalidate];
}
if(_timerSeconds < 5)
{
[_sceneRef shrinkWearingOut];
}
}
If the timer gets to be less than 5 seconds then the shrinkWearingOut function is called, which adds a label to a node that fades in and out (self refers to the helicopter skspritenode):
SKLabelNode *downLbl = [[SKLabelNode alloc] init];
downLbl.text = #"↓";
downLbl.fontColor = [UIColor whiteColor];
downLbl.fontSize = 9.0f;
downLbl.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft;
downLbl.position = CGPointMake(0,-30);
downLbl.zPosition = 5;
SKAction *fadeOut = [SKAction fadeOutWithDuration: .2];
SKAction *fadeIn = [SKAction fadeInWithDuration: .2];
SKAction *pulse = [SKAction sequence:#[fadeOut,fadeIn]];
SKAction *pulseForever = [SKAction repeatActionForever:pulse];
SKSpriteNode *wearingOut = [[SKSpriteNode alloc] init];
wearingOut.name = #"wearingOut";
[wearingOut addChild:downLbl];
[wearingOut runAction:pulseForever];
[self addChild:wearingOut];
Once the timer reaches zero, the disableShrink function is called, which attempts to remove the pulsating node and scale the helicopter to its original size (self refers to the helicopter skspritenode):
[[self childNodeWithName:#"wearingOut"] removeFromParent];
SKAction *scaleHeli = [SKAction scaleBy:1.3 duration:1];
SKAction *setPhysicsBody = [SKAction runBlock:^{
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(self.size.width, self.size.height)];
self.physicsBody.mass = 0.080944441;
self.physicsBody.restitution = 0.0f;
self.physicsBody.allowsRotation = FALSE;
self.physicsBody.categoryBitMask = APAColliderTypeShip;
self.physicsBody.collisionBitMask = APAColliderTypeCavePiece;
self.physicsBody.contactTestBitMask = APAColliderTypeCavePiece | APAColliderTypeBonusPickup;
_shrinkEnabled = false;
}];
SKAction *seq = [SKAction sequence:#[scaleHeli, setPhysicsBody]];
[self runAction:seq];
Problem # 1: This line of code:
[[self childNodeWithName:#"wearingOut"] removeFromParent];
does not actually remove the pulsating node from the helicopter node, I have set a breakpoint here and verified that the node is not nil, yet it still seems to not remove the node. Very frustrating as I have used the exact same line of code with a different bonus pickup with a different name and it works just fine. The only difference is the other bonus pickup is using an skshapenode, either way they both inherit from sknode, so they should both function the same.
Problem # 2:The pulsating node is added successfully, but only pulses a couple of times, which I find strange because I use the exact set of skactions on a different bonus and that bonus pulses until it is removed from the helicopter node.

Observations:
You should not use NSTimer. Instead use the update: method of SKScene to perform timed actions and if needed forward it to child nodes that need it. Or use SKAction runBlock sequenced with waitForDuration. NSTimer may not run synchronous with Sprite Kit updates / render loop, and they won't stop firing if a node or the scene is paused.
Check that you don't have multiple child nodes with the name "wearingOut". Sounds like you may have added more than one. The use of NSTimer may actually cause strange behavior because Sprite Kit may delay adding/removing nodes from the scene and so timing of add/remove of nodes may be an issue. This is a wild guess though.
The pulsing issue may also indicate multiple sprites, perhaps they pulsate asynchronously so they appear as not pulsating. Alternatively try the fadeAlphaTo: actions.
In general try using the documented initializers. There may be issues if you do just [[SKLabelNode alloc] init]. I ran into some issues with [SKSpriteNode node], not drawing for instance even after setting a texture. Instead do [SKLabelNode labelNodeWithFontNamed:#"Arial"] or any of the other designated initializers. Refer to the Sprite Kit Framework reference.

Related

Drawing line starting from SKSpriteNode_01.pos - ending at SKSpriteNode_02.pos (and adding particles)

Hi All I am wondering what is the best approach to draw a particlesystem line between two sprites (which updates if the sprite positions moved nudged/gravity etc)
Ive tried this way (ps sorry for my newbie code ><)
drawing the line starting from obj01 - ending at obj02
that is working so far but I im not sure how to update the line coordinates if the sprites move for example
the other problem related probably because the pathToDraw is not updated the particle system seems to drift off
So I know I need to update it (if someone could write a sudo code/idea of where I would expect to init the path to draw, where to remove etc that would really be very helpful in figuring out)
with thanks N :)
-(void)drawLineFromObj2:(EnemyClass*)obj03 to:(EnemyClass*)obj04
{
if (UFO02IsDead == NO && UFO03IsDead == NO)
{
if (ufo_02ReadyToLink == YES && ufo_03ReadyToLink == YES) {
NSLog(#"Line from Obj2 to obj03");
// **CreateLine**
lineNode02.path = pathToDraw;
lineNode02 = [SKShapeNode node];
CGMutablePathRef pathToDraw = CGPathCreateMutable();
CGPathMoveToPoint(pathToDraw, NULL, obj03.position.x, obj03.position.y);
CGPathAddLineToPoint(pathToDraw, NULL, obj04.position.x, obj04.position.y);
lineNode02.path = pathToDraw;
//Add Particles
NSString *myParticlePath = [[NSBundle mainBundle] pathForResource:#"ForceField" ofType:#"sks"];
SKEmitterNode *myParticle = [NSKeyedUnarchiver unarchiveObjectWithFile:myParticlePath];
[self addChild:myParticle];
//get particles to drop by adding physics?
(this no effect the particles don't follow the line and don't seem
attached to it, and if the sprites move they dot change position either)
//myParticle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.frame.size];
//myParticle.physicsBody.affectedByGravity = YES;
//myParticle.physicsBody.allowsRotation = NO;
pathToDraw
myParticle uses SKAction followPath:pathToDraw
How to update pathToDraw to draw smooth lines and remove last line properly
SKAction *followTrack =
[SKAction followPath:pathToDraw asOffset:NO orientToPath:YES duration:.5];
SKAction *forever = [SKAction repeatActionForever:followTrack];
myParticle.particleAction = forever;
lineNode02.name = #"lineNode";
[self addChild:lineNode02];
//is there a way to link the movement with the crystal positions and animate the line length and angle (is there another way of doing this)
//stops line being redrawn (but how to update when sprite is moved, and how to delete the old lines?)
ufo_03ReadyToLink = NO;
}
}
}
Fixed it by moving the function down into
-(void)update
created an action block
SKAction *remove = [SKAction removeFromParent];
SKAction* blockAction = [SKAction runBlock:^
{
[self DrawLine]; //draws new line every update
//then removes line
}];
[self runAction:[SKAction sequence:#[blockAction,remove]]];
also used a texture rather than a particle but this is how I attached it
//Add Particles
NSString *myParticlePath =
[[NSBundle mainBundle] pathForResource:#"ForceField" ofType:#"sks"];
myParticle = [NSKeyedUnarchiver unarchiveObjectWithFile:myParticlePath];
[self addChild:myParticle];
SKAction *followTrack =
[SKAction followPath:pathToDraw asOffset:NO orientToPath:YES duration:.5];
SKAction *forever = [SKAction repeatActionForever:followTrack];
myParticle.particleAction = forever;
lineNode01.name = #"lineNode";
[self addChild:lineNode01];
Hope it helps someone else :)

SpriteKit SKAction group is running, but the SKSpriteNode image isn't being affected

I have created an SKAction group that alters the SKSpriteNode image (alpha and rotation) whilst playing a sound effect. However, when I runAction: the sound effect is played, but the image remains fixed.
In my subclassed SKSpriteNode (The Sheep) I have the following code that adds another SKSpriteNode (The Food) as a child:
-(void)setupEatingAction
{
_grassEating = [SKSpriteNode spriteNodeWithImageNamed:#"SheepFood"];
_grassEating.size = self.size;
_grassEating.anchorPoint = CGPointMake(0.5, 0.5);
_grassEating.alpha = 0.5;
_grassEating.zPosition = -1;
SKAction *fadeIn = [SKAction fadeInWithDuration:0.01];
SKAction *soundFX = [SKAction playSoundFileNamed:#"Munching2s.mp3" waitForCompletion:NO];
SKAction *rotate = [SKAction sequence:#[[SKAction rotateToAngle:degsToRads(-20.0) duration:kMovementAnimationInterval/4.0],[SKAction rotateToAngle:degsToRads(0.0) duration:kMovementAnimationInterval/4.0]]];
SKAction *eatAction = [SKAction group:#[fadeIn,soundFX,rotate]];
_grassEating.userData = [NSMutableDictionary dictionaryWithObject:eatAction forKey:KEY_EatAction];
[self addChild:_grassEating];
}
Then when I want the action group to run, I do the following:
-(void)eat
{
if(![_grassEating actionForKey:KEY_EatAction])
{
[_grassEating runAction:[_grassEating.userData objectForKey:KEY_EatAction] withKey:KEY_EatAction];
}
}
_grassEating is a property of my parent node:
#property (nonatomic,retain) SKSpriteNode *grassEating;
As I said, I can hear the sound effect playing, but the image isn't changed?!? For the life of me I can't work out why, and I've spent a lot of time trying to work out what is going on... any ideas?
I should also point out (although I don't think is anything to do with it) that this problem appears in both simulator and on hardware - both running iOS7.
I don't know if this is related iOS Spritekit Cannot Run SKAction on a Subclassed SKSpriteNode
But, it's the closest I can find and doesn't appear to be the same problem (on the surface).
Found the problem. It was a logical issue. The SKSpriteNode was being added and the sound FX started playing, but in the next frame or so the sprite was being removed.
As a result, it appeared that the sprite wasn't being displayed, but didn't have time to move into view. The sound FX continued to play, hence the confusion.

Emitted particles follow parent node

I am setting up an SKNode as follows:
- (id)init {
self = [super init];
if (self) {
SKTextureAtlas *atlas = [SKTextureAtlas atlasNamed:#"Sprites"];
SKSpriteNode *spriteNode = [SKSpriteNode spriteNodeWithTexture:[atlas textureNamed:#"Cat"]];
[self addChild:spriteNode];
// Create particle trail
SKEmitterNode *emitterNode = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:#"Hearts" ofType:#"sks"]];
emitterNode.position = CGPointMake(0, 20);
[self insertChild:emitterNode atIndex:0];
// Setting up the physics
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spriteNode.frame.size];
self.physicsBody.dynamic = YES;
self.physicsBody.affectedByGravity = NO;
}
return self;
}
When this node moves around horizontally along the X-axis, the source of the particle trail moves along with it, which is expected. What is not expected is that emitted particles do this as well, when instead I expect them to move straight up along the Y-axis from their original X position.
Is there a way to prevent this behaviour?
Creation (in scene):
SPAPlayer *player = [[SPAPlayer alloc] init];
player.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:player];
Movement code:
[node.physicsBody applyForce:CGVectorMake(20 * data.acceleration.x, 0)];
Image for reference:
Make the emitter a child of the sprite (as you already have).
To make the particles move independently from where the player node is, assign the SKEmitterNode's targetNode property, preferably assign it the scene as in:
// be sure self.scene isn't still nil when you do this, for instance in node's init
emitter.targetNode = self.scene;
This will spawn the particles at the player sprite position but they won't travel along with it.

Apply Angular Impulse

Here is my code for the player and the ball that interact with each other. What I want to do is to apply force to the ball like if my player is shooting it. I want the ball to move away from my player with force. how can I apply Impulse or force to this. I have tried many times but I am a newbie with Sprite Kit.
- (void) Player {
_Player = [SKSpriteNode spriteNodeWithImageNamed:#"player1"];
_Player.xScale = 0.09;
_Player.yScale = 0.09;
_Player.position = CGPointMake(self.size.width/4, self.size.height/2);
_Player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_Player.size];
_Player.physicsBody.dynamic = NO;
[self addChild:_Player];
}
- (void) TheMethodForBall {
SKSpriteNode *sprites = [SKSpriteNode spriteNodeWithImageNamed:#"ball"];
sprites.xScale = 0.19;
sprites.yScale = 0.19;
sprites.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprites.size];
sprites.physicsBody.dynamic = YES;
sprites.physicsBody.affectedByGravity = NO;
sprites.physicsBody.allowsRotation = YES;
sprites.physicsBody.restitution = YES;
sprites.physicsBody.angularVelocity = 4;
sprites.physicsBody.usesPreciseCollisionDetection = YES;
[self addChild:sprites];
}
I think you want to apply an impulse like a kick ?
You need the following, maybe when you touch the screen / or a button
[_myBall.physicsBody applyImpulse:CGVectorMake(somePowerX, somePowerY)];
Here is another post that will help get you started
Also, this is a good tutorial for beginners.

Randomizing SKActions

I'm trying to move a Sprite over the screen from left to right. The sprite should start at a random y position on the right side offscreen. With a repeatActionForever and an integer with a random Y number i want the sprite to repeat the action starting from different y Positions. Any ideas on how to achieve this besides putting the random int in an update method? Is it anyhow achieveable through actions?
I use this method on the Sprite:
int randomY = (arc4random()%121;
SKAction *pos = [SKAction moveTo:CGPointMake((STAGESIZE.width+(self.size.width/2)),randomY) duration:0];
SKAction *move = [SKAction moveToX:0-self.size.width/2 duration:3];
SKAction *wait = [SKAction waitForDuration:1 withRange:5];
SKAction *sequence = [SKAction sequence:#[pos,move,wait]];
SKAction *repeater = [SKAction repeatActionForever:sequence];
[self runAction:repeater];
There is no way to randomise standard actions once defined and running.
But there is a workaround you can use to achieve desired effect using [customActionWithDuration].1
SKAction* randomPositionAction = [SKAction customActionWithDuration:0 actionBlock:^(SKNode *node,CGFloat elapsedTime){
int randomY = arc4random_uniform(121);
//Set position instead of running action with duration 0
[node setPosition:CGPointMake((STAGESIZE.width+(self.size.width/2)),randomY)];
}];
RandomY is randomised every time action is run and position is set according to that.