たぬきすのアプリ開発日記

アプリ開発の備忘録兼日記

【Android】ShareIntentで共有先のアプリやアプリごとに共有するデータをカスタマイズできるクラスを作ってみた

どうも。たぬきすです。

最近、仕事でAndroidで画像やテキストをSNSに共有する機能を作成することになり、思いのほか詰まったので記事にしようと思いました。
単純に共有するだけなら数行のコードで簡単に実装できるのですが、タイトルに示したような細かい条件をつけると実装に工夫が必要になります。

一番簡単な方法はこんな感じ

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // シェアインテントの作成
        val shareIntent = Intent(Intent.ACTION_SEND)
        shareIntent.setType("text/plain")   //共有する内容のタイプ
        shareIntent.putExtra(Intent.EXTRA_TEXT, "test")     // テキストを共有
        val chooser = Intent.createChooser(shareIntent, "共有")

        var shareButton = findViewById<Button>(R.id.btShare)

        // ボタンを押してシェア開始
        shareButton.setOnClickListener {
            startActivity(chooser)
        }

    }
}

実行結果は下の通り

f:id:tanukis:20210823065221g:plain

上のGIFのようにこれだけでもシェアはできるのですが、特定のアプリのみに共有することはできませんし、ツイッターのみにハッシュタグをつけるといったカスタマイズもできません。

なので、今回はこれらの目的を実現できるクラスを作成してきたので紹介したいと思います。

下に参考にしたURLを貼っておきます。

https://codehero.jp/android/9730243/how-to-filter-specific-apps-for-action-send-intent-and-set-a-different-text-forcodehero.jp

実装

下がそのクラスになります

class ShareIntentCreator(private val packageManager: PackageManager) {

    private val map = HashMap<String, Intent>()

    public fun create(): Intent {

        val plainIntent = Intent()
        plainIntent.setAction(Intent.ACTION_SEND)
        plainIntent.setType("")

        val sendIntent = Intent()
        sendIntent.setAction(Intent.ACTION_SEND)
        sendIntent.setType("*/*")

        val openInChooser = Intent.createChooser(plainIntent, "共有")
        val resInfoList = packageManager.queryIntentActivities(sendIntent, 0)

        val intentList = ArrayList<LabeledIntent>()

        for (resInfo in resInfoList) {
            val name = resInfo.activityInfo.name

            var targetIntent: Intent? = null

            for(targetPackageName in map.keys) {
                if(name.toLowerCase(Locale.getDefault()).contains(targetPackageName.toLowerCase(Locale.getDefault()))) {
                    targetIntent = map.get(targetPackageName)
                    break;
                }
            }

            if(targetIntent == null) {
                continue
            }

            targetIntent.setComponent(ComponentName(resInfo.activityInfo.packageName, resInfo.activityInfo.name))
            intentList.add(LabeledIntent(targetIntent, resInfo.activityInfo.packageName, resInfo.loadLabel(packageManager), resInfo.icon))

        }


        val extraIntents = intentList.toTypedArray()
        openInChooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents)

        return openInChooser
    }

    public fun addTargetActivity(targetName: String, intent: Intent) {
        map.put(targetName, intent)
    }

}

このクラスがやっていることをざっくり説明すると、

共有するアクティビティがない空のchooserを作成

共有できる全てのアクティビティをもつインテントからアクティビティ情報を取得

取得したアクティビティ情報からchooserに含めたいものをLabeledIntentにしてリスト化する

空のchooserにリスト化したLabeledIntentを追加する

といった流れになっています。

使い方はこんな感じ

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val shareIntentCreator = ShareIntentCreator(this.packageManager);
        val twitterIntent = Intent()
                .setAction(Intent.ACTION_SEND)
                .putExtra(Intent.EXTRA_TEXT, "#ツイッター")
                .setType("text/plain")
        shareIntentCreator.addTargetActivity("twitter.composer", twitterIntent)

        val gmailIntent = Intent()
                .setAction(Intent.ACTION_SEND)
                .putExtra(Intent.EXTRA_TEXT, "Gメール")
                .setType("text/plain")
        shareIntentCreator.addTargetActivity("gmail", gmailIntent)

        val chooser = shareIntentCreator.create()

        var shareButton = findViewById<Button>(R.id.btShare)

        // ボタンを押してシェア開始
        shareButton.setOnClickListener {
            startActivity(chooser)
        }

    }
}

addTargetActivityメソッドにアクティビティ名と共有内容を追加してcreateメソッドでシェアインテントを作成するだけです。
あとはインテントを開始すれば下のような結果になります。

f:id:tanukis:20210826221310g:plain

問題点

この方法はAndroid9までは全てのアクティビティを表示できていたのですが、Android10から2つまでしか表示できなくなるといった仕様になったみたいです。

https://issuetracker.google.com/issues/134367295?pli=1

stackoverflow.com

上のナレッジでは代替手段として共有しないアクティビティを選択して共有から除外する方法を提案していますが、この方法だとアプリごとに共有するデータを設定することはできない気がするので、今回の目的は達成できませんね
いっそのこと共有画面を自作してしまうのもありかもしれません。めんどくさいのでそこまでやる気はないですが。

今回はこの辺で終わろうと思います。誰かの参考になれたら幸いです。

それではまた!

【Swift】Delegateの使い方について

どうも。たぬきすです。

皆さんはdelegateというのをご存知ですか?

簡単に言えばデザインパターンの一種で、クラスの処理の一部を他のクラスに任せることができるので、任せる相手に応じて実行する処理を自由に変えることができるという特徴があります。

delegateの詳しい解説はネット上に数多く存在するので、知っているという人は多いのではないでしょうか。

delegateとは何か詳しく知りたい人は、私が参考にした記事のリンクを貼ったのでよければ参考にしてください。

SwiftにおけるDelegateとは何か、なぜ使うのか - Qiita

  ここでは、delegateのことは知っているけど使いどころがわからないというひとに向けて、私がdelegateを実装して便利だったケースを紹介します。参考になれば嬉しいです。

実装方法

ケースを紹介する前に実装方法についてまとめておこうとおもいます。下は委託する側とされる側のクラスのコードです。

protocol DelegateSampleDelegate: AnyObject {
    func sayHello(str: String)
}

class DelegateSample {
    
    // 委託される側を保持する
    weak var delegate: DelegateSampleDelegate?
    
    func sayHello(){
        let str = "Hello"
        
        // ここで処理を委託する
        delegate?.sayHello(str: str)
    }
}

class Main: DelegateSampleDelegate {
    
    private var delegateSample = DelegateSample()
    
    func main() {
        // delegateSampleに委託させる
        delegateSample.delegate = self
        delegateSample.sayHello()
    }
    
    // ここで委託された処理を実行する
    func sayHello(str: String) {
        print(str)
    }
}

let main = Main()
main.main()

上の例でいうと「DelegateSample」が委託する側,「Main」が委託される側になります。
委託する側のクラスに必要なのは

  • プロトコルを用意し、デリゲートメソッドを定義する
  • 委託される側を保持しておくためのメンバ変数を用意しておく
  • 処理を委託したいタイミングにデリゲートメソッドを呼ぶ

委託される側のクラスに必要なのは、

  • 委託する側用に用意したプロトコルに準拠する(クラスの継承と同じ要領)
  • 委託する側に委託される側を保持させる
  • プロトコルで定義されているデリゲートメソッドを用意する

になります。 ちなみに、委託する側のdelegate変数はweakにしておくことをおすすめします。 前にビューコントローラに処理を委託させたときにweakをつけなかったことが原因でコントローラが破棄されず、メモリリークを起こした経験があります。

今回はシンプルなデリゲート実装を例に挙げました。 しかし、この例だと「DelegateSampleのsayHelloメソッドに戻り値を用意するのとどう違うんだ?」と疑問に思うと思いますが、実は関数だけでは対応が難しい場面があります。 私の経験でいうと

  • DelegateSampleでイベントが起き、その内容をMainに伝えたいとき
  • DelegateSampleで非同期処理を行い、結果をMainに伝えたいとき

がそれに該当しました。

具体的にいうと、前者は定期実行を行うクラスで、後者はWebAPIなどの非同期処理を行うクラスです。

定期実行を行うクラス

下がそのサンプルになります

protocol SampleServiceDelegate: AnyObject {
    func countUpdate(count: Int)
}

class SampleService {
    
    // 処理を任せる側のdelegateを保持する
    weak var delegate: SampleServiceDelegate?
    private var timer = Timer()
    private var count = 0
    
    func start(timeInterval: Double) {
        timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(update), userInfo: nil, repeats: true)
    }
    
    func stop() {
        timer.invalidate()
    }
    
    // 指定したtimeInterval秒ごとに呼び出される
    @objc func update(){
        count += 1
        
        // ここで処理を委託する
        delegate?.countUpdate(count: count)
    }
    
}

class Main: SampleServiceDelegate {
    
    private var sampleService = SampleService()
    
    func main() {
        sampleService.delegate = self
        sampleService.start(timeInterval: 5.0)
    }
    
    // 委託された処理を実行する
    func countUpdate(count: Int) {
        print(count)
    }
    
}

let main = Main()
main.main()

上の例では、timeIntervalで指定した時間ごとにカウントをするという定期実行をするクラス「SampleService」を用意しました。
このとき、カウントが更新されるたびにカウント数をMainで使用したいとします。
上の例のMainクラスはSampleServiceから処理を委託されているので、カウントが更新されるごとにカウント数を受け取ることができ、Mainクラスの中で更新後の処理をすることができます。
ここで、delegateを使わずにカウントを取得しようとしたとき、どのように実装すれば同じことができるでしょうか。
私がパッと思いついたのは

  • カウントが更新された後に行いたい処理を「SampleService」クラスの「update」メソッド内に記述する
  • 「Main」クラスで定期的に「SampleService」クラスの「count」の値を取りにいかせる

の二つです。しかし、この二つの実装は問題があり、前者では「SampleService」クラスの汎用性がなくなりますし、後者では、Mainに実装する機能が増える上に更新されたタイミングでカウントを使用することができません。
delegateを使うおかげで、「SampleService」クラスが汎用性を保ったまま他のクラスで「SampleService」からのイベントを簡単に取得することができるわけです。

非同期処理を行うクラス

下がそのサンプルになります

protocol SampleAsyncDelegate: AnyObject {
    func result(result: Int)
}

class SampleAsync {
    
    // 処理を任せる側のdelegateを保持する
    weak var delegate: SampleAsyncDelegate?
    
    func calcAddAsync(num1: Int, num2: Int) {
        
        DispatchQueue.global().async {
            let result = num1 + num2

            // ここで処理を委託する
            self.delegate?.result(result: result)
        }
        
    }
    
}

class Main: SampleAsyncDelegate {
    
    private var sampleAsync = SampleAsync()
    
    func main() {
        sampleAsync.delegate = self
        sampleAsync.calcAddAsync(num1: 1, num2: 2)
    }
    
    // 委託された処理を実行する
    func result(result: Int) {
        print(result)
    }
    
}

let main = Main()
main.main()

上の例では、非同期処理を伴う「SampleAsync」クラスの「calcAddAsync」メソッドを「Main」クラスで呼び出し、処理結果をdelegateメソッドで受け取るという処理をしています。
できれば「calcAddAsync」メソッドの戻り値で処理結果を返したいところですが、非同期の処理が絡む場合メソッド実行中に処理結果を受け取ることはできません。
ここで、delegateを利用することで非同期処理が終わったタイミングで「Main」クラスに実行結果を返すことができます。
「このクラスのメソッドでどうしても非同期処理をしなければいけないけど処理結果は別クラスで使用したい」という場面に私は結構多く遭遇するのですが、delegateで簡単に解決できるので私はこの方法をよく使っています。
解決方法はこればかりではないみたいなので、もっとよい方法があるかもしれませんが。

あとがき

以上が私がdelegateを使ってみて便利だった実装でした。
極端な話、動くプログラムを作るというだけならdelegateを利用しなくても実装はできます。
ただ、クラスの機能を明確にし、それぞれが独立した汎用性の高いクラスを作ろうとしたときdelegateのようなデザインパターンが必要になってきます。
デザインパターンはよりよい設計を目指し続けた先人たちが編み出してきた、工夫の結晶なんだなあとデザインパターンを利用しているとつくづく思います。
今回紹介した実装はほんの一例でしかありませんが、参考になれば幸いです。

お久しぶりです

どうも。たぬきすです。

社会人になってからブログを更新しなくなって、気づけばもう最後の更新から1年以上経ってますね(汗

この一年の間に実は色々とあって、現在は新卒で入社した会社を辞めて業務委託中心のweb系企業さんのところで働かせてもらってます。

面接の時に提出したポートフォリオがモバイルばかりだったせいか任される仕事はandroidiosの開発が中心です。いずれはサーバーサイドもやってみたいなあ。

ともあれ、この備忘録自体は今後も続けていきたいと思っています。ただ、今後は業務で使うような実用的な内容が多くなると思います。今勉強していることを取り上げていくつもりなので今のところはkotlinとかswiftとかモバイルが中心になるのかな。

ブログを始めたときはUnityの記事ばっかですが、最近はUnityを全然いじってないのでほとんど取り上げることがなくなりそうです。ゲーム作りは楽しかったのでまた再開するかもしれないですが。

ブログを投稿しなくなって長くなりましたが、これから定期的に更新して行こうと思っています。

ではまた次の更新で。バイバイ!

音声素材で困っている人必見!ビット効果音を自動生成する「Bfxr」が非常に便利だったのでご紹介

どうも!たぬきすです。

現在横スクロールシューティングゲームを作っている最中で、効果音が欲しくなってきたこの頃です。

しかし、シューティングゲームともなると多種多様な効果音を揃える必要があり、フリー素材からピッタリの効果音を一つ探すのも大変です。

ですが、このBfxrというフリーソフトならワンクリックで音声を自動生成してくれるので、多種多様な効果音をこのソフト一つで簡単に作ることができます。このソフトで作られた効果音に関しては完全フリーなので、どんな用途でも使用可能です。下のURLからダウンロードできます。

https://www.bfxr.net/

ただし、作れるのはビット効果音だけなので、作品のデザインと合うかどうかは検討が必要です。

実際に使ってみる

Bfxrをダウンロードして開くと下のような画面が表示されます。

f:id:tanukis:20200223103033p:plain

ワンクリックで作れる音声にはそれぞれテーマがあり、上から順に

  • コイン
  • 発砲
  • 爆発
  • パワーアップ
  • ダメージ
  • ジャンプ
  • 選択
  • ランダム

となっています。一番下にあるMutationというボタンは上のボタンで作成した音声を若干変化させた音声を作成でき、私の場合は自分が欲しいのに近い音声が現れた時に、より近い音声にするために使用しています。もちろん上の画面中央にあるパラメータも自分でいじれるので細かい部分にまでこだわった効果音が作れます。

 実際に使ってみた動画がこちら


bfxr sample

動画のように、ワンクリックで効果音を作成できます。気をつけて欲しいのは、動画中央上のMixerを選択している時に作成ボタンを押すと次々と効果音を生成することができますが、Synthを選択しているときは選択されている効果音が上書きされます。もし間違えて上書きした時は左端中央あるRevert Synthで元に戻せます。逆に変更を保存したい場合はApply Synthで保存できます。

気に入った効果音が作れ、効果音を保存したい場合、Synthを選択してから左端下の保存したい効果音をクリックして選択し、右端下のExport Wavボタンをクリックしてwavファイルとして保存できます。

実際にこのソフトで作った効果音を自分のゲームに取り入れた結果、こんな感じになりました


gamePlay 2020/2/23

ビット音声であることさえ許容できれば、このソフト一本で効果音の大部分は網羅できますし、何より音声作成に関する知識ゼロの人でも簡単に効果音を作ることができるのは非常に便利でした。フリーソフトなので、効果音にお困りの方はぜひ使ってみてはどうでしょうか。

 

ではまた!

【Unity】【開発日記】障害物を増やしたりカメラワークを取り入れたりしてみた

どうも!たぬきすです。

現在、横スクロールシューティングゲームを作っている最中で、アニメーションも追加でき、徐々にクオリティが上がったような気がします。内容はこんな感じ、


2Dshooting

ただ、まだまだゲームとしては単調です。今はまだ平地に敵を振りまいているだけですので、これから障害物をどんどん追加していきます。

障害物の追加

まず最初に考えたのは、2Dシューティングの定番のこの二つです。

f:id:tanukis:20200216082057p:plain

f:id:tanukis:20200216082128p:plain

みての通り木箱と爆発物です。木箱は結構凝って描いたので自信作です。爆発物は、色を赤くして中央に!マークを付ければそれだけでそれっぽくなります。あとはこれらにコンポーネントスクリプトを持たせてやったらこんな感じになりました。爆発物の実装は記事にしようか考え中です。

 


GamePlay ExplosiveCan & woodBox 2020/2/16

 

これらをバラまくだけでゲームのバリエーションが増えましたが、弾が前方からしか飛んでこないのは面白くないですね。もっと高低感を出した方がプレイ中の緊張感も上がる気がします。ここで思いついたのが、高台とヘリコプターです。この二つの実装で高いところに敵を配置します。実装したところ、こんな感じになりました。


GamePlay WoodStand 2020/2/16


GamePlay Heri 2020/2/16

ヘリコプターの挙動はこだわりました。プレイヤーの座標を受け取って倒されるまでつきまとってきます。ですが、ただプレイヤーの座標から位置を変えるだけではちっともヘリコプターらしい動きをしないので、2秒おきに前後左右に機体が移動するようにしました。これだけでかなりヘリコプター感を演出できます。

とりあえず障害物のバリエーションはこれくらいにしとこうと思います。他にいいアイデアが思いついたら追加しようと思います。こればっか考えているとキリがなくて先に進めなくなりますから笑。

カメラワーク

あと前々からどうにかしたいと思っていたのがプレイヤーに追従するカメラの挙動です。カメラがプレイヤーにぴったりくっついてくるのは違和感しかないんですよね。何か方法はないものかとネットで情報を探した結果、良記事を見つけてきました。それがこちら

qiita.com

この記事では3つのカメラワークの紹介をしていますが、その中でもLerp減衰は私が求めていたカメラワークでした。Lerp減衰が簡単にいうと、プレイヤーの移動に遅れてカメラがついてくるカメラワークです。2Dスクロールゲームだと見かけないことがないくらいよく使われる手法だと思います。実装方法はこの記事を読めばできますし、何より数行のコードでできます。

今回の改良の結果

こんな感じで改良を重ねた結果、こんな感じになりました。


GamePlay 2020/2/16

期待した通り、敵の攻撃に高低感が生まれ、より緊張感のあるゲームになりました。敵の弾丸を避けるための木箱を利用もゲーム攻略の幅を広げられたように感じます。何より、カメラワークを改善するだけでゲームの臨場感がここまで上がるとは思っていませんでした。

しかし、まだまだゲームとして成り立たせるには足りないものばかりです。これから効果音、UI、背景、メニュー、ゲームレベルを全て完成させなければなりません。一人でゲーム作るのは楽しいけど大変です汗。まあ焦らずぼちぼちやっていきます。また進んだら記事にしようと思います。

ではまた!

 

 

【Unity】【開発日記】ドット絵アニメーションをゲームに反映させてみた その2

どうも!たぬきすです。

この記事は前回の続きです。前回の記事もよければ読んでみてください。

tanukis.hatenablog.com

前回の記事では、UnityのAnimationを使って一動作のアニメーションを作るところまでやりました。今回は、Animationで作成した複数のアニメーションをAnimatorを使ってアニメーション遷移をコントロールしてみましょう。

それでは作成開始!

作成開始

前回はプレイヤーが待機しているアニメーションを作成しましたが、今度は追加でダッシュのアニメーションをAnimationで作成していきましょう。

前回と同様に、Animator、Animationのタブを開いてAnimationタブを表示し、GameObjectのPlayerをクリックして選択します。

f:id:tanukis:20200211084932p:plain

すると、前回編集したPlayerの待機アニメーションの編集画面が表示されました。今は待機アニメーションだけですが、ここからどんどんアニメーションを追加していきます。

画像右上にある現在編集しているアニメーションの名前(この記事ではNew Animation)を選択すると、下のような画面になります。

f:id:tanukis:20200211085613p:plain

ここで、Create New Clipを選択すると新しいアニメーションの新規作成ができます。アニメーションの名前は作りたい動作の名前をつけましょう。この記事ではダッシュアニメーションを作成するのでRunAnimationとしました。新規作成をすると下の編集画面が現れます。これでRunAnimationが編集できるようになりました。前に作ったアニメーションを編集したい場合は、右上のアニメーションの名前をクリックすれば選択できます。

f:id:tanukis:20200211090237p:plain

あとは思い思いのアニメーションを作成しましょう。前回と同様に、Add PropertyからSprite Renderer→Spriteを追加してパラパラ漫画的にアニメーションを作成します。

f:id:tanukis:20200211091146p:plain

編集の基本的な機能を説明します。

アニメーションのコマの追加はUnityに保存している画像ファイルをキャラクターの絵が表示されている帯の上にドラッグ&ドロップするとできます。

コマ送り一つ一つのスピードは追加されたコマの絵を左右にドラッグすることで変更できます。

アニメーションのコマを削除したい場合は、削除したいコマの絵を右クリック→Delete Keyするとできます。


RunAnimation

アニメーションが完成したらGame(もしくはScene)タブでアニメーションの確認ができます。GameObjectをGame、Sceneで見える位置に配置して、Animationタブの再生ボタンをクリックします。そうすればアニメーションの動作確認ができます。参考に上の動画を用意しました。

ここで、Animatorタブを選択してみると先ほど追加したRunAnimationがAnimatorに追加されています。

f:id:tanukis:20200211095534p:plain

RunAnimationも完成したのでここからはAnimatorによるアニメーションの遷移制御を行いたいと思います。

アニメーションの制御

f:id:tanukis:20200211100804p:plain

まずは、アニメーションが遷移する方向を決めます。今回は、条件に応じて待機(NewAnimationではわかりづらいのでIdleに変更)とダッシュを行き来させたいので、両方に伸びる矢印(Transition)を設定します。設定方法は、アニメーションを右クリック→Make Transitionを選択し、矢印を伸ばしたいアニメーションをクリックします。

それができたら、今度はアニメーションを切り替えるフラグ(名称が分からないのでここではフラグとします)の設定をします。

f:id:tanukis:20200211102015p:plain

Parametersを選択し、検索バーの隣にあるプラスをクリックすると、Float、int、Bool、Triggerの四つを選択できます。この記事ではBoolを使います。

f:id:tanukis:20200211102717p:plain

Boolを選択するとNew Boolという名前でフラグが作成されるので、今回はRunと変更しました。

それができたら、今度はフラグの状態に応じてアニメーションを変化させる設定をしていきましょう。

f:id:tanukis:20200211103540p:plain

先ほど設定したIdle→RunAnimationの矢印(Transition)をクリックするとInspectorが画像右のように表示されます。

右下にConditionsという設定項目がありますが、このConditionsでアニメーションの遷移条件を設定することができます。Conditionsの右下にあるプラスをクリックすると条件が追加されます。この記事の設定では、画面右下のCondition設定のように、先ほど追加したJumpフラグがtrueになったときに待機→ダッシュに遷移するという条件を付与しました。

あと、条件が満たされたらすぐにアニメーションを遷移して欲しいので、Inspector内のHas Exit Timeのチェックマークは外します。

f:id:tanukis:20200211105005p:plain

同様に、RunAnimation→Idleの矢印をクリックしInspectorでConditionを追加、Runフラグを設定し、今度はfalseのときにダッシュ→待機に遷移するように条件を付与しました。

そして、Has Exit Timeのチェックマークは外します。

これで、Animatorでの遷移の設定が整いました。

今度は、スクリプト内でフラグの上げ下げをして、望んだタイミングで遷移できるように設定します。

f:id:tanukis:20200211110304p:plain

 

スクリプトは簡単のためにスペースキーが押されている時にダッシュアニメーションに遷移し、離された時に待機に遷移するようにしました。

Playerが持っているAnimatorコンポーネントを取得し、フラグを切り替えたいタイミングでSetBoolで切り替えています。スペースが押されている時はRunフラグがtrueになるので待機→ダッシュに切り替わります。離した時はRunフラグがfalseになるのでダッシュ→待機に切り替わります。


AnimationTransition

実際に動かしてみた映像がこちらです。期待した通りの動きになりました。

 

以上がドット絵アニメーションををゲームに反映させてみたでした!長くなりました汗。

絵を描かなくちゃいけなかったり設定がややこしかったりとめんどくさいアニメーションですが、実装するとゲームの見栄えはグッと上がるので興味がある方は挑戦してみてはどうでしょうか?

 

最後に、GooglePlayで自作アプリを公開しているので、興味がある方時間がある方はぜひ遊んでみてください(スマホ推奨)。

play.google.com

 

ではまた!

【Unity】【開発日記】ドット絵アニメーションををゲームに反映させてみた その1


どうも!たぬきすです。

この記事は前回の続きです。前回の記事もよかったら読んでみてください。

tanukis.hatenablog.com

前回はドット絵を描き、それをアニメーションにするまでを書きましたが今回はそこで作ったアニメーションを実際に実装してみた話です。

結論から言うと、いい感じにできました。アニメーションの実装はアニメーションの遷移条件をしっかり定めてやれば別段難しいことはない印象でした。アニメーションを実装した後のゲーム内容はこんなかんじ


2Dshooting

前回の記事の時に描いたダッシュと立ち絵をそのまま実装し、さらにジャンプアニメーションと敵のアニメーションも追加しました。ゲームっぽい見た目になってきましたね。この記事では実装の手順をざっくりと説明できたらと思っています。

それでは作って行きましょう!

作成開始

まずはアニメーションの実装に必要なタブを開きましょう。

UnityのツールバーからWindow→Animation→Animatorを開きます。また、UnityのツールバーからWindow→Animation→Animationを開きます。アニメーション実装の時はAnimatorとAnimationの二つのツールを使います。具体的には、Animationで歩く、走るといったアニメーションを作って、アニメーションの遷移をAnimatorで管理するような感じです。

タブが開けたら、まずはAnimationでキャラクターの動作をアニメーションにしていきましょう。

まずはAnimationタブを選択し、アニメーションを実装したいGameObject(この記事ではPlayer)をクリックすると、Animationタブが下のようになります。

f:id:tanukis:20200209145701p:plain

ここでCreateボタンをクリックすると下の画面が現れるので、ここでは自分が作りたいアニメーションの名前(ダッシュならRunAnimation的に)を入力して保存します。

f:id:tanukis:20200209150346p:plain

保存をすると画面が切り替わって下のようになり、UnityにAnimationとAnimatorのファイルが保存されているはずです。

f:id:tanukis:20200209152418p:plain

f:id:tanukis:20200209152437p:plain

これでアニメーションを作る準備が整いました。ここからアニメーションを作っていきましょう。

Add Propertyをクリックし、Sprite Renderer→Spriteを追加すると下のような画面になります。

f:id:tanukis:20200209153917p:plain

編集の基本的な機能を説明します。

アニメーションのコマの追加はUnityに保存している画像ファイルをキャラクターの絵が表示されている帯の上にドラッグ&ドロップするとできます。

コマ送り一つ一つのスピードは追加されたコマの絵を左右にドラッグすることで変更できます。

アニメーションのコマを削除したい場合は、削除したいコマの絵を右クリック→Delete Keyするとできます。

下の画像では屈伸するアニメーションを作りました。

f:id:tanukis:20200209155420p:plain

ここでAnimatorのタブを表示してみましょう。おそらく下のような画面になっていると思います。

f:id:tanukis:20200209160226p:plain

Animatorはアニメーションのコントローラーみたいなものなのですが、EntryからNew Animationへ矢印が伸びています。矢印は動作の遷移の方向を示しているのですが、デフォルトだとアニメーションが始まった瞬間にNew Animationを再生することになっています。なのでこの状態のままUnityを再生すると先ほど作ったアニメーションが再生されます。


AnimationSample

このように待機、ダッシュといったAnimationファイルを複数作成し、それらをAnimatorで遷移条件を設定することで、待機からダッシュダッシュからジャンプのような動作の遷移を細かく編集することができます。

この記事ではAnimationファイルで一動作を編集する方法を紹介しましたが、Animatorでアニメーションを遷移させる方法は次回にしようと思います。

 

ではまた!

 

追記

続きの記事を投稿しました。よかったら読んでみてください。

tanukis.hatenablog.com