SpriteKit: Scene Editor

SpriteKit: Scene Editor

Preparation

Download assets
Create Health Component
  • Create a new file called HPComponent.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import SpriteKit
import GameplayKit

class HPComponent: GKComponent {

    override func didAddToEntity() {
        guard let node = entity?.component(ofType: GKSKNodeComponent.self)?.node
        else {
            return
        }
        if let HPMeter = SKReferenceNode(fileNamed: "HealthMeter") {
            HPMeter.position = CGPoint(x:0, y:50)
            node.addChild(HPMeter)
        }
    }

    override func willRemoveFromEntity() {

    }

    override func update(deltaTime seconds: TimeInterval) {

    }

    override class var supportsSecureCoding: Bool {
        true
    }
}
  • In GameScene.sks, select the player node. Click the button to show the inspectors and then switch to the Component Inspector.
  • Click the + button to reveal the list of available components. Add the HealthComponent
  • Test the build. You should see the health bar attached to your player and move with the player.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Foundation
import SpriteKit

enum Direction: String {
    case stop
    case left
    case right
    case up
    case down
}

class Player: SKSpriteNode {
    func move(_ direction: Direction) {
        print("player move: \(direction.rawValue)")
    }

    func stop() {
        print("Stop")
    }
}
  • Assign Player node to Player class using Custom Class
Test player movement
  • Modify this section of GameScene.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
private var lastUpdateTime : TimeInterval = 0
private var player: Player?

override func sceneDidLoad() {
    self.lastUpdateTime = 0
}

override func didMove(to view: SKView) {
    player = childNode(withName: "player") as? Player
    player?.move(.stop)
}

func touchDown(atPoint pos : CGPoint) {
    print("touch down")
    let nodeAtPoint = atPoint(pos)
    if let touchedNode = nodeAtPoint as? SKSpriteNode{
        if touchedNode.name?.starts(with: "controller_") == true{
            let direction = touchedNode.name?.replacingOccurrences(of: "controller_", with: "")
            player?.move(Direction(rawValue: direction ?? "stop")!)
        }
    }
}
...
  • Run and test movement (based on the print statement)
  • Modify functions move and stop inside class Player with the following contents.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func move(_ direction: Direction) {
    print("player move: \(direction.rawValue)")
    switch direction {
    case .up:
        self.physicsBody?.velocity = CGVector(dx: 0, dy: 100)
    case .down:
        self.physicsBody?.velocity = CGVector(dx: 0, dy: -100)
    case .left:
        self.physicsBody?.velocity = CGVector(dx: -100, dy: 0)
    case .right:
        self.physicsBody?.velocity = CGVector(dx: 100, dy: 0)
    case .stop:
        stop()
    }
}

func stop() {
    print("Stop")
    self.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
}
  • Rerun the simulation and observe the movements of the player node.

Hands-on Exercise

Preparation
  • Unzip Pokemon2D_Assets_2.zip and drag the files into Assets
Tasks
  • Add a new node, tree, to the game (from the assets).
  • Use multiple tree nodes to create a maze that the player cannot pass through.
    • Hint: Set the Category Mask and Collision Mask for both the player and the tree node.
  • Add a Pokémon node (node name: pokemon), and when the player hits the Pokémon, it will print “Player hit the Pokémon” in the console.

  • You will need to set up contactDelegate and SKPhysicsContactDelegate