Categories
iOS SpriteKit SWIFT

SKAction and SKConstraint

Use SKAction and SKConstraint: How to implement a space shooter with SpriteKit and SWIFT – Part 1

Initial project setup, sprite creation and movement using SKAction and SKConstraint

Tutorial Overview: How to implement a space shooter with SpriteKit and SWIFT

  • Part 1: Initial project setup, sprite creation and movement using SKAction and SKConstraint
  • Part 2: Adding enemies, bullets and shooting with SKAction and SKConstraint
  • Part 3: Adding a HUD with SKLabelNode and SKSpriteNode
  • Part 4: Adding basic game logic and collision detection
  • Part 5: Adding particles and sound 
  • Part 6: GameCenter integration
  • Part 7: iAd integration
  • Part 8: In-App Purchases

1. Create a new universal project (template: game; language: Swift)

part1-1
 
 
part1-2
 

2. Remove the GameScene.sks file

I’ll not use the internal level editor, so this file is obsolete and can be deleted:

 
part1-3
 

Inside GameController.swift remove this code block:

extension SKNode {

    class func unarchiveFromFile(file : NSString) -> SKNode? {         if let path = NSBundle.mainBundle().pathForResource(file, ofType: “sks”) {             var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)!             var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData) archiver.setClass(self.classForKeyedUnarchiver(), forClassName: “SKScene”)

            let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as GameScene

archiver.finishDecoding()             return scene } else {             return nil } } }

 

Replace the  ViewDidLoad method with this code to create a scene in fullscreen mode for iPad and iPhone:

    override func viewDidLoad() {
        super.viewDidLoad()
 
        // Detect the screensize
        var sizeRect = UIScreen.mainScreen().applicationFrame
        var width = sizeRect.size.width * UIScreen.mainScreen().scale
        var height = sizeRect.size.height * UIScreen.mainScreen().scale
 
        // Scene should be shown in fullscreen mode
        let scene = GameScene(size: CGSizeMake(width, height))
 
 
        // Configure the view.
        let skView = self.view as! SKView
        skView.showsFPS = true
        skView.showsNodeCount = true
 
        /* Sprite Kit applies additional optimizations to improve rendering performance */
        skView.ignoresSiblingOrder = true
 
        /* Set the scale mode to scale to fit the window */
        scene.scaleMode = .AspectFill
 
        skView.presentScene(scene)
    }

 

Limit the supported screen orientations to LandscapeLeft:

 
    override func supportedInterfaceOrientations() -> Int {
        return Int(UIInterfaceOrientationMask.LandscapeLeft.rawValue)
    }

3. Add the space ship:

 

I’ll add a spaceship sprite. The sprite will automatically orient and move to a touch location on the screen:
 
part1-4
 
The automated orienting of the spaceship is implemented with a little trick. I’ll add a SKConstraint to an invisible sprite. I case of a touch event, the invisible sprite is moved to the touch location and the spaceship is automatically oriented to this invisible sprite/touch location.

3.1. Open the GameScene.swift file and remove the code for creating a label in didMoveToView:

    override func didMoveToView(view: SKView) {
        /* Setup your scene here */
 
    }
 

3.2. Create two global Sprite properties:

 
    var heroSprite = SKSpriteNode(imageNamed:“Spaceship”)     var invisibleControllerSprite = SKSpriteNode()
 
    override func didMoveToView(view: SKView) {
        /* Setup your scene here */
 
 
    }
 

3.3. Create a sprite inside didMoveToView:

    self.backgroundColor = UIColor.blackColor()

 
    // Create the hero sprite and place it in the middle of the screen
    heroSprite.xScale = 0.15
    heroSprite.yScale = 0.15

    heroSprite.position = CGPointMake(self.frame.width/2, self.frame.height/2)     self.addChild(heroSprite)

 
 

3.4. Create an invisible sprite to implement the orientation behavior inside didMoveToView:

    // Define invisible sprite for rotating and steering behavior without trigonometry

    invisibleControllerSprite.size = CGSizeMake(0, 0)
    self.addChild(invisibleControllerSprite)
 

    // Define a constraint for the orientation behavior     let rangeForOrientation = SKRange(constantValue: CGFloat(M_2_PI*7))

    heroSprite.constraints = [SKConstraint.orientToNode(invisibleControllerSprite, offset: rangeForOrientation)]
 
 

3.5. Replace the code inside the touchesBegan method to implement the sprite movement:

    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        /* Called when a touch begins */
 
        for touch in (touches as! Set<UITouch>) {
 
            // Determine the new position for the invisible sprite:
            // The calculation is needed to ensure the positions of both sprites
            // are nearly the same, but different. Otherwise the hero sprite rotates
            // back to it’s original orientation after reaching the location of
            // the invisible sprite

            var xOffset:CGFloat = 1.0             var yOffset:CGFloat = 1.0             var location = touch.locationInNode(self)             if location.x>heroSprite.position.x { xOffset = 1.0 }             if location.y>heroSprite.position.y { yOffset = 1.0 }

 
        // Create an action to move the invisibleControllerSprite.
        // This will cause automatic orientation changes for the hero sprite

        let actionMoveInvisibleNode = SKAction.moveTo(CGPointMake(location.x  xOffset, location.y  yOffset), duration: 0.2)         invisibleControllerSprite.runAction(actionMoveInvisibleNode)

 
        // Create an action to move the hero sprite to the touch location

        let actionMove = SKAction.moveTo(location, duration: 1)         heroSprite.runAction(actionMove) } }

 
 
 
That’s all for today. In my next part I’ll add some enemies and bullets. You can download the code from GitHub: Part 1 or the latest version here.

You can also download my prototyping App for this tutorial series:

 

Cheers,
Stefan