khaled
khaled

Reputation: 865

Sine Wave Motion In SpriteKit

I want to make sine wave motion from the first point in the screen to the last Point in the screen, independent on the size of the screen.

Here is my code, but it doesn't work correctly:

 SKSpriteNode* RedBird = (SKSpriteNode*)[self childNodeWithName:@"RedBird"];
CGPoint currentPoint=CGPointMake(-60,0);
double width=self.frame.size.width/4;
CGPoint cp1=CGPointMake(width, self.frame.size.height/2);
CGPoint cp2=CGPointMake((width*3), 0);
CGPoint e=CGPointMake(self.frame.size.width+200, 0);


CGMutablePathRef cgpath = CGPathCreateMutable();


CGPathMoveToPoint(cgpath,NULL, currentPoint.x, currentPoint.y);
CGPathAddCurveToPoint(cgpath, NULL, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);



[RedBird runAction:[SKAction group:@[[SKAction repeatActionForever:RedBirdAnimation],[SKAction followPath:cgpath asOffset:YES orientToPath:NO duration:12.0]]]];


CGPathRelease(cgpath);

Upvotes: 11

Views: 3125

Answers (3)

ABakerSmith
ABakerSmith

Reputation: 22939

I agree with @Gord, in that I think SKActions are the best way to go. However, there is no need to approximate the sine curve when you can use the sin function.

Firstly, you need π as it's going to be useful for the calculations:

// Defined at global scope.
let π = CGFloat(M_PI)

Secondly, extend SKAction (in Objective-C this would be done with categories) to easily create an SKAction that oscillates the node in question:

extension SKAction {
    static func oscillation(amplitude a: CGFloat, timePeriod t: CGFloat, midPoint: CGPoint) -> SKAction {
        let action = SKAction.customActionWithDuration(Double(t)) { node, currentTime in
            let displacement = a * sin(2 * π * currentTime / t)
            node.position.y = midPoint.y + displacement
        }

        return action
    }
}

In the code above: amplitude is the height of the oscillation; timePeriod is the time for one complete cycle and the midPoint is the point around which the oscillation occurs. The formula for displacement comes from the equations for Simple Harmonic Motion.

Thirdly, putting that all together. You can combine the SKAction.oscillation action and SKAction.moveByX to make the sprite move along the path of the curve.

class GameScene: SKScene {
    override func didMoveToView(view: SKView) {
        let node = SKSpriteNode(color: UIColor.greenColor(), size: CGSize(width: 50, height: 50))
        node.position = CGPoint(x: 25, y: size.height / 2)
        self.addChild(node)

        let oscillate = SKAction.oscillation(amplitude: 200, timePeriod: 1, midPoint: node.position)
        node.runAction(SKAction.repeatActionForever(oscillate))
        node.runAction(SKAction.moveByX(size.width, y: 0, duration: 5))
    }
}

Upvotes: 14

Gord
Gord

Reputation: 155

This is an old post but I wanted to post an answer to make it google-able. I made this function in a subclass of SKSpriteNode that made the sprite go in an approximated sine wave from one side to the other.

It's in swift, but could be easily done in objective C. Also, you'd want to be more intelligent and put the path building in a loop, but I just kept it unlooped to make the math easy to understand.

(I don't disagree with the game loop answer but it kind of goes against the philosophy of Sprite Kit)

func setSineAction()
{
    let scene = self.parent as! SKScene

    let y42 = scene.size.height/2
    let x1 = scene.size.width
    let x2 = scene.size.width * 0.875
    let x3 = scene.size.width * 0.750
    let x4 = scene.size.width * 0.625
    let x5 = scene.size.width * 0.500
    let x6 = scene.size.width * 0.375
    let x7 = scene.size.width * 0.250
    let x8 = scene.size.width * 0.125
    let x9 = 0.0 as CGFloat

    let path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil, x1, y42)
    CGPathAddQuadCurveToPoint(path, nil, (x1/2)+(x2/2), scene.size.height*0.7, x2, y42)
    CGPathAddQuadCurveToPoint(path, nil, (x2/2)+(x3/2), scene.size.height*0.3, x3, y42)
    CGPathAddQuadCurveToPoint(path, nil, (x3/2)+(x4/2), scene.size.height*0.7, x4, y42)
    CGPathAddQuadCurveToPoint(path, nil, (x4/2)+(x5/2), scene.size.height*0.3, x5, y42)
    CGPathAddQuadCurveToPoint(path, nil, (x5/2)+(x6/2), scene.size.height*0.7, x6, y42)
    CGPathAddQuadCurveToPoint(path, nil, (x6/2)+(x7/2), scene.size.height*0.3, x7, y42)
    CGPathAddQuadCurveToPoint(path, nil, (x7/2)+(x8/2), scene.size.height*0.7, x8, y42)
    CGPathAddQuadCurveToPoint(path, nil, (x8/2)+(x9/2), scene.size.height*0.3, x9, y42)

    self.runAction(SKAction.followPath(path, asOffset: false, orientToPath: false, duration: 10))

}

Upvotes: 1

Theis Egeberg
Theis Egeberg

Reputation: 2576

I would create a game loop based that updates all of your game elements instead of relying on SKActions. SKActions is generally a way to perform animations, for continuos movement of game elements make a simple engine and have it update the positions of your red birds.

Upvotes: 0

Related Questions