株式会社スマートテック・ベンチャーズの意識最下層系の@omotesandoです。
さて12月といえば、リア充歓喜のイベントであるクリスマスがあります。クリスマスといえば、クリスマスツリーですね。
ということで、童心に戻りクリスマスツリーでも飾り付けしましょう。
どうぞよろしくお願いいたします。
#材料
- Xcodeさん(8.2にアップデートするのに3時間くらいかかりました。解せない。)
- Swift3さん(ころころ変わりたいお年頃だそうです。若いですね。)
- UIKitさん(iOSアプリを開発する上でなくてはならない方です。)
- SpriteKitさん(2Dゲームを作れるAppleのフレームワークです。接頭辞はSK。物理演算もできるってばよ。)
- イラスト素材(この記事のために自作しました。Photoshop様様。)
#それではやってみませう
##1. ツリーと飾りの準備
ツリーを表示するViewを用意します。
新規プロジェクトファイルを作るなら必ずお目にかかるこの画面ですが、ここで「Game」を選択すると、いかにもゲーム作れますよ的なプロジェクトが作れます。
**が!**今回はゲーム性のあるものを作るわけでもないので、「Single View Application」でいきましょう。(前にGameから作って訳わからん状態になったので)
プロジェクトファイルが作れたら、使う画像を追加しておきます。ぽいっと。
あと、プロジェクトにSpriteKit.frameworkを追加してください。
次に、新しいファイルを用意します。
File
> New
> File
で現れるウィンドウを下へスクロールすると見つかるSpriteKit Sceneさんを選択します。
これはシーンというゲームの1場面?のようなものを作るファイルになります。拡張子は**.sks**です。
作ったファイルを開くと、こんな感じの画面になります。Storyboardのような画面に。
画面中央付近にある白枠の中が、画面での表示領域となります。
次に、ノードと呼ばれる、ゲームを構成する要素のようなものを作ります。
今回用意するノードは、背景ノード、ツリーノード、雪ノード、飾りノードをいくつか。
右下にノードやらがずらずら用意されているので、1番上にある「Color Sprite」を画面内までドラッグ&ドロップしましょう。StoryboardでUILabelなどを貼り付けるのと同じ感じです。
これでシーン上にノードが用意されました。デフォルトでは赤い四角かと思います。
ノードを選択していると、これまたStoryboardのようにウィンドウ右側に、設定項目が表示されます。「Name」でノードの名前を、「Texture」で、ノードに使う画像を設定します。
例えば、ノードのTextureに「bg.png」を設定すると、こんな感じになります。
こんな感じで、ノードにテクスチャを適当に当てて、とりあえず準備完了。
すでにクリスマスツリー感あります?絵が下手とかそういうのは気にしてはいけません。
ノードのサイズはあとで調整します。
##2. ドラッグイベントの実装
いよいよ実機orシミュレータさんで表示して、ノードを動かせるようにします。
さて、やはりGUI的なアレでは実現できないことも多々あるため、このsksファイルに対応するクラスを用意しましょう。
File
> New
> File
で、今度は普通に「Cocoa Touch Class」でファイルを作ります。
「Subclass...」に「SKScene」クラスを指定します。
作成したファイルを開いたら、SpriteKitをインポートします。
import SpriteKit
で、クラス内でdidMove(to view: SKView)
メソッドさんを呼び出します。このメソッドは、シーンが呼び出された際に必ず呼ばれるものです。viewDidLoad()
のようなものでしょうか。
import SpriteKit
class XmasScene: SKScene {
override func didMove(to view: SKView) {
super.didMove(to: view)
}
}
この時点では、XmasScene.sksとXmasSceneクラスはひも付いていません。
ということで次。
StoryboardさんにあるViewControllerさんのviewのクラスをSKViewに変えます。
ViewControllerクラスさんに、このシーンを表示してくれるようにお願いします。
import UIKit
//SpriteKitをインポート
import SpriteKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//XmasSceneクラスとXmasScene.sksをひも付けて取得
let xmasScene = XmasScene(fileNamed: "XmasScene")
//self.viewをSKViewにキャスト
//StoryboardでviewのクラスをSKViewと指定しないと、キャストできず落ちます
let xmasView = self.view as! SKView
//シーンさんを表示します
xmasView.presentScene(xmasScene)
}
これでビルドすると、とりあえずシーンさんが実機orシミュレータに表示されると思いますが、なんじゃこりゃあ。
それではノードさんを動かせるようにしましょう。
XmasScene.swiftで、sksファイルで用意したノードを拾います。
import SpriteKit
class XmasScene: SKScene {
var bgNode:SKSpriteNode? //背景
var snowNode:SKSpriteNode? //雪
var treeNode:SKSpriteNode? //ツリー
var starNode:SKSpriteNode? //★
var ribonNode:SKSpriteNode? //リボン
var bellNode:SKSpriteNode? //ベル
var obe1Node:SKSpriteNode? //なんか丸いやつ(赤)
var obe2Node:SKSpriteNode? //なんか丸いやつ(水色)
var obe3Node:SKSpriteNode? //なんか丸いやつ(白)
override func didMove(to view: SKView) {
super.didMove(to: view)
//シーンに配置したノードを取得
//引数withNameには、sksファイルで各ノードの設定した「Name」を指定
//sksファイルで作ったノードはすべてSKSpriteNode型で作っていますが、
//childNode(withName:)でそのまま取得すると、SKNode型のため、キャストします
bgNode = self.childNode(withName: "bg") as? SKSpriteNode
snowNode = self.childNode(withName: "snow") as? SKSpriteNode
treeNode = self.childNode(withName: "tree") as? SKSpriteNode
starNode = self.childNode(withName: "star") as? SKSpriteNode
ribonNode = self.childNode(withName: "ribon") as? SKSpriteNode
bellNode = self.childNode(withName: "bell") as? SKSpriteNode
obe1Node = self.childNode(withName: "obe1") as? SKSpriteNode
obe2Node = self.childNode(withName: "obe2") as? SKSpriteNode
obe3Node = self.childNode(withName: "obe3") as? SKSpriteNode
//サイズをシーンに合わせる(今回は背景、雪、ツリーの画像は同一サイズ)
bgNode?.size = self.size
snowNode?.size = self.size
treeNode?.size = self.size
}
}
これでコード上でノードを自由に扱えます。
さて先ほどは無様な画面をお見せしてしまったので、諸々調整していきましょう。
背景、雪、ツリーノードさんは画像が同サイズで画面いっぱいに表示する予定なので、位置を表すposition
というプロパティをシーンと同じ値にしましょう。
これを指定すると、anchorPoint
という、ノードを回転させたりする際の基準点となる座標を指定するプロパティがposition
の位置になります。
position
//サイズと表示位置をシーンに合わせる(今回は背景、雪、ツリーの画像は同一サイズ)
bgNode?.size = self.size
bgNode?.position = self.position
snowNode?.size = self.size
snowNode?.position = self.position
treeNode?.size = self.size
treeNode?.position = self.position
//飾りノードの表示位置を左下に
let xOrigin = self.frame.origin.x
let yOrigin = self.frame.origin.y
starNode?.position = CGPoint(x: xOrigin + (starNode!.size.width / 2), y: yOrigin + (starNode!.size.height / 2))
ribonNode?.position = CGPoint(x: xOrigin + (ribonNode!.size.width / 2), y: yOrigin + (ribonNode!.size.height / 2))
bellNode?.position = CGPoint(x: xOrigin + (bellNode!.size.width / 2), y: yOrigin + (bellNode!.size.height / 2))
obe1Node?.position = CGPoint(x: xOrigin + (obe1Node!.size.width / 2), y: yOrigin + (obe1Node!.size.height / 2))
obe2Node?.position = CGPoint(x: xOrigin + (obe2Node!.size.width / 2), y: yOrigin + (obe2Node!.size.height / 2))
obe3Node?.position = CGPoint(x: xOrigin + (obe3Node!.size.width / 2), y: yOrigin + (obe3Node!.size.height / 2))
では、ノードをドラッグして、ツリーを飾れるようにしましょう!
ドラッグイベントを検知してくれるのは、おなじみのアレです。
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
//ドラッグイベント
for touch in touches{
//タップした座標を取得
let location = touch.location(in: self)
//タップした座標にあるノード(配列)を取得
let nodes = self.nodes(at: location) as? [SKSpriteNode]
//指定した座標までの移動アクションです
let moveAction = SKAction.move(to: location, duration: 0)
//ノードにアクションを適応させます
let node = nodes?[0]
//背景、ツリー、雪ノード以外であれば
if node != bgNode && node != treeNode && node != snowNode{
node?.run(moveAction)
}
}
}
最後に、シーンに対してノードが大きすぎるので、調整しましょう。
ノードにはsize
プロパティがありますが、下手に動かすとアスペクト比が合わなくなるので、ここはXscale
、Yscale
プロパティをいじることにしましょう。
ということで、ノードのサイズを調整した完成品がこちらになります。
#おわりに
作り終わって気付きましたが、SpriteKitらしさが皆無ですね。すみません。
##参考
SpriteKit入門
はじはじアプリ体験記