##・はじめに
Playgroundが新たにインタラクションに対応したということで、Playgroundだけで完結するゲームを作ってみました。
実は、最初にミニゲームを作り貯めて公開していこう!ぐらいの気持ちだったので、あまり考えずにPlaygroundを使えば、公開もスムーズにできるかな?と作り始めたのですが、いざ始めてみると、「あれ?」、「あれれ?」が続いたので、これを書き記しておきたいと思います。
Spritekitの物理シミュレーションなどは、動作を確認したことがあったので、てっきり、タッチアクションもできるものかと思っていましたが、Xcode7.2以下のバージョンできないことが、作成の途中でわかりました。
数字をタッチして順々に消していく、どこかで見たことのあるゲームです。
最初に、以下をPlaygroundにて作りました。
##・ゲームの見た目
数字のボタンをランダムに並べる。見た目の作業が終わりました。
↓タッチアクションのないプログラム↓
・Playground プラットフォームをiOSとします。
・シーンサイズ 320×568pt
・画像ファイル rect.png 50×50px
(下部にあります。rect.pngとリネームして、Resourceフォルダにドラッグ&ドロップしてください。)
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
そこには、1年前に同じことを考えていた人がいたようで、その答えに「ユーザーインタラクションは、Playgroundではできないよ。(ソース: WWDC)」(意訳)とありました。「あれ?」これダメじゃんと思いつつ、よくよく、最新のコメントを見たところ、「今のPlaygroundは、インタラクションに対応していて、Xcode 7.3からスタートしているよ」(意訳)と書いてありました。今のXcodeのバージョンっていくつ? Xcode 7.3っていつから?って思いながら、確認してみると、現在インストールしているバージョンで、つい最近バージョンアップしたやつでした。「あれれ?」出来るようになっている。しかも、最近じゃないですか!これは、ラッキー。ということで、一旦、ゲームを離れて、Playgroundのインタラクションに関する情報をここでも調査しました。
・Apple:Interactive Playgrounds デモサンプルもある。
・Qiita:Xcode7.3のPlaygroundはついにインタラクションに対応! UIKitを動かしている。
・Github:Sprite-Kit-Collisions-Playground 1ファイルで記述されている。touchesbegan関数は組み込まれていませんでしたが、ほぼこれを参考にしています。
これらの情報を参考にして、touchesbegan関数を使うには、Sceneを作ってSKSceneを継承し、XcodeのProjectのようにtouchesbegan関数をoverrideすれば使えるという事がわかりました。以下プログラムです。
↓touchesbegan関数を組み込んだ最小のプログラム↓
・Playground Xcode7.3以上、プラットフォームをiOSとします。
・シーンサイズ 320×568pt
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
↓数字をタッチして消すゲーム↓
・Playground Xcode7.3以上、プラットフォームをiOSとします。
・シーンサイズ 320×568pt
・画像ファイル rect.png 50×50px
(下部にあります。rect.pngとリネームして、Resourceフォルダにドラッグ&ドロップしてください。)
内容はコメントにて解説してあります。
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 50×50px
(rect.pngとリネームして、Resourceフォルダにドラッグ&ドロップしてください。)