初心者がUnityでなんかしちゃうぞBlog

Unity初心者は、ゲーム開発の夢を見るか?

Mathf.InverseLerpとMathf.Lerp で作る逆HP比例ダメージ(アーロンさんの正宗を目指して)

ツイッターばかりでblog更新をサボっていたので自分が覚えたこと、覚えておきたいことを書くことでなんか良い感じの更新をしていきたいと思います。 今回紹介するのは、Mathf.InverseLerp みんなだいすき Mathf のメソッドです。

先にいうけど、Mathfはマジで神が詰まっているから必ずチェックしよう。自分がやりたいと思ったことはきっとはるか昔に他の人がやろうとしたことなんだ…!

何がしたかったのか?

f:id:yuu9048:20191103204717j:plain
FF10 アーロン

FF10のヒロインこと、アーロンさんの武器に正宗というものがあります。これはHP残量が低ければ低いほど大ダメージを与えられるようになるという逆HP比例ダメージの武器です。 これが作りたかったんです!!!

内容をかんたんに言い換えると「値が小さければ小さいほど、大きな値になる」ということが実現できれば作れそうなんですが 正直、これの作り方がいまいちわかっていませんでした。今まではしきい値を作ってそれによって段階的に切り替えるという手法をとっていました。 (HP1000以下ならダメージ1.5倍、HP500以下ならダメージ2倍 みたいな)

でもさ、この時代だぜ? もっとスムーズになめらかな補間がかかるべきだとは思いませんか? 僕は思います!! ということで色々と調べていてエンジニアの先輩との雑談中にこの話題を振ったら解決したのです。なのでそれを思い出しながらBlog用に書き直してみました

今回やること

アーロンさんの七曜武器「正宗」に則って

  • HPが満タンのときはダメージが0.5倍
  • HPが0に近いほどダメージが上がる(最大2.2倍)

というものを作っていきます。

テストコード

今回書いたのはこんなコードです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    [SerializeField] Text UI_LIFE_TEXT;
    [SerializeField] Text UI_DAMAGE_LOG;

    [SerializeField, Range(0, 9999)]
    int nowLife = 9999;         // アーロンさんの現在HP

    int maxLife = 9999;       // アーロンさんの最大HP
    float damageBase = 1000;    // 通常攻撃のダメージ量
    float min = 0.5f;           // 最低ダメージ倍率(HP満タンに近いとき)
    float max = 2.2f;           // 最高ダメージ倍率(HPが0に近いとき)

    private void Update() {
        UI_LIFE_TEXT.text = nowLife + "/" + maxLife;
    }

    public void Attack() {

        // 1を0、maxLifeを1としたときに現在のHPを0~1で出し、それを1から引く
        var _inverseProportion = 1 - Mathf.InverseLerp(1, maxLife, nowLife);

        // 計算した現在のHP割合を使い、Lerpで下限倍率~上限倍率の間での現在値を出す
        var _damagePercent = Mathf.Lerp(min, max, _inverseProportion);

        var _finalDamage = damageBase * _damagePercent;

        UI_DAMAGE_LOG.text = "アーロンさんの正宗での攻撃!" + Mathf.Round(_finalDamage) + "のダメージを与えた!\n"
            + "【ベースダメージ】"+ damageBase + " 【攻撃倍率】" + _damagePercent;
    }
}

f:id:yuu9048:20191103212644p:plain

解説

キモとなるのはAttack()内でやっているところです。 今回はHP残量に対してダメージ倍率が変動するので、まずはHP残量がどのぐらいなのかを計算して出す必要があります。ココで登場するのがMathf.InverseLerp です!

docs.unity3d.com

Mathf.InverseLerp(float a, float b, float t) aを0、bを1としたときのtの量を出すことができます。つまり今回の場合は、HP1~HP9999を範囲として自分のHPがどのぐらいなのかを0~1で求めることができることができます。 そして、今回は値が小さければ小さいほど効果が強くなるタイプなので求めた値を1から引きます。なんで正規化したかというとこの処理をかんたんに済ませるためだったんですね。

これで、現在のHPがどのぐらいの感じなのかを取得できました。あとはこれを使ってダメージ倍率の計算を行います。 今回の場合、満タンだとダメージが0.5倍、0に近ければ2.2倍になるので、ダメージ倍率の取りうる範囲は0.5~2.2となります。範囲が決まっている値から特定のものを取り出す…これはまさに Mathf.Lerp の出番じゃないですか!

docs.unity3d.com

ダメージ倍率を0.5~2.2の範囲でMath.Lerpで先程求めたHPの値を使って取り出します。値が1に近ければ2.2倍に近づいていく値が求まりますし、逆に0に近ければ0.5倍に近づいていく値が求まります。つまりこれがアーロンさんの正宗による逆HP比例ダメージのダメージ倍率となります。 ベースダメージとして定義しておいた1000にこの倍率をかければ、やっと欲しかった値が求まりました!

確認

さぁ、HPを動かして動作を確認してみます。

HPが満タンのとき(0.5倍) f:id:yuu9048:20191103214230p:plain

HPが約半分のとき、(1.35倍) f:id:yuu9048:20191103214416p:plain

HPが1のとき(2.2倍) f:id:yuu9048:20191103214253p:plain

動きました! これでアーロンさんはみんなのものです! 今回は正宗の逆HP比例ダメージで話を進めましたが、「値が小さいほど効果が高まる」という仕組みを作るのであればこの考え方が応用できます。たとえば

  • 距離が近いほど大ダメージ
  • 残り時間が少ないほど高得点
  • 味方が近いほど攻撃力アップ
  • 敵が近いほど加速する弾

など、値が~~なほど○○というのは色々使われるので使い勝手がよさそうですね!