ゴマちゃんフロンティア

アザラシが大好きなエンジニアの開発日記です

【Unity】円状の体力ゲージ作成と最大値の制御について

time 2017/05/30

というわけで、今回はUnityのUIを使った体力ゲージに関するお話です!
既に体力ゲージ自体は作成していましたが、しょぼい見た目で気に入らなかった上、妙にアクセス数が伸びてしまっているので、今回はそれを円状にしてみました!

【Unity】体力ゲージの実装方法の紹介 (一瞬で減る緑ゲージ+徐々に減る赤ゲージ)

単に「円状のゲージを作成」なら記事にするほどのものではありません。ImageコンポーネントのイメージタイプをFilledにし、FillMethodを「Radial 360」にすればOKです。
ただし、タイトルにある通り”最大値を変更可能な”ゲージの作成を考えた場合はちょっとややこしいことになるので、その際に苦戦したことをご紹介します。

作成するゲージのイメージ

「イメージ」と言いつつ、先に完成したUIを載せておきます。

自作ゲームとしてほしい仕様を列挙すると以下のような感じ。

  • ゲージ始点が左側、終点が下側の270度の範囲を使用
  • 体力の最大値は100~250の範囲で変化し、250時に体力ゲージが最大となるようにする
  • ゲージ外枠として黒色のフレームがある
  • ゲージが減少した部分の背景は透明 (後ろのゲーム画面が見える)

意識して抜き出した仕様ではありませんが、今思うと全項目で苦戦したのでちょうどよかったです (小声)

苦戦したところ

前提として、Canvas直下にPlayerGageという空オブジェクトに設定し、その下に各種ゲージのImageを配置しています。
所々自作ゲーム用の変数が混じっておりますが、適当に脳内変換していただければと思います。

「FillMethod」に270度(3/4)がない

そのせいでさっそく「ゲージ始点が左側、終点が下側の270度の範囲を使用」で詰まる羽目に。180と360はあるのですが…。
Imageクラスを継承して作っちゃおう!」とか思いましたが、実際にやってみるとかなり無謀なチャレンジだったため即挫折。
仕方がないので「Radial 360」を選択し、常にFillAmountを0.75倍することで強引に表現します。

float fillProp = 0.75f;

Image GreenGage = transform.FindChild("GreenGage").GetComponent<Image>();
GreenGage.fillAmount *= fillProp;

ひとまず問題なさそうなので、このままいきます!

最大値に応じたゲージの増減

上で疑似270度のFillを設定できましたが、そのままではゲージが「常に体力最大値であること」を期待した描画になってしまいます。初期値が100、最大値が250なのでこれではまずいです。
そこで、あらかじめ体力ゲージの上限を決めておき、その値を最大(FillAmountが1)と定義しておきます。あとは「現在の最大値/最大値上限」で割合をとってあげればOKです。結果を0.75倍して設定するのも忘れないようにしましょう。

float fillProp = 0.75f;
maxLifeLimit = 250;

Image GreenGage = transform.FindChild("GreenGage").GetComponent<Image>();
GreenGage.fillAmount = (player.maxLife / maxLifeLimit) * fillProp;

【体力最大値が100のとき】

【体力最大値が200のとき】

ちなみに最大値が上限なく上がり続けるゲームの場合、この方法は使えません。ただ、そのようなゲームの場合は常に最大表示とした割合で表示することが多いと思います。大抵のRPGは正にそれですね。

ゲージ枠(フレーム)の表示

この部分を作り終えた後で、Outlineコンポーネントでフレームが作成できることを知りました。どちらにしても制御が必要かと思いますが、ここで紹介するよりは簡単にできる気がします…。

次は体力ゲージ周りを囲むためのフレームを制御します。円状故にスライスしたりタイル状に並べたりできないので工夫が必要です。
手始めに「体力ゲージのフレームのみ」の画像を用意します。

スクリプトにフレームの長さを制御する処理を追加します。「外側のフレームの長さ=現在の体力最大値」なので、難しく考えずにFillAmountを変えてあげましょう。また、緑ゲージは現在の体力を表すように修正します。

float fillProp = 0.75f;
maxLifeLimit = 250;

Image Frame = transform.FindChild("Frame").GetComponent<Image>();
Image GreenGage = transform.FindChild("GreenGage").GetComponent<Image>();

Frame.fillAmount = (player.maxLife / maxLifeLimit) * fillProp;
GreenGage.fillAmount = (player.life / player.maxLife) * Frame.fillAmount;

これで「始端と終端以外は」フレームもできました!

フレームの始端と終端部分の制御

最後にフレームの「始端と終端」部分を作ります。
ゲージ背景がフレームと同じ色でよければ、フレームの描画順を後ろにした状態で塗りつぶすことで、今まで実装した処理で実現できます。が、ゲージの減少した部分の背景色を変えたり透明にしたい場合は塗りつぶすわけにはいきません。
ということで自前で制御する必要があります。パッと思いついたのは、フレームの始端と終端を別画像で用意し、フレーム本体のFilAmountに合わせて位置を変える方法です。ここまで来たら勢いで終わらせてしまいたいので、それ以上深くは考えませんでした。

上のような棒のみの画像を用意し、各ゲージ類と中央位置や大きさを合わせます。始点は固定で問題ないので、終点のみスクリプトから制御します。
フレーム部分の位置を変えるのは「Z軸に回転」が手っ取り早いです。どのくらい回すかはFramefiliAmountを参照しましょう。

float fillProp = 0.75f;
maxLifeLimit = 250;

Image Frame = transform.FindChild("Frame").GetComponent<Image>();
Image GreenGage = transform.FindChild("GreenGage").GetComponent<Image>();
Image GageTerminal = transform.FindChild("GageTerminal").GetComponent<Image>();

Frame.fillAmount = (player.maxLife / maxLifeLimit) * fillProp;
GreenGage.fillAmount = (player.life / player.maxLife) * Frame.fillAmount;

GageTerminal.rectTransform.rotation = Quaternion.Euler(new Vector3(0, 0, -360f * Frame.fillAmount));

書いてから思いましたが、GageTerminalgetComponent()は直接RectTransformを取っちゃった方がよさそうですね。後々画像をいじる場合はImageのままで良いかと思います。

これで「円状で」「最大値を0~270度で変更可能な」ゲージができました!

その他適当な飾りつけ

お好みでキャラアイコン入れるなり、簡単に文字を入れるなりします。

LIFEは体力ゲージの曲がり具合に沿って配置したかったので、GIMPで円状に配置したものを画像として取り込んでいます。
キャラクターアイコンは画像を配置するだけですが、レンダリング順序を一番手前にし、ゲージにちょっと覆いかぶさるくらいの大きさにしました。

あとがき

そんなわけで、円状の体力ゲージと最大値の増減について考えてみました!
私的な意見ですが、アクションゲームにおける体力ゲージが割合表示なのはあまり好きではありません。ゲーム攻略に合わせてキャラクターを強くするシステムがある場合は尚更です。
体力ゲージがビヨーンと長ければ「こんな強くなったんだなー」と一目で分かりますよね。プレイヤーにその印象を抱かせるには、やはり最大値を可変にするしかなかったので、今回あれこれやってみました!

down

コメントする