Problems making a property from a SKAction (SpriteKit) - ios

Again, probably a newbie question. I'm following the instructions I found in these two links, since they solve exactly the problem I have (modify the speed of a SKAction from outside its own method):
How to change duration of executed SpriteKit action
How to run or execute an SKAction from outside of the object?
In my case, have this SKAction:
SKAction * moveBall = [SKAction moveToY:0 duration:1];
[ball runAction:moveBall withKey:#"ball falling"];
I create the property, like this:
#property SKAction * moveBall;
And then I want to call it from the touchesBegan after touching a button, like this:
if ([node.name isEqualToString:#"Slow Down Button"]) {
self.moveBall.speed = 0.5;
}
moveBall.speed inside its own method is 1.0, but self.moveBall.speed indicates a speed of 0.0 (same for _moveBall.speed), so the property declaration is not working correctly. I tried several things, but so far I couldn't find what is missing.
Thanks in advance!

This:
SKAction * moveBall = [SKAction moveToY:0 duration:1];
creates a local variable named moveBall. But your property is accessed through self.moveBall respetively directly through its auto-synthesized ivar named _moveBall.
The solution is one of these two:
self.moveBall = [SKAction moveToY:0 duration:1];
or
_moveBall = [SKAction moveToY:0 duration:1];
and then continue to use that variable for addChild: and other uses.

You most likely need to spend some time reading about properties and pointers. But it's possible that you just aren't showing all the code you are using. The phrase "inside its own method" is confused.
SKAction *moveBall = is not the same as _moveBall, unless you have assigned that pointer to the property. But you are using the same name for a pointer and a property in your code, confusing things further.
I suspect you need to change:
SKAction * moveBall = [SKAction moveToY:0 duration:1];
to
_moveBall = [SKAction moveToY:0 duration:1];
and then only use _moveBall throughout your code. But make sure you read up on pointers and properties and understand the difference.

Related

iOS Changing SKAction speed in repeating action

I've read in a few placing that changing the speed variable of an SKAction will change the speed; however, that doesn't seem to be working for me.
- (void)startAnimating {
SKAction *moveDown = [SKAction moveByX:0 y:-[CAUtilities screenSize].height duration:self.animationDuration];
[self setMoveDownAction:[SKAction repeatActionForever:moveDown]];
[self runAction:self.moveDownAction];
}
- (void)incAnimationSpeedBy:(CGFloat)aFloat {
self.moveDownAction.speed += 0.5;
NSLog(#"%f", self.moveDownAction.speed);
}
The actual value of self.moveDownAction.speed changes as seen in the NSLog call, but the actual animation doesn't change.
I have incAnimationSpeedBy: being called when the screen is tapped, so using a SKAction sequence with a runBlock won't work for my needs.
I've tried:
Having the initial moveDown as an instance variable.
Having the repeat forever action as an instance variable (seen above).
Changing the duration property rather than speed.
Any help is appreciated, thanks.
The problem was I was trying to change the speed on the different SKAction objects when I should have been changing the speed on the parent SKSpriteNode.
- (void)startAnimating {
SKAction *moveDown = [SKAction moveByX:0 y:-[CAUtilities screenSize].height duration:3.0];
[self runAction:[SKAction repeatActionForever:moveDown]];
}
- (void)incAnimationSpeedBy:(CGFloat)aFloat {
if (self.speed + aFloat < kMaxSpeed)
self.speed += aFloat;
}

Modify SKActions during the game

I am developing a game where all the enemies are SKNodes in their own classes. Within my SKScene I am spawning the mobs via allocing them and calling a specific method for spawning.
However, when they are spawned each mob are defined with a set of actions they run during their lifetime. One example are for one specific mob is:
SKAction *moveLeft = [SKAction moveToX:0 - (fragment.size.width/2) + (width / 2) duration:1.0];
SKAction *moveRight = [SKAction moveToX:(fragment.size.width / 2) - (width / 2) duration:1.0];
SKAction *sequence = [SKAction sequence:#[moveLeft, moveRight]];
SKAction *bounceOnWalls = [SKAction repeatActionForever:sequence];
[enemy runAction:bounceOnWalls];
So, back to my question. How can I modify a specific SKAction after its been created? I would like to change lets say the speed of moveLeft for all enemies that have this SKAction.
You can run action with key :
SKAction *moveLeft = [SKAction moveToX:0 - (fragment.size.width/2) + (width / 2) duration:1.0];
SKAction *moveRight = [SKAction moveToX:(fragment.size.width / 2) - (width / 2) duration:1.0];
SKAction *sequence = [SKAction sequence:#[moveLeft, moveRight]];
SKAction *bounceOnWalls = [SKAction repeatActionForever:sequence];
[enemy runAction:bounceOnWalls withKey:#"moving"]; //Run action with key
And when you need to change the speed on all nodes which running that action, you can use enumerateChildNodesWithName method. Like this:
[parentNode enumerateChildNodesWithName:name usingBlock:^(SKNode *node, BOOL *stop){
if([node actionForKey:#"moving"]){
SKAction* action = [node actionForKey:#"moving"];
action.speed = 1.5f;
}
}];
You could probably change dynamically duration of actions and affect in that way on speed of moving nodes, but I think that changing speed of an action directly is better choice. Take a look at both answers in this example on how you can change duration parameter dynamically.

Endless Action with SKActions Objective C

I am building a game with sprite kit and have a sprite moving from left to right with an endless action.
SKAction *moveRight = [SKAction moveByX:3.0 y:0 duration:3.5];
SKAction *moveLeft = [SKAction moveByX:-3.0 y:0 duration:3.5];
SKAction *reversedMoveRight = [moveRight reversedAction];
SKAction *reversedMoveLeft = [moveLeft reversedAction];
SKAction *completion = [SKAction runBlock:^{
SKAction *sequence = [SKAction sequence:#[moveRight, moveLeft, reversedMoveRight,reversedMoveLeft]];
SKAction *endlessAction = [SKAction repeatActionForever:sequence];
[snake runAction:endlessAction];
}];
[snake runAction:completion withKey:#"KeySnake"];
This works, but after a short period of time my game slows down. The CPU and memory usage continues to grow in the debug navigator in Xcode. I think the endless action is causing the problem, but I don't know any other way to move it constantly like I want to.
From your comment I understand you are calling
[snake runAction:completion withKey:#"KeySnake"];
inside the update method. This is the problem, infact you are creating and running a new action every frame.
Move the whole block of code (you showed in your question) inside a method that is called only once.
Example: here I also refactored the construction of your action and changed the x value (in the action) from 3.0 to 100.0
#import "GameScene.h"
#implementation GameScene
{
SKSpriteNode * _snake;
}
- (void)didMoveToView:(SKView *)view {
[self addSnake];
[self startSnakeMoving];
}
- (void)addSnake{
_snake = [SKSpriteNode spriteNodeWithImageNamed:#"Snake"];
_snake.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:_snake];
}
- (void)startSnakeMoving {
SKAction * moveRight = [SKAction moveByX:100.0 y:0 duration:3.5];
SKAction * sequence = [SKAction sequence:#[moveRight, moveRight.reversedAction, moveRight.reversedAction, moveRight]];
SKAction * endlessAction = [SKAction repeatActionForever:sequence];
[_snake runAction:endlessAction withKey:#"KeySnake"];
}
#end
Rather than making snake (whatever is snake) calling your endless action (that is in fact 'sequence' repeated forever), you should call 'sequence' via a CADisplayLink (which is a screen refresh), that is make to drive the rendering of anything (so game) at screen refresh frequency.

SKAction is not running after another action

I'm basically trying to code a "find the ball under the cups" game for practice.
So there is 3 cups, and one target to find. All using SKSpriteNode. The target is randomly a child of one cup, and follows rotations as the parent rotate around an SKNode.
Between each game, the program is supposed to show where is the target, by simply animate it up, then down. Here the sequence code :
//THE ANIMATIONS
SKAction *moveUp = [SKAction moveByX:0.0 y:100 duration:1];
SKAction *moveDown = [SKAction moveByX:0.0 y:-100 duration:1];
SKAction *wait = [SKAction waitForDuration:0.5];
_presentTargetSequence = [SKAction sequence:#[moveUp,wait,moveDown]];
And the method using it :
- (void) presentTarget
{
NSLog(#"presentTarget()");
[_target runAction:_presentTargetSequence completion:^{
_canMove = YES;
}];
}
The code works fine, but only the first time, after that, the method is called but never go through [_target runAction ...].
BUT it's working if the target does get in the rotation/swap.
So my question is : is there anything that can make a node ignore it run action method ? NSLog(#"presentTarget()") is called as I said, but not reaction.
I find that every time I'm confused about an action not running, it's because the object running the action is not currently in the node hierarchy for the scene. There is no error for this, it just doesn't run the action. So double check to make sure that the object is added to the current scene at the time you ask it to run the action.

SKAction scaleBy: duration: SpriteKit

I have a trouble trying to make one circle big and small using [SKAction scaleBy: duration:]
SKAction *scaleDown = [SKAction scaleBy:0.2 duration:1.8];
SKAction *scaleUp= [scaleDown reversedAction];
SKAction *fullScale = [SKAction sequence:#[scaleDown, scaleUp, scaleDown, scaleUp]];
[_circleChanging runAction:fullScale];
What I get is the circle becoming so small that disappears and then doesn't come back. It has to become small and then come back to his original size doing it 2 times.
Try:
SKAction *scaleDown = [SKAction scaleTo:0.2 duration:0.75];
SKAction *scaleUp= [SKAction scaleTo:1.0 duration:0.75];
SKAction *fullScale = [SKAction repeatActionForever:[SKAction sequence:#[scaleDown, scaleUp, scaleDown, scaleUp]]];
[_circleChanging runAction:fullScale];
Not all actions are reversible, and the reverse sometimes doesn't mean "go back to the original value".
If you check the documentation, the reverse action of scaleBy is actually scaling to -0.2 in your case. Just create a new scale action instead of reversing.
Also try making a copy of the actions for the 2nd use:
SKAction *fullScale = [SKAction sequence:
#[scaleDown, scaleUp, [scaleDown copy], [scaleUp copy]]];

Resources