LoginSignup
15

More than 5 years have passed since last update.

【Swift】iOSで放置型育成ゲームを作るよ(1) 〜ドット絵を一定間隔で動かす〜

Last updated at Posted at 2017-06-21

今回のゴール

  • UIImageViewにgifを描画して動かす
  • UIImageView自体を1秒間に少しずつ左右に動かす

output-ffmpeg-palette.gif

今回のキーワード

  • UIImageView
  • gif
  • UIButton
  • CGAffineTransform
  • Timer

やったこと

新規プロジェクトを作成するよ

  • 省略
    • アプリ名は適当にデジモン+艦これで「Dejikore」にしたよ

画面を横のみを許可する

  • デジモン = 横画面というイメージなので、まずは画面を横画面表示のみ許可するようにするよ
    • プロジェクトを選んでTarget → Deployment Info → Device OrientationをLandscape leftとLandscape Rightのみにチェックを入れるよ スクリーンショット 2017-06-20 21.29.20.png

Main.storyboardにUIImageViewを置いてViewControllerと紐づけるよ

  • とりあえず何も考えずにimageViewという名前で接続するよ スクリーンショット 2017-06-20 21.33.37.png

描画する用のgifを用意して、imageViewに紐づける

今回使う素材
image.png
素材はクローバーラボ株式会社様ご提供の素材を利用させていただきます
ありがとうございます:pray:
http://cloverlab.jp/
http://yurudora.com/tkool/

  • 素材を用意したらMacのプロジェクトフォルダ側にコピーしてからXcodeにD&Dしてファイルを追加するよ
  • 追加したらインスペクタからimageViewのimageとして追加してビルドしてみるよ

そのままじゃgifアニメが動かないのでググるよ

  • 検索ワード「UIImageView gif
  • どうやらそのまま突っ込むだけじゃgifアニメは動いてくれない様子
  • こちらの記事を参考にダウンロード・UIImage+gif.swiftをプロジェクトに追加して動かしてみるよ
  • UIImage.gifのurlは追加した画像と同じファイル名を記述するよ
  • ビルド!
  • 動いた:thumbsup: output-ffmpeg-palette.gif
ViewController.swift
class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        imageView.image = UIImage.gif(name: "character")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

このimageViewをButtonタップしたら動かすようにするよ

  • とりあえずUIButtonを設置してIBOutlet接続するよ
    • スクリーンショット 2017-06-20 22.33.31.png
viewController.swift
class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        imageView.image = UIImage.gif(name: "character")
    }


    @IBAction func move(_ sender: UIButton) {
        // キャラ画像のframeを変数に格納
        var frame = imageView.frame

        // x方向に50ほど移動量を追加して変数に格納
        let moveX = frame.origin.x + 50

        // moveXを使ってframeを更新
        frame = CGRect(x: moveX, y: frame.origin.y, width: imageView.frame.width, height: imageView.frame.height)

        // imageViewのframeに代入
        imageView.frame = frame
    }
~~~省略~~~
  • これでとりあえず動くけど画面外まで行って帰ってこなくなる上に進む方向に画像が向いていないよ・・・ output-ffmpeg-palette.gif

画面内で動くように修正するよ

  • 画像が右向きなのか左向きなのかを判定するフラグ「isLeft」を追加するよ
  • 左を向いていればマイナス方向(←)に、逆ならプラス方向(→)に動くようにするよ
ViewController.swift
class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    private var isLeft: Bool = true    //  キャラが左を向いているかどうかのフラグ

    override func viewDidLoad() {
        super.viewDidLoad()

        imageView.image = UIImage.gif(name: "character")
    }


    @IBAction func move(_ sender: UIButton) {
        // キャラ画像のframeを変数に格納
        var frame = imageView.frame

        // 移動量を定義して変数に格納
        let moveX: CGFloat = 50.0

        // 画面の左端(x座標が0)に到達したら
        if frame.origin.x < 0 {
            // 右を向くことを表すためにフラグをfalseにする
            isLeft = false

        // もしキャラのx座標が右端(画面の最大x座標 - imageViewのサイズ分)に到達したら
        } else if frame.origin.x > view.frame.maxX - imageView.frame.width {
            // 左を向くことを表すためにフラグをtrueにする
            isLeft = true
        }

        // 左を向いていれば左に動かすためにx軸の値をマイナス、逆ならプラスする
        isLeft ? (frame.origin.x -= moveX) : (frame.origin.x += moveX)

        // imageViewのframeに代入
        imageView.frame = frame
    }
  • これで画面外に出ることはなくなったけどまだ一方向しか画像が向いてないよ output-ffmpeg-palette.gif

imageViewをどうやれば反転させられるかをググるよ

  • 検索ワード「UIImageView 反転
  • UIViewを反転させるの記事を参考にCGAffinetransformを使ってUIImageViewを反転させるよ
  • isLeftの値が変わる時にCGAffineTransformでx軸の値を変えてあげればよさげだよ
ViewController.swift
~~~省略~~~
        if frame.origin.x < 0 {
            // 右を向いてることを表すためにフラグをfalseにする
            isLeft = false

            // 右を向いたら反転させる
            imageView.transform = CGAffineTransform(scaleX: -1 , y: 1)

        // もしキャラのx座標が右端(画面の最大x座標 - imageViewのサイズ分)に到達したら
        } else if frame.origin.x > view.frame.maxX - imageView.frame.width {
            // 左を向いていることを表すためにフラグをtrueにする
            isLeft = true

            // 左を向いたら正しい方向にする
            imageView.transform = CGAffineTransform(scaleX: -1 , y: 1)
~~~省略~~~

これでもよさげだけど、なんとなくプロパティ監視(willSet)とか使ってみるよ

ViewController.swift
~~~省略~~~
    private var isLeft: Bool = true {
        willSet(newValue) {
            // 新しい値がtrueだったら正しい方向に、falseだったら反転させる
            if newValue {
                imageView.transform = CGAffineTransform(scaleX: 1 , y: 1)
            } else {
                imageView.transform = CGAffineTransform(scaleX: -1 , y: 1)
            }
        }
    }
~~~省略~~~
  • うまいこと動いた!

output-ffmpeg-palette.gif

ボタンをポチポチするのめんどくさいからTimerを使って一定間隔ごとに実行するようにするよ

  • まずIBOutletされているmoveの接続を外してただのfunctionに修正するよ
  • ViewControllerにstartTimerという名前の関数を作ってButtonとIBOutlet接続するよ
  • ViewControllerのプロパティとしてTimerを宣言するよ
  • startTimer内でtimerを初期化、timer.fire()でタイマーを動かすよ
  • startTimerではタイマーが動いていればタイマーを止めて、そうでなければ再生成して動かしているよ
  • ついでにタップした時にボタンのタイトルを変えるようにしているよ
ViewController.swift
class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    private var timer = Timer()    // Timerの宣言

~~~省略~~~

    func move(_ timer: Timer) {
        // キャラ画像のframeを変数に格納
        var frame = imageView.frame

        // 移動量を定義して変数に格納
        let moveX: CGFloat = 50.0

        // 画面の左端(x座標が0)に到達したら
        if frame.origin.x < 0 {
            // 右を向いてることを表すためにフラグをfalseにする
            isLeft = false

            // もしキャラのx座標が右端(画面の最大x座標 - imageViewのサイズ分)に到達したら
        } else if frame.origin.x > view.frame.maxX - imageView.frame.width {
            // 左を向いていることを表すためにフラグをtrueにする
            isLeft = true
        }

        // 左を向いていれば左に動かすためにx軸の値をマイナス、逆ならプラスする
        isLeft ? (frame.origin.x -= moveX) : (frame.origin.x += moveX)

        // imageViewのframeに代入
        imageView.frame = frame
    }

    @IBAction func startTimer(_ sender: UIButton) {
        // タイマーが動いていれば
        if timer.isValid {
            // タイマーを止める
            timer.invalidate()
            sender.setTitle("start", for: .normal)
        } else {
            sender.setTitle("stop", for: .normal)
             // そうでなければ動かす
            timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(move(_:)), userInfo: nil, repeats: true)
            timer.fire()
        }
    }

LGTM:thumbsup:
output-ffmpeg-palette.gif

結果

疲れた!とりあえず今回の目標は達成!!
今日の作業ブランチはこちら
https://github.com/nasteng/Dejikore/tree/update-gif-moving

2017/6/22追記

次回
素材画像から一部を切り取ってgif化する
http://qiita.com/nasteng/items/a46315145abaa626adee

最後に

ご指摘・リファクタ大歓迎です! :pray:

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
15