LoginSignup
6
4

More than 5 years have passed since last update.

【Swift】iOSで放置型育成ゲームを作るよ(4) ~BGMなど効果音・環境音関係を管理するクラスを作る~

Last updated at Posted at 2017-07-02

今回のゴール

  • なんとなく寂しいのでBGMと効果音を鳴らすことができるようにする
  • BGMを管理するクラスはシングルトンで実装してみる

今回のキーワード

  • シングルトン
  • 音声ファイル

まず初めに

前回やった修正をdevelopmentにマージしようとしたらなんかコンフリクトしてて、プロジェクトファイルは読み込めないしやべえよやべえよ状態だったけど、ファイルの差分見てたらプロジェクトファイル自体がコンフリクトしててなんとかvimで編集してあれこれして事なきを得る。(gitのコミットログがものすごい汚いことになってしまった・・・)
さてそんなことは置いておいて今日のゴールを目指してガシガシコード書くよ

やったこと

まずはBGMをプロジェクトに追加するよ

  • 今回はBGMと何かをタップした時の音を実装するよ
  • クローバーラボさんのありがたいご厚意によりBGMも素材として提供されているよ
  • 何かを選択した時の効果音は無料音源を探すのに最適な「魔王魂」さんから拝借します(ありがたや:pray:)
  • 「hometown.m4a」、「select.mp3」の2つのファイルをプロジェクトに追加するよ(ma4とmp3なのは特に意味はないよ)
  • ついでにいらなくなったcharacter.gifを消して、SupportingFilesを作成、ImageとSoundにディレクトリを分けて整理するよ スクリーンショット 2017-07-01 18.24.58.png

とりあえず音を流してみるよ

  • 起動時にホームタウン.m4a(分かりやすいようにhometown.m4aにリネームしたよ)が流れるように実装してみるよ
  • 検索ワード「Swift BGM」
  • いくつか良さげな記事があったけど、この記事を参考にさせてもらうよ(音楽再生する簡単な方法)
ViewContoller.swift
import AVAudioPlayer
~~~省略~~~
override func viewDidLoad() {
~~~省略~~~
        do {
            // 音楽ファイルが"howntown.m4a"の場合
            let filePath = Bundle.main.path(forResource: "hometown", ofType: "m4a")
            let audioPath = NSURL(fileURLWithPath: filePath!)
            player = try AVAudioPlayer(contentsOf: audioPath as URL)
            player.prepareToPlay()
        } catch {
            print("Error")
        }

        player.play()
    }

とりあえず鳴ったー! (qiitaにはmovがアップデートできないみたいなので実際に音が出てるかが分からないけど・・)

サウンドを管理するデザインパターン

SoundManger.swift
import Foundation
import AVFoundation

final class SoundManager {

    enum type: String {
        case BGM
        case Select

        func path() -> URL {
            switch self {
            case .BGM:
                return URL(fileURLWithPath: Bundle.main.path(forResource: "hometown", ofType: "m4a")!)
            case .Select:
                return URL(fileURLWithPath: Bundle.main.path(forResource: "select", ofType: "mp3")!)
            }
        }
    }

    static let shared = {
        return SoundManager()
    }()

    private init() {
        do {
            try self.bgmPlayer = AVAudioPlayer(contentsOf: type.BGM.path())
            try self.selectPlayer = AVAudioPlayer(contentsOf: type.Select.path())
        } catch {
            print("Failed To Initialize SoundPlayer")
            self.bgmPlayer = AVAudioPlayer()
            self.selectPlayer = AVAudioPlayer()
        }

        self.setupPlayer()
    }

    private var bgmPlayer: AVAudioPlayer!
    private var selectPlayer: AVAudioPlayer!

    private func setupPlayer() {
        bgmPlayer.numberOfLoops = -1

        // 起動してすぐにBGMが鳴り出して違和感があるので一旦prepareはしないでおく
        // bgmPlayer.prepareToPlay()
        selectPlayer.prepareToPlay()
    }

    func play(_ type: SoundManager.type) {
        switch type {
        case .BGM:
            bgmPlayer.play()
        case .Select:
            selectPlayer.play()
        }
    }

    func pause(_ type: SoundManager.type) {
        switch type {
        case .BGM:
            bgmPlayer.pause()
        case .Select:
            selectPlayer.pause()
        }
    }

    func stop(_ type: SoundManager.type) {
        switch type {
        case .BGM:
            bgmPlayer.stop()
        case .Select:
            selectPlayer.stop()
        }
    }

    func stopAll() {
        bgmPlayer.stop()
        selectPlayer.stop()
    }
}

こんな感じかしら・・・ AVAudioPlayerをtrycatchで囲わなきゃいけないので、error時にとりあえずインスタンス作ってプロパティに代入してるけどなんかものすごい気持ち悪い実装な気がする
まぁとりあえず動くのでこれでいいか!(毎度のこと)

ViewController.swift
    override func viewDidLoad() {
        super.viewDidLoad()

        // キャラクターのインスタンスを生成してViewControllerのプロパティに設定
        valkyrie = Character(name: "【戦乙女】ヴァルキリー", charaImage: UIImage(named: "sozai.png"))

        // imageViewのimageにキャラのimageを設定
        imageView.image = valkyrie?.image
        name.text = valkyrie?.name
        name.adjustsFontSizeToFitWidth = true

        strLabel.text = "攻撃力: \(String(describing: valkyrie!.status.str))"
        defLabel.text = "防御力: \(String(describing: valkyrie!.status.def))"
        lucLabel.text = "天運: \(String(describing: valkyrie!.status.luc))"

        SoundManager.shared.play(.BGM)
    }

使う側はSoundManger.sharedで再生したいBGMのTypeを指定してあげればよいので、これはこれで使いやすい気もする

結論

次回

アプリの起動画面の設定とゲームのシステムについて考えるよ

最後に

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

6
4
2

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