Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constant movement in SpriteKit

Tags:

ios

sprite-kit

I have a game where I would like to move an SKNode left or right depending on if the user touches the left or right side of the screen. How to I apply a constant movement to a node while the user is touching the screen?

like image 994
user1007895 Avatar asked Oct 10 '13 03:10

user1007895


People also ask

What is a node in SpriteKit?

Nodes are the fundamental building blocks of SpriteKit, and SKNode is the base class of all nodes. All of your onscreen assets will be an SKNode or a subclass thereof. SKNode s by themselves do not provide any visual content, however.


1 Answers

You really have three options for preserving the velocity.

One is to simply keep reseting the velocity back to your desired amount every update. In this case I chose 200,200. This will make your motion completely static.

-(void)update:(NSTimeInterval)currentTime {
    node.physicsBody.velocity=CGVectorMake(200, 200);
}

The second option is to preserve the velocity but leave room for motion distortions using a rate factor. The example below would preserve the velocity to 200,200 but not instantaneously. Instead it would depend on the rate. This will make your motion much more realistic and a little less static. For example, lets say you have a character moving at constant speed and you change direction, you will see a tiny acceleration as it changes to the new constant speed value, making the change in motion less static and more dynamic. I highly recommend this option.

-(void)update:(NSTimeInterval)currentTime {

    CGFloat rate = .05;
    CGVector relativeVelocity = CGVectorMake(200-node.physicsBody.velocity.dx, 200-node.physicsBody.velocity.dy);
    node.physicsBody.velocity=CGVectorMake(node.physicsBody.velocity.dx+relativeVelocity.dx*rate, node.physicsBody.velocity.dy+relativeVelocity.dy*rate);

}

The third option you have is to just set the velocity or apply an impulse ONCE (as opposed to every frame like we did above), but turn off its gravity and air resistance. The problem with this approach is that it will not always preserve your velocity. It will only keep constant speed until acted on by an external force (i.e. friction, colliding with another body, etc). This approach works for cases where you want some object to move at constant speed but still remain completely dynamic. I don't recommend this option at all if you are looking to control and/or preserve your objects velocity. The only scenario I could think of for this is maybe a game with moving asteroids that float around the screen at constant speed but are still affected by external forces (i.e. 2 asteroids collide which will result in a new constant speed).

node.physicsBody.velocity=CGVectorMake(200, 200);
node.physicsBody.linearDamping = 0; //You may want to remove the air-resistance external force.
node.physicsBody.affectedByGravity = false; //You may want to remove the gravity external force

Here is an example project I wrote in Swift using option 2. I tried to match your description. It simply moves a node left or right at constant speed. Be sure to experiment with the rate factor.

import SpriteKit
class GameScene: SKScene {
    var sprite: SKSpriteNode!
    var xVelocity: CGFloat = 0
    override func didMoveToView(view: SKView) {
        sprite = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 50, height: 50))
        sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
        sprite.physicsBody.affectedByGravity = false
        sprite.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0)
        self.addChild(sprite)
    }
    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        let touch = touches.anyObject() as UITouch
        let location = touch.locationInNode(self)
        if (location.x<self.size.width/2.0) {xVelocity = -200}
        else {xVelocity = 200}
    }
    override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!)  {
        xVelocity = 0
    }
    override func update(currentTime: CFTimeInterval) {
        let rate: CGFloat = 0.5; //Controls rate of motion. 1.0 instantaneous, 0.0 none.
        let relativeVelocity: CGVector = CGVector(dx:xVelocity-sprite.physicsBody.velocity.dx, dy:0);
        sprite.physicsBody.velocity=CGVector(dx:sprite.physicsBody.velocity.dx+relativeVelocity.dx*rate, dy:0);
    }
}

Note: Try to stay away from SKActions for real-time motion. Only use them for animations.

By real-time motion, I mean motion in which the position and velocity of your objects are important to you at each step in their simulation. Whereas in animation, you are really only concerned with the start and end points over some duration. For example, a game with moving characters requires real-time motion. You need to be able to monitor at each step in the simulation the positions and velocities of your characters, and potentially make adjustments at certain intervals. But for simpler things like moving UI elements, you don't need to track their motion at each step, so use animation

like image 93
Epic Byte Avatar answered Sep 23 '22 06:09

Epic Byte