LoginSignup
6
15

More than 5 years have passed since last update.

Unityで2D格闘ゲームを作るときに欲しいと思われる、アニメのフレームと当たり判定の合わせ方

Last updated at Posted at 2017-06-05

Qiitaの画像アップロード容量の上限になったんで 古い記事をまとめ直した。

201701190426gif22.gif
半透明の赤い四角が 1フレームのずれもなく 手や足の先にかぶさっていることに着目してほしい。

Unityで2D格闘ゲームを作るときに、手の先だけ、足の先だけに当たり判定をつけるには どれが一番いい方法かは知らないが、次のような方法でも実現できる。

キャラクターのスプライト とは別に、 ヒットボックス(=上図で手や足に被っている半透明の赤い四角)のスプライト を用意する。

201701182222a29.png

LateUpdate() もあるがカメラで使ってるし、とりあえずスプライトの移動は Update() に入れた。

じゃあ、スプライトのアニメに合わせて ヒットボックスの位置を

// ヒットボックスのスプライト

void Update()
{
    // ~略~
    hitboxTransform.Translate(
        offsetX - hitboxTransform.position.x,
        offsetY - hitboxTransform.position.y, 
                  hitboxTransform.position.z
    );
}

といった感じで スプライト・アニメに合わせればいい。

あと2つ、

  • 今表示されているモーション
  • 今表示されているフレーム

の2つを取得できれば あとはOKだろう。

今表示されているフレームの計算式を出す

まず、アニメーションを作成しよう。

201701160449a2.png

弱パンチは この2フレームを使うとしよう。

201701160449a4.png

3Dゲームではあまり考えられないタイムラインの使い方だが、

弱パンチを 2フレーム で繰り出されるようにしたいなら 単純に サンプル数も 2にして スプライトを 2枚並べよう。

3フレームにしたければ [Samples] を 3 にすればいい。
タイムラインは 画像を全部埋めなくても、モーションの先頭フレームだけ 置いておけばいい。

201701160058gif8.gif

設定したパンチ・モーションは、その場で再生して確認できる。

今表示されているフレームの計算式

何フレーム目が表示されているかは、次の計算式で出せる。

// キャラクターのスプライト

using UnityEngine;
// 中略

    void Update()
    {
        // 中略

        // animator は GetComponent<Animator>(); で取得しておく

        AnimationClip clip = animator.GetCurrentAnimatorClipInfo(0)[0].clip;
        AnimatorStateInfo animeStateInfo = animator.GetCurrentAnimatorStateInfo(0);
        float normalizedTime = animeStateInfo.normalizedTime;

        // これが、今表示されているフレームの計算式
        int currentFrame = Mathf.FloorToInt((normalizedTime % 1.0f) * clip.frameRate);
    }

ノーマライズド・タイムというのが 今表示しているフレームなんだが、単位はノーマライズド、つまりアニメーションのリピート1回を 0~1 に直したものだ。
リピート2週目の半分なら 2.5、3週目の3分の2なら 3.6666... とかになる。ピッタリではなく思っているより端数は出る。

% 1.0f は1で割った余りなので、0~0.999…に変換できる。数学的には 0.999... も 1 も、同じと見ていい。

frameRate は 1秒間に何フレーム表示されるかなんだが、[Samples]と同じと見ていい。2 とか 3。

ノーマライズドタイムが 3.8 とかなら リピート3回目で、2フレーム目、ぐらいのことが分かる。
これで 今何フレーム目が表示されているかは出せた。

だいたいでいいのなら、スピード調整もできる

201701160449a8.png

Animator でステートをクリックすると、Inspectorビューで [Speed] を設定できる。
このスピードという数字は何なのか 結局分からないんだが、だいたい次のように使うことにした。

入れるSpeedを概算する式: Speed = 60 / Samples / 表示したいフレーム数

別に これでうまくいくわけでもないんだが、他にいい方法も見つからないので これで良しとすることにした。ドープシートのタイムラインには画像を隙間なくきっちり詰めている(つまりSamplesと画像の枚数が同じ)ものとして説明する。

(例1)弱パンチが画像2枚として、5フレームで表示したい場合。

60 / 2 / 5 = 6 (スピード 6)

(例2)中パンチが画像3枚として、7フレームで表示したい場合。

60 / 3 / 7 = 約 2.8571 (スピード 約2.8571)

(例3)強パンチが画像5枚として、9フレームで表示したい場合。

60 / 5 / 9 = 約 1.3333 (スピード 約1.3333)

(例4)ギブアップは画像4枚として、120フレームで表示したい場合。

60 / 4 / 120 = 約 0.125 (スピード 約0.125)

201701160449a10.png

ステートを抜けるときは、Exit Time を 1、Duration を 0 にしておくと モーションをピタッと抜ける。

今表示されているモーションの名前の取得方法

201701182222a34.png

例えば上図は Animatorビューでキャラクターのモーション遷移図を開いたところだが、黄色い丸で囲んだステートは Base Layer.SAtkHK だ。スタンド・ハード・キック、つまり立ち強キック。

ただし、Unity は「Base Layer.SAtkHK」という文字列を持っていない。文字列を ハッシュ という不可逆な数字に変換して持っている。

    AnimatorStateInfo animeStateInfo = animator.GetCurrentAnimatorStateInfo(0);

    // これがハッシュ
    int hash = animeStateInfo.fullPathHash;

実行速度や メモリ容量の省エネのために ハッシュ はよく使われる手法だ。

そこでプログラムでは事前に

using System.Collections.Generic;
using UnityEngine;
// ~中略

    Dictionary<int, string> hashToName = new Dictionary<int, string>();

    // 自分でハッシュを作る方法
    int hash = Animator.StringToHash( "Base Layer.SAtkHK");
    hashToName.Add( hash, "Base Layer.SAtkHK");

をするなりして、検索テーブルを作っておくこと。

これで アニメーターのフル・パス・ハッシュを調べれば、
現在のステートが分かるので、そこに紐づくモーションと、
そのモーションの何フレーム目は どの位置にヒットボックスがあるか、といった情報を追いかければ、

ヒットボックスの位置を アニメに合わせることができる。
201701190426gif22.gif

Unity でアクションゲームを作るときに使えるだろう。

6
15
0

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
15