この記事は CAMPHOR- Advent Calendar 2014 11日目の記事です。
こんにちは、@ryota-ka です。いきなりですが、割とタイトル詐欺です。どのあたりが詐欺かと言うと、
“iOS初心者が”
-> 某社のインターンで、デスマ的に40時間ぐらいで iPhone アプリのデザインあて (w/ Objective-C) をしたことはあったので、まったくの CocoaTouch 初心者というわけではないです。“3日でゲームを作った”
-> 実は、以前 JavaScript で書いたブラウザーゲームとして、ある程度実装してたものの焼き回しなので、最初からアイディアを考えたわけではないです。しかも、確かにベースシステムはほぼ出来上がりましたが、細かいところは実はできてません。忙しかったのだ。
その上作ったゲームに関して書くわけでもないんですが、まぁそれでも初めて Swift に触れたということは事実ですし、12月1日の午前4時から始め、途中風邪やらバイトやらで大変だったんですが、どうにかこうにか3日でゲームのベースシステムはほとんど完成したので、その間に思ったこととかを書いてみます。やめて、石とか投げないで。
本当は「Swift と SpriteKit を使ってゲームを作ってみよう!」的な記事になればよかったのですが、残念ながら余裕が全然ありませんでした……
Singleton パターンと access control の話
まず手始めに、Game class を Singleton パターンで実装しようと思ったのですが、ググってもいろいろな情報があって困りました。
たまたまその場に来た商業 Swift エンジニアこと @ymyzk に聞いてみると「俺 Swift の Singleton の実装方法ブログに書いたで」と言われたので、探したら確かに書いてました。さすが大先生!
Swift で Singleton パターンを実装する – 意識低い開発者のBlog
ところで、最初自分は class
定義の外側に private
で定数を宣言している意味がわからなかったのですが、ドキュメントを読んでみると以下のように書いてありました。
Private access restricts the use of an entity to its own defining source file.
(Cited from : The Swift Programming Language: Access Control)
private
という修飾子は、変数・定数のスコープを、クラスの中などではなく、同じファイル内に限定させるものらしいです。Java の private
とは違いますね。あと、read-only computed property
と書いていますが、computed property
についてはあとで話が出てきます。
SKAction と CGPath の話
プロトタイプの映像
このように、車が十字路を左折・直進・右折するイメージのゲームなのですが、オブジェクトを軌道に沿って動かすのがとても簡単でハッピーでした。
1 2 3 4 5 6 7 8 9 10 |
var path = CGPathCreateMutable() // 編集可能な CGPath を作成 CGPathMoveToPoint(path, nil, 0, 0) // 原点に移動 CGPathAddArcToPoint(path, nil, 0, 70, 190, 70, 50) // 右折前の直進部分の線分と円弧を描画 CGPathAddLineToPoint(path, nil, 190, 70) // 右折後の直進部分の線分を描画 var node = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 24, height: 24)) // 正方形の sprite node を生成 someScene.addChild(node) // sprite node をシーンに追加 let action = SKAction.followPath(path, speed: 80) // 上で生成した CGPath を辿るアニメーションを生成 node.runAction(action) // アニメーションを実行 |
これで、下側の白線の判定ラインのところから、少し直進して右折、その後直進して画面右方に消えていく、という動きを実現することができました。(画面下から判定ラインまでは、また別の SKAction
でアニメーションしています。)
さて、このゲームでは、正方形のノードが下方からだけではなく、上下左右の4方向から現れるわけですが、いちいちこんな風に軌道を定義してたんじゃめんどくさいですよね。せっかくなので使い回していきましょう。
1 2 |
let rot90: CGAffineTransform = CGAfineTransformMakeRotation(CGFloat(M_PI_2)) // 90度の回転を表す CGAffineTransform を生成 var path2: CGMutablePath = CGPathCreateMutableCopyByTransformingPath(path, &rot90) // path を90度回転させた path2 を生成 |
Quartz には CGAffineTransform
というデータ構造体があり、これはその名の通り Affine 変換を表すものです。M_PI_2
は、円周率を2で割った値を表す定数ですが、そのままだと Double
型なので、CGFloat
にしてから渡してあげます。また、CGPathCreateMutableCopyByTransformingPath
の第2引数が要求する型は UnsafePointer<CGAffineTransform>
なので、先頭に &
を付けて渡してあげましょう。こうして生成された path2
を、先程と同様に SKAction.followPath
に渡してあげれば、画面右側から右折して上方向に消えていくアニメーションが実現できます。
computed properties の話
自分は7-8年ほどPHPを書いてきた人間なので、オブジェクト指向といえば Java の流儀が根強く染み付いているのですが、func getFoo() -> Type { ... }
とか書いてると某氏に「クソコード書くな!」と怒られてしまったので、getter, setter 周りを少し見直してみました。ドキュメントを読んでいる時に computed properties については目に入っていたのですが、使ってみるとかなり便利だった、というか、read-only computed properties
を使うことにより、関数の定義を随分削減できたので、メモっておきます。
getter, setter というと、延々と getFoo()
とか setFoo()
とかを定義していくイメージですが、Swift においては、プロパティーを直接触ることも多いようです。プロパティーを直接触るというと、意図しない値の書き換えなどが監視できず、ツライ思いをしてしまうのではないかと心配になるのですが、それを防ぐ仕組みが conputed property
として用意されています。これは、クラスのプロパティーにアクセスする際の振る舞いを関数として記述できるという優れ物です。上記のページに記載されているサンプルコード(Terms of Use を読み切る時間がないので転載はしないでおきます……)では、正方形の中心を基準点として位置を決める際に、正方形の大きさに左右されずに、新しい座標を規定できるという例が説明されています。(結局公式のドキュメントが一番わかりやすい。)
ちなみに computed property
に対して、オブジェクトが値そのものを保持しておく普通のプロパティーは、stored property
というらしい。
結局自分の作っているものに specific な内容になってしまって、全然世の中の役に立たない記事になってしまったのですが、Swift がんばるぞ!という決意表明ということでお願いします(?)
明日は @siriusjack の担当です。お楽しみに!
ピンバック: CAMPHOR- Advent Calendar 2014をまとめるよ! - CAMPHOR- Blog