最初に成果物を提出。こんな感じになりました。それっぽく見えるとうれしいですが、ROM を解析して厳密にやったわけではないです。
データの詳細は、記事の最後に今回のサンプルデータを公開しているので、そちらをご確認ください。
実装したことの流れ
- 画面のクリック。(エフェクト開始の基点のこと)
- ランダムにダメージ値を生成する。
- 一文字する Image として生成して、Canvas 上に並ぶようにする。
- 画像は0-9までの数字だけを扱う。
- 一文字ずつ飛び跳ねるアニメーションを実行する。(跳ねる回数は2回)
- アニメーションが終了したら、ゲームオブジェクトを Destroy する。
こんな感じがよいのではないかと思いました。
前回に書いた「FF5 のモンスターを倒した演出」に続いてになりますが、今回は「FF4、FF5のダメージ表示アニメーションをUnityで再現する」という記事が元ネタになっています。
すでに先駆者が居られたわけですが、(個人的にですが)作り込みに満足できなかったので、自分でもやってみた感じです。
いくらかブラッシュアップできていたら、うれしく思うところです。
数字を画像表示する方法について
フォントではなくて、数字の画像 (png) を使ってどうやって表示するのか、というところは以下の記事が参考になりました。
SpriteNo
というクラスを基本にしている仕組みで、Image
型と SpriteRenderer
型をサポートしています。賢い作り方だと思いました。
自分は、Image
クラスを基本に作ろうと思っていたので参考にさせていただきました。変更点のひとつに、スプライトの並びに自分自身の横幅が計算に含まれていないように思いました。なので、width
を入れてあげるとこうなりました。
private void UpdateLayouts() { var textLenght = _Text.Length; for (var i = 0; i < _Components.Count; i++) { var position = Vector3.zero; var width = _Components[i].GetComponent<RectTransform>()?.sizeDelta.x ?? 0; switch (_LayoutType) { case LayoutType.Center: position.x = (i - (textLenght - 1) / 2.0F) * width * _Span; break; case LayoutType.Left: position.x = i * width * _Span; break; case LayoutType.Right: position.x = -(textLenght - 1 - i) * width * _Span; break; } _Components[i].transform.localPosition = position; } }
上記記事のとおりすすめると、ここで画像による数字の表示に成功するわけです。一応使用した画像はつぎのですが、お手製なので正確には違っているんじゃないかと思います。
画像の分割
これもたぶん初めてだったので、メモしておきます。
インポートした画像の Assets の設定を開きます。
- Sprite Mode = Multiple
- Filter Mode = Point (no filter)
- Sprite Editor を開く
- Slice を選択
- Type = Grid By Cell Size
- Pxel Size X=8, Y=8
- Slice を選択
最終的な Assets はこんな感じで、うまくできました。
アニメーションの実装
FF5 はダメージ表示がコミカルです。上の桁の数字から順番に時間差で飛び跳ねます。(2回だけ)
これをどのように実装するとよいのか考えたのですが、(初の)アニメーションを使ってみようと思いました。
Assets
に Animation
と Animation Controller
を 追加します。
- Animation (BoundAnimation)
- Animation Controller (BoundAnimationController)
シーンの中の Canvas
に空の Game Object
を追加して、BoundAnimation
を Add Component してやります。
Animation Controller
のほうは、アニメーターの画面を開いて、Entry
から BoundAnimation
に繋ぐだけの単純なアニメーションのシナリオを設定します。
最後に BoundAnimation
をダブルクリックしてアニメーションの画面を開いて、(シーンの Game Object
が選択された状態で)Add Property GameObject: Anchored Position.Y
を次の表のように「Curves」から設定します。
このツールの使い方は「マニュアル - アニメーションカーブの使用」が参考になりました。
パスの引き方は「Adobe - パスとシェイプ」などを参考にしました。ポイントだったのは、アンカーポイントを右クリックして、「Break」にすることで方向線を別々に制御できます。(放物線を描くためには、おそらく必須だと思います)
最後にアニメーション終了の位置に CompleteAnimation のメソッドを登録しておきました。対応するクラスは、以下のようなものを用意しておきます。
public class SpriteAnimation : MonoBehaviour { public int Length { get; set; } public int No { get; set; } public event EventHandler Completed; public void Initialize(int no, int length) { Length = length; No = no; } public void CompleteAnimation() { Completed?.Invoke(this, EventArgs.Empty); } }
アニメーションの実装 (Script)
以下のコードを SpriteNo
(基底クラス)に追加しました。
[SerializeField] private RuntimeAnimatorController _AnimationController = null; [SerializeField] [Range(0.1F, 5.0F)] private float _AnimationSpeed = 1.0F; [SerializeField] [Range(0.0F, 1.0F)] private float _AnimationDelaySeconds = 0.14F; [SerializeField] [Range(0.0F, 5.0F)] private float _DestoryDelaySeconds = 0.8F; public void ClearText() { _Components.Where(component => component != null) .ToList() .ForEach(component => DestroyImmediate(component.gameObject)); _Components.Clear(); } private void UpdateComponents() { UpdateSprites(); UpdateLayouts(); if (_AnimationController != null) { AttachAnimation(); } } private void AttachAnimation() { for (var i = 0; i < _Components.Count; i++) { StartCoroutine(this.DelayAction(i * _AnimationDelaySeconds, (int no, int length) => { var animation = _Components[no].gameObject.AddComponent<SpriteAnimation>(); var animator = _Components[no].gameObject.AddComponent<Animator>(); animation.Initialize(no + 1, _Components.Count()); animator.runtimeAnimatorController = _AnimationController; animator.speed = _AnimationSpeed; animator.enabled = true; // 末尾のデータ if (no + 1 == length) { animation.Completed += (sender, args) => { StartCoroutine(this.DelayAction(_DestoryDelaySeconds, () => { ClearText(); Destroy(transform.gameObject); })); }; } }, i, _Components.Count)); } }
こうしておくと、一文字ずつ実行するエフェクトとロジックを切り分けしておくことができるので、好ましいと思いました。
プロパティをいじってみる
DelaySeconds を 0.0F
にしてやると、同時に数字が動くようになるので FF6 っぽいアニメーションになります。
ただ、これでいいなら、パック化できそうなど、軽量化する工夫の余地はありそうです。
FF5, 6 に対応、かつ、ちょっとした変更ならプロパティで対応できたし、アニメーションの雰囲気はアニメーションカーブで変更に対応できそうです。
カラーの変更に対応することで、回復(緑)やクリティカル(黄)なども表現しやすくなります。
ラグナロクオンラインみたいに文字が伸びたり薄くなって消えたりするには、アニメーションカーブのプロパティを追加してやるとよさそうですね。
ガチくんの FF5 コンテンツがおもしろかったので、いろいろ遊んでしまった。
サンプル
今回のサンプルは GitHub に公開しています。
参考
- ぺんごや - 数字を画像で描画する方法(gUIじゃないよ)
- 可変する数字を画像(Sprite)を使って、SpriteRendererやImageで表示【Unity】【uGUI】
- FF4、FF5のダメージ表示アニメーションをUnityで再現する
リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)
- 作者:Dustin Boswell,Trevor Foucher
- 発売日: 2012/06/23
- メディア: 単行本(ソフトカバー)
- 作者:秋山 高廣
- 発売日: 2019/07/19
- メディア: 単行本(ソフトカバー)