spritekit SKAction.resizeToHeight is not repeating - ios

I want to increase the height of in touch events, it's same like stick hero is doing. I am trying following code
func changeHeight(){
let action = SKAction.resizeToHeight(self.leadherNode!.size.height+50, duration: 0.5);
let seq = SKAction.repeatActionForever(action)
self.leadherNode?.runAction(seq, withKey: "height")
}
but unfortunately it just increase the height of node for first time and it never repeats. How can I achieve this?

Changes to an argument of an SKAction after it has started will have no effect on the action. You will need to create a new action with the updated value at each step. Here's one way to do that:
Define and initialize the height and max height properties
var spriteHeight:CGFloat = 50.0;
let maxHeight:CGFloat = 500.0
Call this from didMoveToView
resizeToHeight()
This function creates an SKAction that resizes a sprite to a specific height. After the completion of the action, the function updates the height value and then calls itself.
func resizeToHeight() {
self.leadherNode?.runAction(
SKAction.resizeToHeight(self.spriteHeight, duration: 0.5),
completion:{
// Run only after the previous action has completed
self.spriteHeight += 50.0
if (self.spriteHeight <= self.maxHeight) {
self.resizeToHeight()
}
}
)
}

I dont know swift. But I can write objective-c version.
CGFloat currentSpriteSize;//Create private float
SKSpriteNode *sprite;//Create private sprite
currentSpriteSize = sprite.size.height;//Into your start method
//And your Action
SKAction *seq = [SKAction sequence:#[[SKAction runBlock:^{
currentSpriteSize += 50;
}], [SKAction resizeToHeight:currentSpriteSize duration:0.5]]];
[sprite runAction:[SKAction repeatActionForever:seq]];

Related

How do I use SpriteKit's SKAction to create a time interval for an SKSpriteNode?

I have a function, basketball. I want to be able to spawn my basketBall node at CGPoint(515,700), the top of the screen, in a regular time interval. The only method I knew that would accomplish waiting a few seconds was sleep(); however, sleep() apparently doesn't allow SpriteNodes to remain on screen, so I need an alternative.
I discovered NSTimeInterval, but I would prefer to refrain from importing Foundation. I think that SKAction allows time to pass through waitForDuration(), but I am very confused as to how that works. Can someone shed some light on SpriteKit's SKAction?
override func didMoveToView(view: SKView) {
let basketBall = SKSpriteNode(imageNamed: "basketball")
let waitForObjects = SKAction.waitForDuration(3)
let basketBallFalls = SKAction.runBlock({
self.Basketball()
})
let action = SKAction.sequence([waitForObjects, basketBallFalls])
SKAction.runAction(action, onChildWithName: "basketball")
SKAction.repeatActionForever(action)
//Basketball()
}
func Basketball(){
basketBall.position = CGPointMake(515, 700)
basketBall.size = CGSizeMake(50, 50)
basketBall.size = CGSize(width: 50.0, height: 50.0)
basketBall.zPosition = 10
basketBall.physicsBody = SKPhysicsBody(circleOfRadius: 25.0)
basketBall.physicsBody?.mass = 0.8
basketBall.physicsBody?.restitution = 0.6
basketBall.physicsBody?.dynamic = true
self.addChild(basketBall)
}
Try to use SKAction.waitForDuration(_:).
Ex. something like this:
let waitDuration = NSTimeInterval(arc4random_uniform(20))
let waitAction = SKAction.waitForDuration(waitDuration)
let ballAction = SKAction.runBlock(self.Basketball) //call your function
runAction(SKAction.sequence([waitAction, ballAction]))
There are two straightforward ways to spawn a new node at a regular interval. They both involve your scene.
The scene's update(_:) method is called every frame, and the argument is the current time. Add an NSTimeInterval property to your scene class which will store the last time you created a basketball. In update(_:), subtract the current time from the last spawn time. If the difference is greater than the interval that you want, spawn a new node and keep the new current time.
Your scene can also run actions, like any other node. SKAction has a waitForDuration(_:) method which creates an action that does, well, exactly what its name says. You can create another action using SKAction.runBlock(_:) which will perform the spawning. Then create a sequence action that runs the delay action followed by the spawn action. Wrap that in a repeatActionForever(_:), and finally tell your scene to run the repeater action.

How to update waitForDuration constant?

I have a flashing light that uses a SKAction sequence that hides and unhides a circle node. I want to be able to change the intervals at which it flashes based on two buttons. I declared a variable stdTime and I change it in the touchesBegan method but it's not working. What am I missing?
my didMoveToView:
let blink = SKAction.sequence([
SKAction.waitForDuration(stdTime),
SKAction.hide(),
SKAction.waitForDuration(stdTime),
SKAction.unhide()])
let blinkForever = SKAction.repeatActionForever(blink)
metronome!.runAction(blinkForever)
and my touchesBegan:
if upArrow!.containsPoint(location) {
stdTime = stdTime + 0.1
println("here: \(stdTime)")
}
waitForDuration takes in a NSTimeInterval and not a variable. So it takes whatever time that variable was set to at creation and does not refer back to the variable you used.
Depending on the result you are looking for this might help.
func startBlink(){
let blink = SKAction.sequence([
SKAction.waitForDuration(stdTime),
SKAction.hide(),
SKAction.waitForDuration(stdTime),
SKAction.unhide()])
let blinkForever = SKAction.repeatActionForever(blink)
metronome!.removeActionForKey("blink")
metronome!.runAction(blinkForever, withKey: "blink")
}
if upArrow!.containsPoint(location) {
stdTime = stdTime + 0.1
startBlink()
println("here: \(stdTime)")
}
Another option without interrupting the sequence is to do something like this
func startBlink(){
let blink = SKAction.sequence([
SKAction.waitForDuration(stdTime),
SKAction.hide(),
SKAction.waitForDuration(stdTime),
SKAction.unhide(),
SKAction.runBlock( {
self.startBlink()
})])
metronome?.runAction(blink, withKey: "blink")
}
if upArrow!.containsPoint(location) {
stdTime = stdTime + 0.1
println("here: \(stdTime)")
}
And recursively call the method on your own. That way each time it hits the end it will take the updated stdTime.

swift: SKSpriteNode not rotating in update function

I'm trying to rotate a bone image which is a SKSpriteNode. I created it and on update, I tried running a SKAction which would rotate it. This doesn't do anything. I even tried to decrease the zRotation each time on update by 10 degrees and still nothing happened. I'm sure that the application is not frozen as when I do a println on update, text keeps rolling. Also, other parts of the app are responding. Its just the bone thats not rotating.
Code:
Declaration on start of class
let bone = SKSpriteNode(imageNamed: "bone")
let rotateBone = SKAction.rotateByAngle(30, duration: 1)
Setting up at didMoveToView
bone.anchorPoint = CGPointMake(0.5, 0.5)
bone.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
bone.zRotation = CGFloat(arc4random())
self.addChild(bone)
Attempting to rotate
override func update(currentTime: NSTimeInterval)
{
bone.runAction(rotateBone)
}
You are re-running the action on every update (every frame):
override func update(currentTime: NSTimeInterval)
{
bone.runAction(rotateBone)
}
Actions run over time. Run it once and let it do its job. Restarting it repeatedly will only replace the previous action and essentially reset it.

iOS game moving object up and down

I am trying to create a game, something slightly similar to Jetpack Joyride. At certain points it will have an object that the player has to fly between - like the pipes on flappy birds. I have managed to create the pipe objects however I wish for the objects to move up and down on the screen, making it harder for the player to jump between. My code for placing the objects is:
// Maths
float availableSpace = HEIGHT(self) - HEIGHT(floor);
float maxVariance = availableSpace - (2*OBSTACLE_MIN_HEIGHT) - VERTICAL_GAP_SIZE;
float variance = [Math randomFloatBetween:0 and:maxVariance];
// Bottom object placement
float minBottomPosY = HEIGHT(floor) + OBSTACLE_MIN_HEIGHT - HEIGHT(self);
float bottomPosY = minBottomPosY + variance;
bottomPipe.position = CGPointMake(xPos,bottomPosY);
bottomPipe.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0,0, WIDTH(bottomPipe) , HEIGHT(bottomPipe))];
bottomPipe.physicsBody.categoryBitMask = blockBitMask;
bottomPipe.physicsBody.contactTestBitMask = playerBitMask;
// Top object placement
topPipe.position = CGPointMake(xPos,bottomPosY + HEIGHT(bottomPipe) + VERTICAL_GAP_SIZE);
topPipe.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0,0, WIDTH(topPipe), HEIGHT(topPipe))];
topPipe.physicsBody.categoryBitMask = blockBitMask;
topPipe.physicsBody.contactTestBitMask = playerBitMask;
How would I go about moving the objects up and down?
Thanks for any help :)
All such operations on a node are done using SKAction objects. Read up on the SKAction class here.
In your case, using an SKAction to move the nodes up and down would go something like this:
SKAction *actionMoveUp = [SKAction moveByX:0 y:20 duration:0.5];
SKAction *actionMoveDown = [actionMoveUp reversedAction];
SKAction *actionMoveUpDown = [SKAction sequence:#[actionMoveUp, actionMoveDown]];
SKAction *actionMoveDownRepeat = [SKAction repeatActionForever:actionMoveUpDown];
[bottomPipe runAction:actionMoveDownRepeat];
[topPipe runAction:actionMoveDownRepeat];
The code given above will make your top and bottom pipe go up and down by a factor of 20 pixels repeatedly.

How do you move an IOS SKSprite at a consistent speed

Playing around with SKSprites in the IOS SpriteKit, and I basically want to have a sprite randomly move a certain direction for a certain distance, then pick a new random direction and distance.. Simple enough, create a method that generates the random numbers then creates the animation and in the completion block of the animation have it callback the same routine.
THis does work, but it also keeps the animation from moving at the same velocity since the animations are all based on duration.. If the object has to move 100 it moves at 1/2 the speed it moves if the next random tells it to move 200... So how the heck do I have it move with a consistent speed?
#Noah Witherspoon is correct above - use Pythagorus to get a smooth speed:
//the node you want to move
SKNode *node = [self childNodeWithName:#"spaceShipNode"];
//get the distance between the destination position and the node's position
double distance = sqrt(pow((destination.x - node.position.x), 2.0) + pow((destination.y - node.position.y), 2.0));
//calculate your new duration based on the distance
float moveDuration = 0.001*distance;
//move the node
SKAction *move = [SKAction moveTo:CGPointMake(destination.x,destination.y) duration: moveDuration];
[node runAction: move];
Using Swift 2.x I've solved with this little method:
func getDuration(pointA:CGPoint,pointB:CGPoint,speed:CGFloat)->NSTimeInterval {
let xDist = (pointB.x - pointA.x)
let yDist = (pointB.y - pointA.y)
let distance = sqrt((xDist * xDist) + (yDist * yDist));
let duration : NSTimeInterval = NSTimeInterval(distance/speed)
return duration
}
With this method i can use an ivar myShipSpeed and directly call my actions for example :
let move = SKAction.moveTo(dest, duration: getDuration(self.myShip.position,pointB: dest,speed: myShipSpeed))
self.myShip.runAction(move,completion: {
// move action is ended
})
As an extension to #AndyOS's answer, if you're going to move only along one axis (X for example) you can simplify the math by doing this instead:
CGFloat distance = fabs(destination.x - node.position.x);

Resources