LoginSignup
4
6

More than 5 years have passed since last update.

【Swift】iOSで放置型育成ゲームを作るよ(2) ~素材画像から一部を切り取ってgif化する~

Last updated at Posted at 2017-06-22

今回のゴール

  • 素材画像から一部分を切り取る
  • 切り取った数枚のUIImageを重ね合わせてgif化して綺麗な画像にする output-ffmpeg-palette.gif

今回のキーワード

  • UIImage
  • gif
  • Extension

やったこと

素材画像を見直すよ

  • 前回で使ったgif画像は実は素材画像を自分で切り貼りして無理やり作ったものだよ
  • だからかなり荒いものになっていたよ
  • 大量にある素材画像からgif化できないか探してたらこんなものがあったよ
  • どうやら左上から右方向に3枚ごとにまとめればgifになりそうなので、この画像の左上から3枚目まで1枚ずつ切り抜いてgif化できないかなと考えたよ

image.png

画像の一部の切り抜き方をググるよ

まず1枚切り抜いて表示してみるよ

  • sozai.pngという名前でプロジェクトに素材画像を追加するよ
  • UIImage+Cropping.swiftというExtension用のファイルを追加して先ほどのコードを追記するよ
UIImage+Cropping.swift
import Foundation
import UIKit

extension UIImage {
    func cropping(to: CGRect) -> UIImage? {
        var opaque = false
        if let cgImage = cgImage {
            switch cgImage.alphaInfo {
            case .noneSkipLast, .noneSkipFirst:
                opaque = true
            default:
                break
            }
        }

        UIGraphicsBeginImageContextWithOptions(to.size, opaque, scale)
        draw(at: CGPoint(x: -to.origin.x, y: -to.origin.y))
        let result = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return result
    }
}
  • 左上の座標(x: 0.0, y: 0.0)から(width: 64.0, height: 64.0)分のCGRectを作って関数に代入するよ (色々調べた結果、1枚1枚は64 * 64のサイズでできているよ)
ViewController.swift
~~~省略~~~
    override func viewDidLoad() {
        super.viewDidLoad()

        // 一旦コメントアウト
//        imageView.image = UIImage.gif(name: "character")

        // まずは素材画像からUIImageを宣言する
        let image = UIImage(named: "sozai.png")

        // 切り取る範囲を指定するCGRectを宣言する
        let croppingRect = CGRect(x: 0.0, y: 0.0, width: 64.0, height: 64.0)

        // imageをcroppingRectで切り抜いて、返り値をimageView.imageに代入する
        imageView.image = image?.cropping(to: croppingRect)
    }
~~~省略~~~
  • キマシタワー 綺麗に切り抜けてるっぽい image.png

UIImageをgifにする方法が分からなかったのでググるよ

  • 検索ワード「UIImage gif化
  • 色々参考になる記事はあったけど、分かりやすいこの記事([iOS][swift] アニメーションGIFを作る)を参考にするよ
  • 前回ダウンロードしてきたUIImage+Gif.swiftに追記していくよ
    • (人のライブラリいじっていいものなんだろうか・・:thinking: )
  • 必要な処理が2つあるので分かりやすいように関数を2つに分けるよ
    • UIImageからgif用の3枚を切り抜く処理(createCharacterImagesForGif)
      • これはExtensionの中でしか使わないようにprivateにしたよ
    • UIImageの配列からgifファイルのURLを生成する処理(createGifURL)
      • これはほぼコピペだけど、動けばいいか!の精神で乗り切るよ
UIImage+Gif.swift
import UIKit
import ImageIO
import MobileCoreServices

~~~省略~~~

extension UIImage {

~~~省略~~~
    // MARK: 自身のimageからgif用の配列を生成するメソッド
    private func createCharacterImagesForGif() -> [UIImage]? {
        let cropX = 64  // 切り抜くwidth
        let cropY = 64  // 切り抜くheight

        // 切り抜いた画像を保存しておく配列
        var images: [UIImage] = []

        // 3枚分for文を回す
        for i in 0..<3 {
            guard let image = self.cropping(to: CGRect(x: i * cropX, y: 0, width: cropX, height: cropY)) else {
                // クロッピングに失敗したらnilをreturnする
                return nil
            }

            // 切り抜けたUIImageを配列に追加
            images.append(image)
        }

        // うまいこといけば3枚分のUIImageが入った配列がreturnされる
        return images
    }

    // MARK: UIImageの配列からgifを生成し、保存したURLを返すメソッド
    func createGifURL() -> URL? {
        guard let images = self.createCharacterImagesForGif() else {
            return nil
        }

        let charaImages: [CGImage] = images.map{$0.cgImage!}

        guard let url = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(NSUUID().uuidString).gif") else {
            print("url化失敗")
            return nil
        }
        guard let destination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeGIF, charaImages.count, nil) else {
            print("CGImageDestinationの作成に失敗")
            return nil
        }

        let properties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]]
        CGImageDestinationSetProperties(destination, properties as CFDictionary)

        let frameProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: 0.25]]
        for image in charaImages {
            CGImageDestinationAddImage(destination, image, frameProperties as CFDictionary)
        }

        if CGImageDestinationFinalize(destination) {
            print("GIF生成が成功")
            return url
        } else {
            print("GIF生成に失敗")
            return nil
        }
    }
}
ViewController.swift
~~~省略~~~
    override func viewDidLoad() {
        super.viewDidLoad()

        // 一旦コメントアウト
//        imageView.image = UIImage.gif(name: "character")

        // まずは素材画像からUIImageを宣言する
        let image = UIImage(named: "sozai.png")

        // 素材のimageからgifのURLが生成できたかをnilチェックする
        if let url = image?.createGifURL() {
            // urlが生成されていたらimageViewのimageに代入
            imageView.image = UIImage.gif(url: url.absoluteString)
        }
    }
~~~省略~~~

できた!前回のと比較すると一目瞭然:relaxed:

今回
output-ffmpeg-palette.gif

前回
ede5a1a7-de78-77a8-cb36-ff04249e84e8.gif

結果

余談

  • 他のgif画像も表示してみた

output-ffmpeg-palette.gif
- どうやら全てがgifになるわけじゃなかった様子だよ
- どう使ったらいいか分からないgifもあるよ・・

次回 2017/6/25追記

キャラクターをクラス化してViewControllerから分離するよ
http://qiita.com/nasteng/items/ed2a3974b4bafdb5f653

最後に

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

4
6
6

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
4
6