Color Switch with Swift Playgrounds

Color Switch with Swift Playgrounds

Much of the learning experience that are available in Swift Playgrounds have wrapped code. This makes coding a little more simplistic for learners and allows them to experience wins early on. However, once students have completed the Learn to Code materials they are often looking to explore new possibilities which typically would involve moving to Xcode.  This tutorial shows the power of Swift Playground and how you can make full games straight on your iPad.

Color Switch is a popular game with over 125 million downloads worldwide. The game is simple and creates attractive visuals using only simple shapes such as circles, lines and squares that are brightly coloured.  We can easily create this game using a "Blank" template from the Starting Points in Swift Playgrounds.

Color_Switch_Swift_Playrounds_DBUDD.PNG
IMG_0242.jpg

Once we have the blank template we need to create a Scene and present it to the liveView.  This is the raw code that is needed to create any game using Swift Playgrounds and should look familiar if you have ever used Xcode.

import PlaygroundSupport
import SpriteKit

class GameScene: SKScene{
    override func didMove(to view: SKView){
      //placeholder
    }
  
}

//Load the Scene
let scene = GameScene(size: CGSize(width: 400, height: 640))
scene.scaleMode = .aspectFill

let view = SKView(frame: CGRect(x:0, y:0, width: scene.size.width, height: scene.size.height))
view.presentScene(scene)
PlaygroundPage.current.liveView = view

Color Switch Code

The following is the code to create a Color Switch Game in Swift Playgrounds for yourself.  There are plenty of opportunities for students to extend this code and make it their own.

If you wish to understand the code more then I would advise you to check out my course in iTunes U called Mobile Game Development (which is also translated into Spanish) - https://itunesu.itunes.apple.com/enroll/DNX-DFC-KRE .

IMG_0245.jpg
import PlaygroundSupport
import SpriteKit

class GameScene: SKScene, SKPhysicsContactDelegate {
    
    struct PhysicsCategory{
        static let Player: UInt32 = 1
        static let Obstacles: UInt32 = 2
    }
    
    let player = SKShapeNode(circleOfRadius: 20)
    let obstacleSpacing: CGFloat = 800
    let cameraNode = SKCameraNode()
    
    func didBegin(_ contact: SKPhysicsContact) {
        if let nodeA = contact.bodyA.node as? SKShapeNode, let nodeB = contact.bodyB.node as? SKShapeNode{
            if nodeA.fillColor != nodeB.fillColor{
                player.physicsBody?.velocity.dy = 0
                player.removeFromParent()
                score = 0
                scoreLabel.text = String(score)
                addPlayer(colour: .blue)
            }
            if nodeA.fillColor == nodeB.fillColor{
                score += 1
                scoreLabel.text = String(score)
            }
        }
    }
    
    func addPlayer(colour: UIColor){
        player.fillColor = colour
        player.strokeColor = player.fillColor
        player.position = CGPoint(x: 0, y: 10)
        
        //Physics Properties
        let playerBody = SKPhysicsBody(circleOfRadius: 15)
        playerBody.mass = 1.5
        playerBody.categoryBitMask = PhysicsCategory.Player
        playerBody.collisionBitMask = 4
        player.physicsBody = playerBody
        
        //Add to scene
        addChild(player)
    }
    
    
    func addCircle(){
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 0, y: -200))
        path.addLine(to: CGPoint(x:0, y: -160))
        path.addArc(withCenter: CGPoint.zero, radius:160, startAngle: CGFloat(3.0 * (Double.pi / 2)), endAngle: CGFloat(0), clockwise: true)
        path.addLine(to: CGPoint(x: 200, y: 0))
        path.addArc(withCenter: CGPoint.zero, radius: 200, startAngle: CGFloat(0.0), endAngle: CGFloat(3.0 * (Double.pi / 2)), clockwise: false)
        
        let obstacle = SKNode()
        
        for i in 0...3{
            let colours = [UIColor.yellow, UIColor.red, UIColor.blue, UIColor.purple]
            let section = SKShapeNode(path: path.cgPath)
            section.position = CGPoint(x: 0, y: 0)
            section.fillColor = colours[i]
            section.strokeColor = colours[i]
            section.zRotation = CGFloat(Double.pi / 2) * CGFloat(i);
            
            //Physics Properties
            let sectionBody = SKPhysicsBody(polygonFrom: path.cgPath)
            sectionBody.categoryBitMask = PhysicsCategory.Obstacles
            sectionBody.collisionBitMask = 0
            sectionBody.contactTestBitMask = PhysicsCategory.Player
            sectionBody.affectedByGravity = false
            section.physicsBody = sectionBody
            
            //Add to Obstacle
            obstacle.addChild(section)
        }
        obstacle.position = CGPoint(x: 0, y: 220)
        addChild(obstacle)
        
        let rotateAction = SKAction.rotate(byAngle: 2.0 * CGFloat(Double.pi), duration: 8.0)
        obstacle.run(SKAction.repeatForever(rotateAction))
    }
    
    func addSquare(){
        let path = UIBezierPath(roundedRect: CGRect(x: -200, y: -200, width: 400, height: 40), cornerRadius: 20)
        
        let obstacle = SKNode()
        
        for i in 0...3{
            let colours = [UIColor.yellow, UIColor.red, UIColor.blue, UIColor.purple]
            let section = SKShapeNode(path: path.cgPath)
            section.position = CGPoint(x: 0, y: 0)
            section.fillColor = colours[i]
            section.strokeColor = colours[i]
            section.zRotation = CGFloat(Double.pi / 2) * CGFloat(i);
            
            //Physics Properties
            let sectionBody = SKPhysicsBody(polygonFrom: path.cgPath)
            sectionBody.categoryBitMask = PhysicsCategory.Obstacles
            sectionBody.collisionBitMask = 0
            sectionBody.contactTestBitMask = PhysicsCategory.Player
            sectionBody.affectedByGravity = false
            section.physicsBody = sectionBody
            
            //Add to Obstacle
            obstacle.addChild(section)
        }
        
        obstacle.position = CGPoint(x: 0, y: 900)
        addChild(obstacle)
        
        let rotateAction = SKAction.rotate(byAngle: -2.0 * CGFloat(Double.pi), duration: 8.0)
        obstacle.run(SKAction.repeatForever(rotateAction))
    }
    
    let scoreLabel = SKLabelNode()
    var score = 0
    
    override func didMove(to view: SKView) {
        physicsWorld.contactDelegate = self
        physicsWorld.gravity.dy = -22
        
        addPlayer(colour: .blue)
        addCircle()
        addSquare()
        
        addChild(cameraNode)
        camera = cameraNode
        cameraNode.position = player.position
        
        scoreLabel.position = CGPoint(x: 180, y: 0)
        scoreLabel.fontColor = .white
        scoreLabel.fontSize = 100
        scoreLabel.text = String(score)
        cameraNode.addChild(scoreLabel)
    }
    
    override func update(_ currentTime: TimeInterval) {
        let playerPositionInCamera = cameraNode.convert(player.position, to: self)
        if playerPositionInCamera.y > 0 && !cameraNode.hasActions(){
            cameraNode.position.y = player.position.y
        }
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        player.physicsBody?.velocity.dy = 600.0
    }
    
}

// Load the Scene
let scene = GameScene(size: CGSize(width: 480, height: 640))
scene.scaleMode = .aspectFill

let view = SKView(frame: CGRect(x:0, y:0, width: scene.size.width, height: scene.size.height))
view.presentScene(scene)
PlaygroundPage.current.liveView = view
Exploring Art in Virtual Reality

Exploring Art in Virtual Reality

Exploring Functions through Music

Exploring Functions through Music