LoginSignup
16
16

More than 5 years have passed since last update.

【SpriteKit】初心者がSpriteKitでクリスマスカードアプリを作って考えたこと

Posted at

アプリ道場の15日目担当のniggです。普段はデザイナーをしております。

私も含め初心者は、いざ自分のアプリを作ろうとする時、何から手をつけて良いのか悩むという方が多いと思います。
初めのうちは「基礎を覚える」という目的がシンプルなこともあり、ネットや本などを参考に学ぶことに集中できるのですが、それが一通り終わって作りたかったアプリ制作をしようとすると、なぜか手が止まってしまう。
その原因の一つに『やりたいことや作りたいものがある』ということがあげられます。それらは単純に初心者が実現するにはハードルが高いことが多いので、やるべきことの壮大さに脳内が一種のパニック状態になるというわけです。

そこで私がおこなった、ちょっとした発想転換の方法について書きたいと思います。
中級者以上にとっては既知な内容かもしれませんが、ご了承ください。

QiitaやGitHubの利用も初めて、XcodeもSwiftも初心者なため、おかしい表記や表現が出てくるかもしれませんが、暖かい目線でご指摘、ご指導くださると助かります。よろしくお願いいたします。

今の知識だけで作れるものを考える

覚えた要素や機能だけを使って何ができるか考える。一つの要素にも複数のプロパティがあります。制作していく中で少しずつプロパティを調べて足すなどしてできることを広げ、それから他の要素と組み合わせていくことで、コードの書き方と感覚を身につけることができると思います。
『割り切り』は上達に重要なことのようです。

自分なりの手順とルールを決める

私は下記のような手順とルールを決めて、アプリを作る練習をしています。
基本的なプログラムの感覚をつかむことが目的ですが、あえてクセをつけることにより設定忘れをなくそうという考えでもあります。

【A.目標設定】
  ↓
【B.完成のイメージ】
  ↓
【C.実現に必要な要素、動き、仕組みなど箇条書する】
  ↓
【D.個別に以下の6つの順番で詳細を考える】

 1. どこに書くべきか考える(読み込むタイミングなどを考える)
 2. 必要な要素を作る(変数やSK●●Nodeなど)
 3. 要素のプロパティを指定する(位置やサイズなど)
 4. 要素を表示する(addChildするなど)
 5. させたい動きや仕組みをつくる(SKActionなど)
 6. 作った動きや仕組みや後処理を実行する(runActionなど)

たとえば

例としてクリスマスカードアプリを作ってみました。
過程がわかりやすいように先に完成イメージを置いておきます。実際には起動後すぐにBGMが流れます。

Untitled3.gif

考え方の流れを説明するために部分的にいくつかコードを発表するというコンセプトで進めさせていただきます。細かいコードやパーティクル設定などの説明はいたしません。
SpriteKitのパーティクルの設定に関しましては、アドベントカレンダー11日目を担当したKoi-fumiさんの『【SpriteKit】爆発や雪などの表現ができる「Particle File」について』にとても丁寧に説明されておりますのでそちらを参考ください。

【A.目標】

今回は覚えたばかりの以下の要素を利用して、シンプルな『クリスマスカードアプリ』を作る

  • SKColor(背景色)
  • SKLabelNode(文字)
  • SKSpriteNode(画像)
  • SKAudioNode(音)
  • KEmitterNode(パーティクル)
  • SKAction(アクション)

【B.完成イメージ】

クリスマスツリーのポストカード + 雪が降る + BGMが流れる

【C.実現に必要な要素、動き、仕組み】

  • 縦画面表示にする
  • 背景を赤くする
  • クリスマスツリー画像を真ん中に置く(フリー素材を一枚使用)
  • メッセージをツリーの下に置く
  • 画面に雪を降らせる(パーティクルを利用)
  • ツリーの頭に星を輝かせる(パーティクルを利用)
  • BGMを流す(サウンドは魔王魂さんのフリー素材を使用)
  • 《余裕があれば》画面をタッチしたら星が出て消えていく(タッチアクション)

以上、流れができたらそれぞれの項目に詳細を書いていく、といった感じです。

【D.個別に詳細を考える】

◼︎縦画面表示にする

●ルール1.どこに書くべきか考える『GameViewController.swift』内からサンプル関係の表記を削除し『override func viewDidLoad()』内のGameSceneを変更し、『override func supportedInterfaceOrientations()』内に縦画面設定を追加。

GameViewController.swift
// override func viewDidLoad()内を変更
let scene = GameScene(size: CGSize(width: 768, height: 1024))  // 画面サイズ指定
let skView = self.view as! SKView

skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill

skView.presentScene(scene)

// override func supportedInterfaceOrientations()内に縦画面設定を追加
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait  // 縦画面設定
}

◼︎背景を赤くする

●ルール1.どこに書くべきか考える『GameScene.swift』内からサンプル関係の表記を削除し『override func didMoveToView(view: SKView)』内に設置

※今回は説明しやすくするために、表示や音関係のコードは全て『override func didMoveToView(view: SKView)』内に書くことにしました

●ルール2.必要な要素を作る(backgroundColorにSKColorを指定)
●ルール3.要素のプロパティを指定する 色のそれぞれ数値を0.1ずつかえて丁度良い色に。

GameScene.swift

// 背景色をワインレッドに
self.backgroundColor = SKColor(red: 0.7, green: 0.1, blue: 0.1, alpha: 1.0)

単純な赤ならコードもシンプルになります。

GameScene.swift

// 背景色を赤に
self.backgroundColor = SKColor.redColor()

◼︎画像を配置

●ルール1.どこに書くべきか考える『override func didMoveToView(view: SKView)』内に設置
●ルール2.必要な要素を作る(SKSpriteNodeに画像を指定して定数treeにいれる)
●ルール3.要素のプロパティを指定する(treeの位置、サイズ、階層を設定)
●ルール4.要素を表示する(treeをaddChildして表示させる)

GameScene.swift
// ツリーイメージを配置
let tree = SKSpriteNode(imageNamed:"christmasTree") 
// 位置を設定。y座標は微調整し+10を加算
tree.position = CGPoint(x:self.size.width/2, y:self.size.height/2+10) 
// 画像サイズ 
tree.size = CGSize(width: 670, height: 715)  
// 階層
tree.zPosition = -10  

// Nodeに追加して画像を表示
addChild(tree)  

◼︎文字を配置

フォントはiOS Fontsのサイトを参考に気に入ったものを選択しました。ラベルは2箇所ありますが、指定は同じ要領なので一つは省略します。
●ルール1.どこに書くべきか考える『override func didMoveToView(view: SKView)』内に設置
●ルール2.必要な要素を作る(SKLabelNodeに表示文字を指定して定数myLabelにいれる)
●ルール3.要素のプロパティを指定する(myLabelの書体、サイズ、色、透明度、位置、上下左右の文字揃え、階層を設定)
●ルール4.要素を表示する(myLabelをaddChildして表示させる)

GameScene.swift
// テキストを置く
// 表示文字
let myLabel = SKLabelNode(text: "Happy X'mas")  
// 書体指定
myLabel.fontName = "AppleSDGothicNeo-Thin"  
// 文字サイズ
myLabel.fontSize = 72  
// 文字色
myLabel.fontColor = SKColor.whiteColor()  
// 透明度
myLabel.alpha = 1.0 
// 配置する場所 
myLabel.position = CGPoint(x: self.size.width/2, y: 150)  
// 縦文字中央揃え
myLabel.verticalAlignmentMode = .Center  
// 横文字中央揃え
myLabel.horizontalAlignmentMode = .Center 
// 階層を設定 
myLabel.zPosition = 15  

// Nodeに追加して文字を表示
addChild(myLabel)  

◼︎BGMを設置

以外に簡単だったので驚きました。多分細かい調整は色々できると思います。※参考リンク

●ルール1.どこに書くべきか考える『override func didMoveToView(view: SKView)』内に設置
●ルール2.必要な要素を作る(SKAudioNodeに音楽データを指定して定数backGroundMusicにいれる)
●ルール4.要素を表示する(backGroundMusicをaddChildして音をならす)

GameScene.swift
// 音楽を指定する
let backGroundMusic = SKAudioNode(fileNamed: "music.mp3")

// Nodeに追加して音をならす
addChild(backGroundMusic)  

定数を使用しないならもっとシンプルにセットできます。

GameScene.swift
// 音楽を追加(シンプル版)
addChild(SKAudioNode(fileNamed: "music.mp3"))  // Nodeに直接追加して音をならす

◼︎パーティクル設置

パーティクルは2種類用意しました。

一つ目は『雪』です。
SpriteKit Particle File追加時に『snow』という設定があったので、それを選択しパラメーターを微調節しただけですが、素敵な感じに仕上がりました。

●ルール1.どこに書くべきか考える『override func didMoveToView(view: SKView)』内に設置
●ルール2.必要な要素を作る(SKEmitterNodeに作ったパーティクルを指定して定数snowEmitterにいれる)
●ルール3.要素のプロパティを指定する(snowEmitterの位置と階層を設定)
●ルール4.要素を表示する(snowEmitterをaddChildして表示させる)

GameScene.swift
// パーティクルで雪をふらせる
// 作ったパーティクルを指定する
let snowEmitter = SKEmitterNode(fileNamed: "snowParticle")
// 位置を設定
snowEmitter?.position = CGPoint(x:self.size.width/2, y:self.size.height)  
// 階層を設定
snowEmitter?.zPosition = -5  

// Nodeに追加して雪を降らせる
addChild(snowEmitter!)  

はじめは『?』や『!』などoptionalの概念を勘違いしていたのでつまずきました。
動作には支障はなかったのですが、どうしてもモヤモヤが晴れないので講師のAkioさんに質問し下記のようにスッキリした形にすることができました。

GameScene.swift
// パーティクルで雪をふらせる(受け渡す内容がnilの場合にそなえ確認処理を追加し安全化)
// 作ったパーティクルを指定する
if let snowEmitter = SKEmitterNode(fileNamed: "snowParticle") {
    // 成功した時だけ以下の処理を行う
    // 位置を設定
    snowEmitter.position = CGPoint(x:self.size.width/2, y:self.size.height)  
    // 階層を設定
    snowEmitter.zPosition = -5  

    // Nodeに追加して雪を降らせる
    addChild(snowEmitter)  
}

2つ目は『星』です。
こちらは『spark』を選択しParticle Textureを自作(star.png)してパラメーターを調整しました。
配置までは順調にできたのですが、星の瞬きを表現したいと欲がでたので、SKActionを試行錯誤しながら追加しました。

●ルール1.どこに書くべきか考える『override func didMoveToView(view: SKView)』内に設置
●ルール2.必要な要素を作る(SKEmitterNodeに作ったパーティクルを指定して定数snowEmitterにいれる)
●ルール3.要素のプロパティを指定する(snowEmitterの位置と階層を設定)
●ルール4.要素を表示する(snowEmitterをaddChildして表示させる)
●ルール5.させたい動きや仕組みをつくる(瞬きの仕組みをつくる)
●ルール6.作った動きや仕組みを実行させる(瞬きの実行させる)

GameScene.swift
// 願い星の配置
if let starEmitterTop = SKEmitterNode(fileNamed: "sparkParticle"){

    // 縦サイズ(倍率)
    starEmitterTop.xScale = 3  
    // 横サイズ(倍率)
    starEmitterTop.yScale = 3  
    // 明るく感じたので透明度設定
    starEmitterTop.alpha = 0.3  
    // 位置を設定。x座標は微調整し+5を加算
    starEmitterTop.position = CGPoint(x:self.size.width/2+5, y:840)
    // Nodeに追加して表示させる
    addChild(starEmitterTop)  

    // 瞬きの表現
    // 2秒で透明度を0.2まで変化させる設定をfadeTopStar01に入れる
    let fadeTopStar01 = SKAction.fadeAlphaTo(0.2, duration: 2.0)
    // 2秒で透明度を0.4まで変化させる設定をfadeTopStar02に入れる
    let fadeTopStar02 = SKAction.fadeAlphaTo(0.4, duration: 2.0)
    // fadeTopStar01とfadeTopStar02の順番でsequenceTopStarにいれる
    let sequenceTopStar = SKAction.sequence([fadeTopStar01, fadeTopStar02])
    // sequenceTopStarを永遠にアニメーションさせる
    let repeatActionTopStar = SKAction.repeatActionForever(sequenceTopStar)
}

◼︎画面をタッチしたら星が現れ消える

上記で目的は達成したのですが、せっかくですからアプリっぽく、星のパーティクルを利用して画面をタッチしたら星が現れ消えていくアクションを追加しました。

●ルール1.どこに書くべきか考える『override func touchesBegan(touches: Set, withEvent event: UIEvent?)』内に設置
●ルール2.必要な要素を作る(条件を指定しSKEmitterNodeに作ったパーティクルを指定して定数sparkParticleにいれる)
●ルール3.要素のプロパティを指定する(sparkParticleの位置や発生させる個数を設定)
●ルール4.要素を表示する(sparkParticleをaddChildしてセットする)
●ルール5.させたい動きや仕組みをつくる(瞬きの仕組みをつくる)
●ルール6.作った動きや仕組みや後処理を実行する(タッチ時に瞬きの実行しNodeを削除する)

GameScene.swift
// 『override func touchesBegan(touches: Set, withEvent event: UIEvent?)』内
// タッチした時、星を出してみる
for touch: AnyObject in touches {
    // タッチ位置をとる
    let location = touch.locationInNode(self)

    // sparkParticleの生成と設定
    if let touchStarEmitter = SKEmitterNode(fileNamed: "sparkParticle"){
        // タッチ位置をセットする
        touchStarEmitter.position = location
        // 秒あたりの個数を指定
        touchStarEmitter.particleBirthRate = 50

        // Nodeに追加
        addChild(touchStarEmitter)

        // 星を2秒で透明度を0.0まで変化させる設定
        let fadeOutTouchStar = SKAction.fadeAlphaTo(0.0, duration: 2)  
        // 星を表示しフェードアウト後にNodeを削除。
        let actionTouchStar = SKAction.sequence([fadeOutTouchStar, SKAction.removeFromParent()])  
        // アクションを実行
        touchStarEmitter.runAction(actionTouchStar)  
    }

}

以上。

まとめ

  • 順序だてて流れを覚えることで、他の要素に応用できるようになり迷うことが少なくなる。
  • 要素が明確なので、わからないことにぶつかっても具体的になことを調べることができ、解決がスムーズになる。
  • 基本的な要素、動き、仕組みの組み合わせで、様々な表現が実現していくことが実感出来る。

約1月前にAkioさん主催の『初心者のためのゲームアプリ開発講座』で2日間だけ学んだSpriteKitですが、そこで教わった内容の一部だけで、40代半ばのプログラム初心者でもここまで出来るようになりました。
この記事が皆さんの夢の実現に少しでも役立つことを願って。

素敵なクリスマスを!

16
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
16