Edited at

冬休みはクリスマスアプリを作って基礎を復習しちゃうぞ!

More than 1 year has passed since last update.


まえがき

こんにちは!iPhoneアプリプログラミングコースのメンターをしているながたです!

さぁさぁ、もうすぐ冬休みに入りますね!

クリスマスにお正月、楽しい行事が目白押しの冬休み。

卒論の量がすさまじくて白目向きのながたです。

そんな冬休みは基礎要素てんこ盛りのクリスマスアプリを作って

一緒に楽しい時間を過ごしましょう!


対象

「プログラミング初心者でiOSアプリ開発を始めてみて、簡単なチュートリアルちょっとやってみた」

くらいのレベル感です。


環境

Xcode9.1

Swift4


作るもの

「スノーキャッチ」

雪だるまを動かして雪をキャッチしよう。

デモ動画


内容


  • Xcodeの使い方

  • 画面遷移

  • 値渡し

  • コードからView(ImageViewの生成・表示)


アプリをつくろう


プロジェクトをつくろう

新しいプロジェクトをつくります。アプリ名は「SnowCatch」としました。

スクリーンショット 2017-12-11 12.25.08.png


見た目をつくろう

今回はViewControllerを2つ用意します!

image.png

1つ目:

太陽と雪だるまはUIImageView

「Time:」 と「00:00」はUILabel

「◀」「▶」「START」はUIButton

合計7個のパーツを置いたよ。

2つ目:

「結果」「0」「ポイント」はUILabel

「もう一度」はUIButton

合計4個のパーツを置いたよ。


結果画面のファイルをつくろう

image.png

右クリック(2本指クリック)して、上から5つめの「New File...」から結果画面のプログラムを書くためのファイルをつくろう

スクリーンショット 2017-12-11 11.04.55.png

「Cocoa Touch Class」から

スクリーンショット 2017-12-11 11.05.19.png

「ResultViewController」という名前でViewControllerのファイルをつくろう。

作ることができたら、Main.storyboardに戻って、作ったファイルと結果画面の関連付けをするよ。

記事みたいなマークをクリックしてClassを「ResultViewController」に設定しよう。

image.png

これでOKだ!ここまで出来たかな?


ゲーム画面のプログラムを書こう

ViewController.swiftにプログラムを書いていくぞ。


タイマーを動かそう

最初は制限時間となるタイマーを動かすぞ。

スタートボタンを押したらタイマーが動くように設定しよう。

そのために必要なことは

① タイマーを動かすこと

② タイムをラベルに表示すること

①ではスタートボタンを押したらTimerを使おう。②を作るためには何が必要か考えてみよう。

ラベルに表示するためには、ラベルを宣言して関連付けすることが必要だ!

それから時間をカウントするための変数(小数)も必要だね。

じゃあ作ってみよう。


ViewController.swift

import UIKit

class ViewController: UIViewController {
/* 宣言 */
//タイマーの宣言
var timer: Timer = Timer()
// 制限時間を表示するラベルの宣言
@IBOutlet var timeLabel: UILabel!
// 制限時間のカウント
var timeCount: Float = 60.0

//スタートボタンを押した時のアクション
@IBAction func start() {

timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { _ in

/* 制限時間をラベルに表示 */
self.timeCount = self.timeCount - 0.05
self.timeLabel.text = String(format: "%.2f",self.timeCount)
}
}
}


関連付けも忘れずにね!

さぁこれでラベルに制限時間が表示されたか確認してみよう!

そしてここで


クイズ〜!

このままだと、スタートボタンを押すたびにタイマーが動いてしまうのでスタートボタンを何回も押すと時間の進みがとっても早くなってしまいます。

さぁ、どうすればいいでしょうか?

ヒント

「もしタイマーが動いなかったらタイマーを動かす」というようにタイマーを動かす前に条件を加えよう。

タイマーが動いているときはtimer.isValidtrueという値が入っているよ。


雪を落とそう


ViewController.swift

    func createSnow() {

/* イメージビューを作成 */
let snowImageView: UIImageView = UIImageView(frame: CGRect(x: CGFloat(arc4random_uniform(UInt32(UIScreen.main.bounds.width))), y: 0, width: 50, height: 50)) //x(左右)の位置はランダム
snowImageView.image = UIImage(named: "snow.png") //雪の画像を入れよう
self.view.addSubview(snowImageView) //表示する

// 落ちてくる雪のアニメーション
// 少しずつ雪の位置を下にずらしているよ
Timer.scheduledTimer(withTimeInterval: 0.02, repeats: true) { fallTimer in

snowImageView.frame.origin.y += 2
// 雪がiPhoneの画面より下にいったら消そう!
if snowImageView.frame.origin.y > UIScreen.main.bounds.height {
snowImageView.removeFromSuperview() //雪を消す
fallTimer.invalidate() //雪のアニメーション用のタイマーを止める
}
}
}


createSnowというメソッドをつくったのでそれを呼び出しましょう。


ViewController.swift


/* 雪を落とす用のタイマーを宣言 */
var snowTimer: Timer = Timer()

//スタートボタンを押した時のアクション
@IBAction func start() {
timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { _ in

self.timeCount = self.timeCount - 0.05
self.timeLabel.text = String(format: "%.2f",self.timeCount)
}

// 1秒ごとに雪を作る
snowTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in

self.createSnow()
}

}


さあ実行してみましょう。上手く動いたかな?

そしてここでお待ちかねの


クイズ〜!

雪の落ちてくるスピードを変えるにはどうすればいいかな?

ヒント

スピードをいじるポイントは「数字」だよ


雪だるまを動かそう

さぁそろそろ折り返し地点!

次は雪だるまを操作しよう。

今回はシンプルに

「左右のボタンを押すたびに雪だるまが少しずつ動く」という動きをつくるよ。


ViewController.swift


//雪だるまのイメージビューを宣言
@IBOutlet var yukidarumaImageView: UIImageView!

//雪だるまの位置を左に動かす
@IBAction func moveLeft() {
yukidarumaImageView.frame.origin.x -= 50
}

//雪だるまの位置を右に動かす
@IBAction func moveRight() {
yukidarumaImageView.frame.origin.x += 50
}


関連付けをしよう

動いたかな?

そして、やっぱりあるよ!


クイズ〜!

このままだとボタンを押しすぎたときに雪だるまが画面の外に消えちゃうよ。

雪だるまが画面の外にいかないようにするにはどうればいいかな?

ヒント

雪だるまが画面内にいる位置のときだけ雪だるまを動かせるようにしよう!


当たり判定をつけよう

さぁ、ここからゲームにしていくよ!

雪を作るcreateSnowメソッドを書き換えよう。


ViewController.swift

    func createSnow() {

/* イメージビューを作成 */
let snowImageView: UIImageView = UIImageView(frame: CGRect(x: CGFloat(arc4random_uniform(UInt32(UIScreen.main.bounds.width))), y: 0, width: 50, height: 50)) //x(左右)の位置はランダム
snowImageView.image = UIImage(named: "snow.png") //雪の画像を入れよう
self.view.addSubview(snowImageView) //表示する

// 落ちてくる雪のアニメーション
// 少しずつ雪の位置を下にずらしているよ
Timer.scheduledTimer(withTimeInterval: 0.02, repeats: true) { fallTimer in

snowImageView.frame.origin.y += 2

/* 追加 */
// もし雪が雪だるまに重なったら
if self.yukidarumaImageView.frame.contains(snowImageView.frame) {
print("当たった")
snowImageView.removeFromSuperview() //雪を消す
fallTimer.invalidate() //雪のアニメーション用のタイマーを止める
}
/* ここまで追加 */

// 雪がiPhoneの画面より下にいったら消そう!
if snowImageView.frame.origin.y > UIScreen.main.bounds.height {
snowImageView.removeFromSuperview() //雪を消す
fallTimer.invalidate() //雪のアニメーション用のタイマーを止める
}
}
}


雪と雪だるまが当たったら雪が消えるようになったかな?

そうしたら、おまちかねの


クイズ~!

雪だるまと雪が当たったらポイントを追加しよう!

今回のクイズはちょっとだけ難しいよ。じっくり考えてみよう!

ヒント

Step1. 「ポイント」用の変数(整数)を宣言しよう。

Step2. 雪と雪だるまが当たったタイミングでポイントを1ずつ足そう。

さらに余裕がある人は

Step3. ラベルを作って現在のポイントを表示できるようにしよう。


結果画面をつくろう

Step1で宣言した 「ポイント」用の変数(整数)を結果画面に受け渡そう。

ここでは 「ポイント」用の変数はViewControllerに


ViewController.swift

var snowPoint: Int = 0


と宣言したとして続きを進めます!

まずはセグエ(Segue)の設定です

Main.storyboardでViewControllerからcontrolキーを押しながらResultViewControllerにドラッグ&ドロップ!

スクリーンショット 2017-12-11 17.45.45.png

Segueの種類はPresent Modallyでいいと思います!

スクリーンショット 2017-12-11 16.43.37.png

そしたらセグエをクリックして、アイデンティファーを設定します。

今回は結果にいくので「toResult」にしました

スクリーンショット 2017-12-11 16.44.11.png

それでは、制限時間が0になったら画面遷移するように設定しましょう。

制限時間のカウントダウン用のタイマーで、もしtimeCountが0より小さくなったら画面遷移するようにしましょう。


ViewController.swift

    @IBAction func start() {

timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { _ in

self.timeCount = self.timeCount - 0.05
self.timeLabel.text = String(format: "%.2f",self.timeCount)
if self.timeCount < 0 {

self.performSegue(withIdentifier: "toResult", sender: nil)
self.timer.invalidate()
}
}

snowTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in

self.createSnow()
}

}


画面遷移しましたか?


結果画面に値を受け渡そう

まず、結果画面に結果を表示させるためのラベルと、表示するポイントの変数(整数)を宣言しましょう。

そしてviewDidLoadで結果をラベルに表示します。


ResultViewCotroller.swift

class ResultViewController: UIViewController {

//結果を表示させるためのラベルと、表示するポイントの変数(整数)を宣言
var snowPoint: Int = 0
@IBOutlet var pointLabel: UILabel!

override func viewDidLoad() {
super.viewDidLoad()

// 結果をラベルに表示
pointLabel.text = String(snowPoint)
}
}


忘れずに関連付けをしよう

でも、このまま実行してもまだ値を受け渡すコードを書いていないのでうまくいきません!

では値を受け渡すコードを書いていきましょう。

値を受け渡すということは、

"結果画面(ResultViewController)のsnowPointに値を代入"できればいいわけです。

セグエを使った画面遷移における値の受け渡しには定石があるので、その手順を紹介します。


ViewCotroller.swift

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

//画面遷移先の結果画面を宣言
let resultViewController = segue.destination as! ResultViewController

//結果画面のsnowPointにゲーム画面のsnowPoint代入
resultViewController.snowPoint = self.snowPoint
}


このprepareというのは「準備する」という意味の英語です。

セグエを準備するとき、つまり画面遷移するときに呼ばれるメソッドです。

segue.destinationというのは画面遷移先という意味で

画面遷移先の結果画面を宣言して、その結果画面(ResultViewController)のsnowPointにゲーム画面(ViewController)のsnowPointを代入しています。

コードは以上になります。

動きましたか??

最後に結果画面にで戻るボタンをつくりましょう!


ResultViewController.swift

    @IBAction func back() {

dismiss(animated: true, completion: nil)
}

そしたら関連付けしましょう

ここまでで基本的なところは完成です。

さぁさぁ最後の


クイズ〜!

アプリを拡張しよう!

例えば…


  • 時間が立つと雪だるまが溶けていくように画像を切り替えよう!(「30秒立つと少し溶けた雪だるまに画像が変わる」など)

  • タイトル画面を作って、ゲームの難易度を設定できるようにしよう!

  • デザインを整えよう!

  • ランキング機能をつくろう!(UserDefaultsなどを使って前の記録を保存しよう!)

  • 雪だるまをタップした位置に移動できるようにしたり、加速度センサを使ってiPhoneの傾きで動かせるようにしよう!

などなど!


最後に

いかがでしたか?

本やサイトではコピー&ペーストや写経になってしまって考える機会がなくなってしまうけれど

考えながらやってるときが一番楽しいですよね。

だからぜひクイズにも挑戦してみてください!

ではよいクリスマスを!