I am trying to create an array of SKActions in Spritekit using objective-c so that two arrays execute in parallel. "rotation" is not an array which is fine with me, unless it is causing the issue which I doubt is the problem here. What I am expecting from this code is that actionMove and rotation will run in parallel and then it moves on to actionMove1 and actionMove2 and finishes the runAction. I am getting the following error in the last line of code shown below (added just the portion of the code needed).
Collection element of type 'SKAction *__strong [3]' is not an Objective-C object
SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX2, actualY2) duration:actualDuration];
SKAction * actionMove1 = [SKAction moveTo:CGPointMake(actualX3, actualY3) duration:actualDuration];
SKAction * actionMove2 = [SKAction moveTo:CGPointMake(actualX4, actualY4) duration:actualDuration];
int rotate = arc4random() % 5;
SKAction * rotation = [SKAction rotateByAngle:M_PI/rotate duration:0.5];
SKAction * moveArray[] = {actionMove, actionMove1, actionMove2};
[game_piece1 runAction:[SKAction group:#[moveArray, rotation]]];
I believe what you want is a combination of group: and sequence: actions. Groups will run together and sequence will wait until the previous action is finished.
SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX2, actualY2) duration:actualDuration];
SKAction * actionMove1 = [SKAction moveTo:CGPointMake(actualX3, actualY3) duration:actualDuration];
SKAction * actionMove2 = [SKAction moveTo:CGPointMake(actualX4, actualY4) duration:actualDuration];
int rotate = arc4random() % 5;
SKAction * rotation = [SKAction rotateByAngle:M_PI/rotate duration:0.5];
SKAction *firstStep = [SKAction group:#[actionMove, rotation]];
SKAction *sequence = [SKAction sequence:#[firstStep, actionMove1, actionMove2]];
[game_piece1 runAction:sequence];
You might find this link helpful Adding Actions to Nodes It does a good job of show different groups and sequences.
Hopefully that will give you the desired result.
Edit
If you are looking to run rotation while running move actions in oder it would look like this.
SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX2, actualY2) duration:actualDuration];
SKAction * actionMove1 = [SKAction moveTo:CGPointMake(actualX3, actualY3) duration:actualDuration];
SKAction * actionMove2 = [SKAction moveTo:CGPointMake(actualX4, actualY4) duration:actualDuration];
int rotate = arc4random() % 5;
SKAction * rotation = [SKAction rotateByAngle:M_PI/rotate duration:0.5];
SKAction *sequence = [SKAction sequence:#[actionMove, actionMove1, actionMove2]];
SKAction *group = [SKAction group:#[sequence, rotation]];
[game_piece1 runAction:group];
Related
The bellow code is not repeating inspite of explicitly setting the SKAction as repeatActionForever.
SKAction *action = [SKAction moveToY:self.frame.size.height + bullet.size.height duration:2];
SKAction *remove = [SKAction removeFromParent];
SKAction *fireForever = [SKAction repeatActionForever:[SKAction sequence:#[action,remove]]];
[bullet runAction:fireForever];
The bullet just fires once , when its expected to be fired every 1 second.
You are removing the bullet from the parent, that's why it's not firing again,
What you should do is create a method to create a bullet - fire it - destroy it, and create an SKAction that uses this method.
-(void)FireBullet
{
//create bullet
//fire it
//remove from parent
}
and add the following code when you want the bullets to start firing.
SKAction *myAction = [SKAction performSelector:#selector(FireBullet) onTarget:self];
SKAction *forever = [SKAction repeatActionForever:myAction];
[self runAction:forever];
Hope this helps, let me know if you need more help.
Ok I'm fairly new to coding with sprite kit so I don't know too much. I'm trying to make 5 bricks spawn above the scene and then slide down into the scene every 5 seconds. I thought this code would work but it does absolutely nothing to my scene.
I have another method that has bricks already loaded into the scene and that works so I don't think that would affect this much but not really sure.
-(void) spawnMoreBricks:(CGSize)size {
for (int i = 0; i < 5; i++) {
SKSpriteNode *brick = [SKSpriteNode spriteNodeWithImageNamed:#"brick"];
//resize bricks
brick.size = CGSizeMake(60, 30);
//position it
brick.position = CGPointMake(self.size.width/2, self.size.height);
SKAction *wait = [SKAction waitForDuration:5];
SKAction *spawn = [SKAction scaleTo:1 duration:0];
SKAction *move = [SKAction moveByX:0 y:-80 duration:3];
SKAction *spawnSequence = [SKAction sequence:#[wait, spawn, move]];
[self runAction:spawnSequence];
}
}
SKAction *wait = [SKAction waitForDuration:5];
SKAction *spawn = [SKAction runBlock:^{[self addChild: brick];
SKAction *move = [SKAction moveByX:0 y:-80 duration:3];
[brick runAction: move];
}];
SKAction *spawnSequence = [SKAction sequence:#[wait, spawn]];
[self runAction:spawnSequence];
Here is a modified set of actions to use. The problem was that you were running all the actions on the scene itself. The code provided runs a sequence on the scene that waits 5 seconds (acting as the delay), and then runs a spawn action. runBlock allows us to add the brick to the scene, create the move action, and apply that action to the brick (not the scene)
I have a sequence of SKActions that I would like to play but they do not play properly. The eye only closes and never opens again. I have no clue why this is occurring but maybe you do! Help is appreciated, thank you.
-(void)blink {
SKAction *delay = [SKAction waitForDuration:3];
SKAction *blinkEye = [SKAction resizeToHeight:1 duration:.2];
SKAction *delay2 = [SKAction waitForDuration:.5];
SKAction *openEye = [SKAction resizeToHeight:3 duration:.2];
SKAction *group1 = [SKAction group:#[delay,blinkEye]];
SKAction *group2 = [SKAction group:#[delay2,openEye]];
SKAction *all = [SKAction sequence:#[group1,group2]];
SKAction *repeat = [SKAction repeatActionForever:all];
[self runAction:repeat];
}
In MyScene.m
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
Player *player = (Player *)[self childNodeWithName:#"player"];
Player *lefteye = (Player *)[player childNodeWithName:#"leye"];
Player *righteye = (Player *)[player childNodeWithName:#"reye"];
[lefteye blink];
[righteye blink];
}
There's no need for grouping, it's causing the problem.
SKAction *delay = [SKAction waitForDuration:3];
SKAction *blinkEye = [SKAction resizeToHeight:1 duration:.2];
SKAction *delay2 = [SKAction waitForDuration:.5];
SKAction *openEye = [SKAction resizeToHeight:3 duration:.2];
SKAction *all = [SKAction sequence:#[delay,blinkEye, delay2,openEye]]];
And letting blink the eye on every frame update can't work. This function is called up to 60 times a second.
Instead do it like this on creation of the eye object:
SKAction *delay = [SKAction waitForDuration:3];
SKAction *blinkEye = [SKAction resizeToHeight:1 duration:.2];
SKAction *delay2 = [SKAction waitForDuration:.5];
SKAction *openEye = [SKAction resizeToHeight:3 duration:.2];
SKAction *all = [SKAction sequence:#[delay,blinkEye, delay2,openEye]]];
//create the lefteye object
lefteye = [[Eye alloc] init...];
//create the righteye object
righteye = [[Eye alloc] init...];
[lefteye repeatActionForever:all];
I use the following piece of code to generate SKNodes periodically. Is there a way to make the period of generation of these SKNodes random. Specifically, how do I make the "delayFish" in the following code an action with a random delay?
[self removeActionForKey:#"fishSpawn"];
SKAction* spawnFish = [SKAction performSelector:#selector(spawnLittleFishes) onTarget:self];
SKAction* delayFish = [SKAction waitForDuration:3.0/_moving.speed];
SKAction* spawnThenDelayFish = [SKAction sequence:#[spawnFish, delayFish]];
SKAction* spawnThenDelayFishForever = [SKAction repeatActionForever:spawnThenDelayFish];
[self runAction:spawnThenDelayFishForever withKey:#"fishSpawn"];
ObjC:
First set an average delay and range...
#define kAverageDelay 2.0
#define kDelayRange 1.0 // vary by plus or minus 0.5 seconds
and then change your delayFish action to this...
SKAction* delayFish = [SKAction waitForDuration:kAverageDelay withRange:kDelayRange];
Swift:
First set an average delay and range...
let averageDelay:TimeInterval = 2.0
let delayRange:TimeInterval = 1.0 // vary by plus or minus 0.5 seconds
and then change your delayFish action to this...
let delayFish = SKAction.wait(forDuration:averageDelay, withRange:delayRange)
Insert a random float instead of a fixed one.
In your case something like this:
double value = ((double)arc4random() / ARC4RANDOM_MAX)
* (maxValue - minValue)
+ minValue;
SKAction* delayFish = [SKAction waitForDuration:value/_moving.speed];
I see. This won't work in your case as repeatActionForever will run with the last created random value. Forever.
Maybe try this instead. I'm not sure if this works though:
SKAction* delayFish = [SKAction waitForDuration: (((double)arc4random() / ARC4RANDOM_MAX) * (maxValue - minValue)+ minValue)/_moving.speed];
I suggest making the random value an own method though.
-(double) getRandomValue(){
return (((double)arc4random() / ARC4RANDOM_MAX) * (maxValue - minValue)+ minValue);
}
EDIT:
Here is a link to a similar issue. Maybe that might help. Sorry!
SKAction *randomXMovement = [SKAction runBlock:^(void){
NSInteger xMovement = arc4random() % 20;
NSInteger leftOrRight = arc4random() % 2;
if (leftOrRight == 1) {
xMovement *= -1;
}
SKAction *moveX = [SKAction moveByX:xMovement y:0 duration:1.0];
[aSprite runAction:moveX];
}];
SKAction *wait = [SKAction waitForDuration:1.0];
SKAction *sequence = [SKAction sequence:#[randomXMovement, wait]];
SKAction *repeat = [SKAction repeatActionForever:sequence];
[aSprite runAction: repeat];
Source: SKAction: How to Animate Random Repeated Actions
I have two different SKSpriteNodes. The first is stationary and the second, I would like, to move around the first node. I have my second node is flying in from off screen with a zPosition of 11 (the first node has a zPosition of 10). When the second node flies to the opposite end of the screen, I have the node flip and fly back across. At this time I would like to change the zPosition to 9.
witch.position = CGPointMake(self.size.width + 200, self.size.height / 2 + 50);
witch.zPosition = 11;
[self addChild:witch];
SKAction *moveLeft = [SKAction moveToX:-200 duration:8];
SKAction *moveRight = [SKAction moveToX:self.size.width + 200 duration:8];
SKAction *moveDown = [SKAction moveToY:secondNode.position.y - 50 duration:0.3];
SKAction *moveUp = [SKAction moveToY:secondNode.position.y + 50 duration:0.3];
SKAction *flipRight = [SKAction scaleXTo:-1 duration:0];
SKAction *flipLeft = [SKAction scaleXTo:1 duration:0];
[secondNode runAction:
[SKAction repeatActionForever:
[SKAction sequence:
#[moveLeft, moveDown, flipRight, CHANGEZPOSITIONHERE, moveRight, moveUp, flipLeft, CHANGEZPOSITIONHERE]]]];
How would I go about changing the zPosition in the middle of an SKAction?
Thank you in advance for all suggestions and help.
This answer was found due to LearnCocos2D comment.
Adding a runBlock:action I was able to change the zPosition. The final code is:
[secondNode runAction:
[SKAction repeatActionForever:
[SKAction sequence:
#[moveLeft, moveDown, flipRight,
[SKAction runBlock:(dispatch_block_t)^(){
secondNode.zPosition = 9;
}], moveRight, moveUp, flipLeft,
[SKAction runBlock:(dispatch_block_t)^(){
secondNode.zPosition = 11;
}]]]]];