SwiftのPlaygroundが、インタラクションに対応したというので、数字をタッチして消すゲームを作ってみた

##・はじめに
Playgroundが新たにインタラクションに対応したということで、Playgroundだけで完結するゲームを作ってみました。
実は、最初にミニゲームを作り貯めて公開していこう!ぐらいの気持ちだったので、あまり考えずにPlaygroundを使えば、公開もスムーズにできるかな?と作り始めたのですが、いざ始めてみると、「あれ?」、「あれれ?」が続いたので、これを書き記しておきたいと思います。

Spritekitの物理シミュレーションなどは、動作を確認したことがあったので、てっきり、タッチアクションもできるものかと思っていましたが、Xcode7.2以下のバージョンできないことが、作成の途中でわかりました。

数字をタッチして順々に消していく、どこかで見たことのあるゲームです。
最初に、以下をPlaygroundにて作りました。

##・ゲームの見た目
数字のボタンをランダムに並べる。見た目の作業が終わりました。
![cap1.png](https://qiita-image-store.s3.amazonaws.com/0/118195/de2d12af-7737-4081-2b2d-dc4d0de40d6b.png)

↓タッチアクションのないプログラム↓
・Playground　プラットフォームをiOSとします。
・シーンサイズ　320×568pt
・画像ファイル rect.png 50×50px
(下部にあります。rect.pngとリネームして、Resourceフォルダにドラッグ&ドロップしてください。)

``` MyPlayground1.playground
import SpriteKit
import XCPlayground

let scene = SKScene(size: CGSize(width: 320, height: 568))
let view = SKView(frame: CGRect(x: 0, y: 0, width: 320, height: 568))
view.presentScene(scene)
view.showsFPS = true
view.showsPhysics = true
XCPlaygroundPage.currentPage.liveView = view
  
var minValue = 1
var nums = [Int](1...25)
var randNums = [Int]()

for _ in nums {
  let randIdx = Int(arc4random_uniform(UInt32(nums.count)))
  randNums.append(nums[randIdx])
  nums.removeAtIndex(randIdx)
}

let offset = CGPointMake(40, 450)
var count = 0

for y in 0...4 {
  for x in 0...4 {
    let numSprite = SKSpriteNode(imageNamed: "rect")
    numSprite.name = "numSprite\(randNums[count])"
    numSprite.position.x = numSprite.position.x + (CGFloat(x) % 5) * (numSprite.size.width + 10) + offset.x
    numSprite.position.y = numSprite.position.y - (CGFloat(y) % 5) * (numSprite.size.height + 10) + offset.y
    numSprite.zPosition = 0
    scene.addChild(numSprite)
    
    let numLabel = SKLabelNode(fontNamed:"Futura-CondensedExtraBold")
    numLabel.text = "\(randNums[count])"
    numLabel.name = "numLabel\(randNums[count])"
    numLabel.verticalAlignmentMode = .Center
    numLabel.fontSize = 36
    numLabel.fontColor = SKColor(red: (255/255.0), green: (40/255.0), blue: (160/255.0), alpha: 1.0)
    numLabel.zPosition = 1
    numSprite.addChild(numLabel)
    
    count += 1
  }
}

```
ここまでは、良かったのですが...

##・Playgroundのインタラクションについて
そして、タッチするには、どうしたらいいのか?と思って、ググってみると...以下検索結果。

・[Stack Overflow:How to get user interaction events in playground with Swift](http://stackoverflow.com/questions/24583846/how-to-get-user-interaction-events-in-playground-with-swift) 

そこには、1年前に同じことを考えていた人がいたようで、その答えに「ユーザーインタラクションは、Playgroundではできないよ。(ソース: WWDC)」(意訳)とありました。「あれ?」これダメじゃんと思いつつ、よくよく、最新のコメントを見たところ、「今のPlaygroundは、インタラクションに対応していて、Xcode 7.3からスタートしているよ」(意訳)と書いてありました。今のXcodeのバージョンっていくつ? Xcode 7.3っていつから?って思いながら、確認してみると、現在インストールしているバージョンで、つい最近バージョンアップしたやつでした。「あれれ?」出来るようになっている。しかも、最近じゃないですか!これは、ラッキー。ということで、一旦、ゲームを離れて、Playgroundのインタラクションに関する情報をここでも調査しました。

・[Apple:Interactive Playgrounds](https://developer.apple.com/swift/blog/?id=35) デモサンプルもある。
・[Qiita:Xcode7.3のPlaygroundはついにインタラクションに対応！](http://qiita.com/noppefoxwolf/items/c882f5b501a5e22593db) UIKitを動かしている。
・[Github:Sprite-Kit-Collisions-Playground](https://github.com/jaredmpayne/Sprite-Kit-Collisions-Playground) 1ファイルで記述されている。touchesbegan関数は組み込まれていませんでしたが、ほぼこれを参考にしています。

これらの情報を参考にして、touchesbegan関数を使うには、Sceneを作ってSKSceneを継承し、XcodeのProjectのようにtouchesbegan関数をoverrideすれば使えるという事がわかりました。以下プログラムです。

↓touchesbegan関数を組み込んだ最小のプログラム↓
・Playground　Xcode7.3以上、プラットフォームをiOSとします。
・シーンサイズ　320×568pt

``` MyPlayground2.Playground
import SpriteKit
import XCPlayground

class Scene: SKScene{
  
  override func didMoveToView(view: SKView) {
    size = CGSize(width: 320, height: 568)
  
  }
  override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    for touch in touches {
      let location = touch.locationInNode(self)
      
      
    }
  }
  
}

let scene = Scene()
let view = SKView(frame: CGRect(x: 0, y: 0, width: 320, height: 568))
view.presentScene(scene)
XCPlaygroundPage.currentPage.liveView = view

```


##・ゲームとして統合してみる。
タッチができます。
![cap2.png](https://qiita-image-store.s3.amazonaws.com/0/118195/68c325b8-0eba-979f-3aaf-c618485d5bb6.png)

↓数字をタッチして消すゲーム↓
・Playground　Xcode7.3以上、プラットフォームをiOSとします。
・シーンサイズ　320×568pt
・画像ファイル rect.png 50×50px
(下部にあります。rect.pngとリネームして、Resourceフォルダにドラッグ&ドロップしてください。)

内容はコメントにて解説してあります。

``` touchNum.playground
import SpriteKit
import XCPlayground //画面を表示するためにインポートが必要

class Scene: SKScene{ //ここでSceneクラスを作っているのだけど、SKSceneを継承する事で、touchesBegan()関数が利用できる。
  
  var minValue = 1//ボタンをタッチし、消えたボタン個数を数える。
  
  override func didMoveToView(view: SKView) {
    size = CGSize(width: 320, height: 568) //320×568ptのsceneを作る。

    var nums = [Int](1...25) //1から25の配列を作る。
    var randNums = [Int]() //空の配列を

    //----1から25をランダムに配列へ格納----
    //numsで作った配列を要素分取り出して、ランダムに空の配列randNumsに入れ替える。この時に、添字をランダムにして、それを元に入れ替えている。
    for _ in nums {
      let randIdx = Int(arc4random_uniform(UInt32(nums.count)))//ランダムを作る。
      randNums.append(nums[randIdx]) //ランダムな添字を使ってrandNums配列に追加
      nums.removeAtIndex(randIdx) //numsからそのランダム添字を使って配列を削除。これを繰り返す。
    }
    
    //----ボタンの生成----
    let offset = CGPointMake(40, 450)//Spritekitは、原点がviewの左下なるので、これを上部位置に来るよう調整。
    var count = 0//下記のループの総計をカウント
    //ボタンのタイリングを行う。
    for y in 0...4 { //x座標
      for x in 0...4 { //y座標
        let numSprite = SKSpriteNode(imageNamed: "rect") //数字の下のボタン(スプライトノード)
        numSprite.name = "numSprite\(randNums[count])"
        numSprite.position.x = numSprite.position.x + (CGFloat(x) % 5) * (numSprite.size.width + 10) + offset.x
        numSprite.position.y = numSprite.position.y - (CGFloat(y) % 5) * (numSprite.size.height + 10) + offset.y
        numSprite.zPosition = 0 //奥行き
        addChild(numSprite)
        
        let numLabel = SKLabelNode(fontNamed:"Futura-CondensedExtraBold") //数字のラベルノード
        numLabel.text = "\(randNums[count])"
        numLabel.name = "numLabel\(randNums[count])"
        numLabel.verticalAlignmentMode = .Center
        numLabel.fontSize = 36
        numLabel.fontColor = SKColor(red: (255/255.0), green: (40/255.0), blue: (160/255.0), alpha: 1.0)
        numLabel.zPosition = 1 //奥行き
        numSprite.addChild(numLabel)
        
        count += 1 //yとxが繰り返されるたびにカウントアップ
      }
    }
  }

  //----終了処理----
  //25個ボタンを押し終えた時の処理。文字が出てくるだけ。
  func win() {
    if minValue > 25{//minValueが25より多くなったら、処理を行う。
      let winLavel = SKLabelNode(fontNamed:"Futura-CondensedExtraBold")
      winLavel.text = "You did it!"
      winLavel.position = CGPointMake(frame.width / 2, frame.height / 2)
      winLavel.fontSize = 100
      winLavel.fontColor = SKColor(red: (255/255.0), green: (40/255.0), blue: (160/255.0), alpha: 1.0)
      addChild(winLavel)
      winLavel.runAction(SKAction.scaleTo(0.5, duration: 1)) //文字の表示アニメーション
    }
  }

  //----タッチ処理----
  override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    for touch in touches {
      let location = touch.locationInNode(self)//タッチした座標をlocationに代入。
      let node:SKNode! = self.nodeAtPoint(location)//そのlocationから、そこにあるノードをnodeに代入。
      
      //ボタンの背景スプライトである"numSprite\(minValue)"か数字のラベルノードである"numLabel\(minValue)"のどちらかのノードネームを押したら、if文内部を実行。
      if node.name == "numSprite\(minValue)" || node.name == "numLabel\(minValue)"{
        childNodeWithName("numSprite\(minValue)")!.runAction(SKAction.fadeAlphaTo(0.0, duration: 0.5)) //ボタン(数字ごと)が、消えるアニメーション
        minValue += 1 //タッチしたボタンが消えたら1追加。
        win() //26になったらwin()を実行。
      }
    }
  }
}

let scene = Scene() //Sceneクラスを生成
let view = SKView(frame: CGRect(x: 0, y: 0, width: 320, height: 568)) //viewを320×568Pt
view.presentScene(scene) //sceneの表示
view.showsFPS = true //FPSの表示
//view.showsPhysics = true //物理体の枠が見える。
XCPlaygroundPage.currentPage.liveView = view //ライブビューの表示

```


##・最後に
海外のサイトで、Playgroundファイルを確認してみると、ドキュメント方式で記述しているものを多く見ました。Playgroundのドキュメント方式+インタラクションの組み合わせでプログラム自体や数学(三角関数等)を解説したら、非常にわかり易い解説となると思います。今回の件で、今後の普及次第ではあると思いますが、プログラマーだけに留まらず、子供の教育、プレゼン、その他、多方面で使えるのではないか?とPlaygroundに秘めたる可能性を感じました。

##・画像
![rect.png](https://qiita-image-store.s3.amazonaws.com/0/118195/93cf65d3-8b5d-79ed-6792-a462686d98bb.png)
rect.png 50×50px
(rect.pngとリネームして、Resourceフォルダにドラッグ&ドロップしてください。)