【Unity】オブジェクトを点滅させる方法

オブジェクトを点滅させる簡単な方法はないの?

やり方は色々あるけど、スクリプトアニメーションなどで出来るわ。

オブジェクトを一定周期で点滅させる方法の紹介です。

例えば、次のようにプレイヤーがダメージを受けて無敵状態になった時に、点滅させるような演出を想定しています。

オブジェクトを点滅させる手段は様々であり、例えば次のような方法が存在します。

点滅させる方法
  • 表示・非表示を交互に行う
  • メッシュや画像のアルファ値(透明度)を0と1交互に変化させる
  • 上記アルファ値をサインカーブで変化させる
  • 上記アルファ値をアニメーションで周期的に変化させる

どの方法が適切かは、場面によって異なってくるでしょう。

本記事では、このようなオブジェクトを点滅させる方法をいくつか紹介します。なお、アセットは使わずすべてUnity標準機能だけで実現するものとします。

この作品はユニティちゃんライセンス条項の元に提供されています

動作環境
  • Unity 2021.1.5f1

スポンサーリンク

前提条件

点滅させたいオブジェクトが予めシーンに配置されているものとします。

本記事では、2DのユニティちゃんとライフのUIが配置されているシーンを例として解説を進めます。

また、後述するアルファ値を変更して点滅させる方法では、シェーダーが半透明描画に対応している必要があります。

なお、上記のライフゲージの画像には以下アセットを使用させていただきました。

表示・非表示の繰り返しで点滅させる

Mesh RendererやImageなどの有効・無効を交互に繰り返すことで点滅が実現できます。

レンダラーを点滅させる

以下、Mesh RendererやSprite Rendererなどのレンダラーの有効・無効を切り替えて点滅させる例です。

RendererOnOffExample.cs
using UnityEngine;

public class RendererOnOffExample : MonoBehaviour
{
    // 点滅させる対象
    [SerializeField] private Renderer _target;
    // 点滅周期[s]
    [SerializeField] private float _cycle = 1;

    private float _time;

    private void Update()
    {
        // 内部時刻を経過させる
        _time += Time.deltaTime;
        
        // 周期cycleで繰り返す値の取得
        // 0~cycleの範囲の値が得られる
        var repeatValue = Mathf.Repeat(_time, _cycle);
        
        // 内部時刻timeにおける明滅状態を反映
        _target.enabled = repeatValue >= _cycle * 0.5f;
    }
}

上記スクリプトをRendererOnOffExample.csという名前で保存し、適当なゲームオブジェクトにアタッチし、インスペクタから各種設定を行うと機能するようになります。

Targetには点滅させたい対象のレンダラー(Mesh Rendererなど)、Cycleには点滅周期を指定してください。

実行結果

点滅対象に指定されたMesh Rendererなどが点滅するようになります。

スクリプトの説明

レンダラーの点滅処理は、内部時刻に基づいて行うようにしています。

// 内部時刻を経過させる
_time += Time.deltaTime;

そして、以下処理で周期cycleで繰り返す値を取得しています。

// 周期cycleで繰り返す値の取得
// 0~cycleの範囲の値が得られる
var repeatValue = Mathf.Repeat(_time, _cycle);

Mathf.Repeatメソッドは指定周期で繰り返した値を取得するメソッドです。コード中のコメントの通り、指定周期で折り返すように値を繰り返します。

最後に、周期的に繰り返す値に対して閾値を設け、閾値以上なら表示そうでなければ非表示にするようにしています。

// 内部時刻timeにおける明滅状態を反映
_target.enabled = repeatValue >= _cycle * 0.5f;

repeatValueは0~_cycleの間で繰り返すため、その中間値_cycle * 0.5fを閾値としています。

UIなどを点滅させる

Unity UIのImageやTextなどを点滅させたい場合は、Behaviour継承クラスなどを点滅対象として処理する必要があります。これらのUI要素はRendererクラスを継承していないためです。

サンプルスクリプトは以下のようになります。

BehaviourOnOffExample.cs
using UnityEngine;

public class BehaviourOnOffExample : MonoBehaviour
{
    // 点滅させる対象(ここがBehaviourに変更されている)
    [SerializeField] private Behaviour _target;
    // 点滅周期[s]
    [SerializeField] private float _cycle = 1;

    private float _time;

    private void Update()
    {
        // 内部時刻を経過させる
        _time += Time.deltaTime;
        
        // 周期cycleで繰り返す値の取得
        // 0~cycleの範囲の値が得られる
        var repeatValue = Mathf.Repeat(_time, _cycle);
        
        // 内部時刻timeにおける明滅状態を反映
        _target.enabled = repeatValue >= _cycle * 0.5f;
    }
}

このスクリプトをBehaviourOnOffExample.csという名前で保存し、適当なゲームオブジェクトにアタッチし、インスペクタから各種設定を行ってください。設定内容は1つ目の例と一緒です。

実行結果

UIのImageなどを点滅させられるようになりました。

明滅の割合(デューティ比)を変える

先述の例では、表示と非表示の時間は同じでした。これは、繰り返す値の中央を閾値としていたためです。この閾値を変えると表示・非表示の時間割合(デューティ比)を変更できます。

以下、表示時間の割合を0~1で任意に指定できるようにした例です。例ではRendererに対して明滅させる処理を行っています。UIなどに対応させたい場合はRenderer部分をBehaviourなどに書き換えてください。

RendererOnOffDutyExample.cs
using UnityEngine;

public class RendererOnOffDutyExample : MonoBehaviour
{
    // 点滅させる対象
    [SerializeField] private Renderer _target;

    // 点滅周期[s]
    [SerializeField] private float _cycle = 1;

    // 明滅のデューティ比(1で完全にON、0で完全にOFF)
    [SerializeField, Range(0, 1)] private float _dutyRate = 0.5f;

    private float _time;

    private void Update()
    {
        // 内部時刻を経過させる
        _time += Time.deltaTime;

        // 周期cycleで繰り返す値の取得
        // 0~cycleの範囲の値が得られる
        var repeatValue = Mathf.Repeat(_time, _cycle);

        // 内部時刻timeにおける明滅状態を反映
        // デューティ比でON/OFFの割合を変更している
        _target.enabled = repeatValue >= _cycle * (1 - _dutyRate);
    }
}

使い方は、先述のサンプルスクリプトと一緒です。Duty Rateから明滅の割合を変更できます。

実行結果

次のようにデューティ比を変更できるようになりました。

スクリプトの説明

以下部分でデューティ比を指定した点滅処理を実現しています。

// 内部時刻timeにおける明滅状態を反映
// デューティ比でON/OFFの割合を変更している
_target.enabled = repeatValue >= _cycle * (1 - _dutyRate);

repeatValueの値は0~_cycleまでの間で繰り返すため、閾値をそれに合わせて変化させるような計算を行っています。

透明度を変化させて点滅させる

ここまで紹介した方法では、レンダラーやUIなどの有効・無効を指定することで点滅を表現していました。

次はこれらの表示物の透明度(色のアルファ値)を変化させて点滅させる方法を紹介します。

マテリアル色のアルファ値を変化させる

マテリアル側の色を直接変更させる方法です。頂点情報などを編集する必要が無いですが、複数オブジェクトに適用すると、その分だけマテリアルが複製され、SetPass Callが増える点にご注意ください。

以下は、レンダラーのマテリアル色のアルファ値を変化させて点滅させる例です。

RendererMaterialAlphaExample.cs
using UnityEngine;

public class RendererMaterialAlphaExample : MonoBehaviour
{
    // 点滅させる対象
    [SerializeField] private Renderer _target;
    // 点滅周期[s]
    [SerializeField] private float _cycle = 1;

    private Material _material;
    private float _time;

    private void Awake()
    {
        // レンダラーのマテリアルを保持しておく
        _material = _target.material;
    }

    private void Update()
    {
        // 内部時刻を経過させる
        _time += Time.deltaTime;
        
        // 周期cycleで繰り返す値の取得
        // 0~cycleの範囲の値が得られる
        var repeatValue = Mathf.Repeat(_time, _cycle);
        
        // 内部時刻timeにおける明滅状態を反映
        // マテリアル色のアルファ値を変更している
        var color = _material.color;
        color.a = repeatValue >= _cycle * 0.5f ? 1 : 0;
        _material.color = color;
    }

    private void OnDestroy()
    {
        // 不要になったマテリアルを破棄
        Destroy(_material);
    }
}

使用方法や結果は先述の例と一緒なので割愛します。

スクリプトの説明

最初にマテリアルへの参照を保持しておきます。

private void Awake()
{
    // レンダラーのマテリアルを保持しておく
    _material = _target.material;
}

Renderer.materialプロパティを初回に参照した時に、そのレンダラー専用のマテリアルがインスタンス化されます。

注意

インスタンス化したマテリアルは、呼び出し元の責任でアンロードする必要がある点にご注意ください。例では、ゲームオブジェクトが破棄されるタイミングでDestroyしていますが、Resources.UnloadUnusedAssetsメソッドで破棄する方法もあります。

マテリアル色のアルファ値は、以下コードで指定しています。

// 内部時刻timeにおける明滅状態を反映
// マテリアル色のアルファ値を変更している
var color = _material.color;
color.a = repeatValue >= _cycle * 0.5f ? 1 : 0;
_material.color = color;

最初にインスタンス化したマテリアルを、オブジェクト破棄時に破棄するようにしています。

private void OnDestroy()
{
    // 不要になったマテリアルを破棄
    Destroy(_material);
}

スプライト色のアルファ値を変化させる

Sprite Rendererを用いた2Dゲームであれば、Sprite RendererのColorを直接変更すれば良いです。こちらの方法は、マテリアルを変更しないためSetPass Callがオブジェクトに比例して増えることはありません。

以下、Sprite Rendererの色のアルファ値を変化させて点滅させる例です。

SpriteRendererAlphaExample.cs
using UnityEngine;

public class SpriteRendererAlphaExample : MonoBehaviour
{
    // 点滅させる対象
    [SerializeField] private SpriteRenderer _target;
    // 点滅周期[s]
    [SerializeField] private float _cycle = 1;

    private float _time;

    private void Update()
    {
        // 内部時刻を経過させる
        _time += Time.deltaTime;
        
        // 周期cycleで繰り返す値の取得
        // 0~cycleの範囲の値が得られる
        var repeatValue = Mathf.Repeat(_time, _cycle);
        
        // 内部時刻timeにおける明滅状態を反映
        // スプライト色のアルファ値を変更している
        var color = _target.color;
        color.a = repeatValue >= _cycle * 0.5f ? 1 : 0;
        _target.color = color;
    }
}

こちらも使用方法や実行結果は一緒のため割愛いたします。

スクリプトの説明

アルファ値を変化させて点滅させるコードは以下のようになります。

// 内部時刻timeにおける明滅状態を反映
// スプライト色のアルファ値を変更している
var color = _target.color;
color.a = repeatValue >= _cycle * 0.5f ? 1 : 0;
_target.color = color;

SpriteRenderer.colorプロパティのアルファ値を変更するだけで実現できるので手軽です。

三角関数で点滅させる

透明度を変化させて点滅させる方法の応用として、三角関数で点滅させる方法を紹介します。

以下、Sprite Rendererを三角関数で点滅させる例です。

SpriteRendererSinExample.cs
using UnityEngine;

public class SpriteRendererSinExample : MonoBehaviour
{
    // 点滅させる対象
    [SerializeField] private SpriteRenderer _target;

    // 点滅周期[s]
    [SerializeField] private float _cycle = 1;

    private float _time;

    private void Update()
    {
        // 内部時刻を経過させる
        _time += Time.deltaTime;
        
        // 周期cycleで繰り返す波のアルファ値計算
        var alpha = Mathf.Cos(2 * Mathf.PI * _time / _cycle) * 0.5f + 0.5f;

        // 内部時刻timeにおけるアルファ値を反映
        var color = _target.color;
        color.a = alpha;
        _target.color = color;
    }
}

実行結果

サインカーブに従って緩やかに透明度が変化して点滅するようになりました。

スクリプトの説明

以下部分で三角関数によってアルファ値を計算しています。

// 周期cycleで繰り返す波のアルファ値計算
var alpha = Mathf.Cos(2 * Mathf.PI * _time / _cycle) * 0.5f + 0.5f;

Mathf.Cosメソッドは三角関数のコサインを表すメソッドで、1→0→-1→0→1→…という風に繰り返す値が得られます。2 \piで一周するので、周期が_cycleになるように係数を掛けて調整しています。

また、得られる値の範囲が-1~1なので、これを0~1になるように補正する計算を行っています。

アニメーションカーブで点滅させる

先述の例の応用として、アニメーションカーブを使って周期的に透明度を変化させる例を紹介します。

以下、Sprite Rendererをアニメーションカーブに従って点滅させるサンプルスクリプトです。

SpriteRendererAnimationCurveExample.cs
using UnityEngine;

public class SpriteRendererAnimationCurveExample : MonoBehaviour
{
    // 点滅させる対象
    [SerializeField] private SpriteRenderer _target;

    // 時系列のアルファ値の変化カーブ
    [SerializeField] private AnimationCurve _alphaCurve = AnimationCurve.Linear(0, 0, 1, 1);

    private float _cycle;
    private float _time;

    private void Start()
    {
        var length = _alphaCurve.length;
        if (length < 1)
            return;

        _cycle = _alphaCurve.keys[length - 1].time;
    }

    private void Update()
    {
        // 内部時刻を経過させる
        _time += Time.deltaTime;
        
        // 内部時刻をcycleで折り返す
        if (_time > _cycle)
            _time = Mathf.Repeat(_time, _cycle);

        // アニメーションカーブに従ったアルファ値計算
        var alpha = _alphaCurve.Evaluate(_time);

        // 内部時刻timeにおけるアルファ値を反映
        var color = _target.color;
        color.a = alpha;
        _target.color = color;
    }
}

以下のようにインスペクタから時間におけるアニメーションカーブを設定できるようになります。

実行結果

アニメーションカーブに従ってアルファ値が繰り返し変化するようになります。

Animator Controllerで点滅させる

ここまで紹介した方法はすべてスクリプトで点滅させていましたが、Unity標準のアニメーションでも同様のことが実現できます。

設定例を2つほど紹介します。

有効・無効を交互に繰り返す場合

Unity UIのImageに対して点滅させることを例にとって手順を説明します。

まず、UnityエディタのProjectウィンドウでAnimation Clipを作りたいフォルダに移動し、右クリックよりCreate > Animationの順に選択してAnimation Clipを作成します。

Animator Controllerを適用するために、作成したAnimation Clipを点滅させたいオブジェクトにドラッグ&ドロップします。

以下のように点滅対象オブジェクトにAnimatorコンポーネントが追加されていればOKです。

Animation Clipを直接ヒエラルキー上のオブジェクトにドラッグ&ドロップすることで、次のようなAnimator Controllerが自動作成されます。

ここからは点滅用のアニメーションを定義していきます。

作成したAnimation ClipをダブルクリックしてAnimationウィンドウを開き、点滅させたいゲームオブジェクトを選択しておきます。

以下動画の操作例を参考に、Imageの表示・非表示を切り替えるようなアニメーションクリップを作成します。

最後に、忘れずにAnimation ClipのLoop Timeプロパティにチェックを入れておきます。これはアニメーションを繰り返し再生させるためです。

実行結果

Animator ControllerによってImageが点滅するようになりました。

透明度を変化させる場合

アルファ値の変化によってオブジェクトを点滅させたい場合は、Animation ClipにColorのキーを定義すれば良いです。

Animation Clip以外の設定は先の例と一緒です。

実行結果

Animation Clipに従って透明度が変化するようになりました。

既にAnimatorが適用されているオブジェクトを点滅させたい場合

ゲームオブジェクトには、Animatorは最大1つまでしかアタッチできません。

そのため、Animatorによって制御されているプレイヤーなどに追加で点滅アニメーションを適用したい場合は、Animator Controllerに点滅用のレイヤーを設けるなどして対応する必要があります。

好きなタイミングで点滅の開始・終了を切り替えられるようにする

プレイヤーがダメージを受けて一定時間だけ点滅させたい場合などは、点滅させるスクリプトを少し工夫することで実現できます。

例えば、点滅開始メソッド点滅終了メソッドの2つを用意して、外部から呼び出せるような形にしておくと良いでしょう。

以下、Sprite Rendererの点滅を自由に開始・終了できるようにした例です。

SpriteRendererBlinker.cs
using UnityEngine;

public class SpriteRendererBlinker : MonoBehaviour
{
    // 点滅させる対象
    [SerializeField] private SpriteRenderer _target;

    // 点滅周期[s]
    [SerializeField] private float _cycle = 1;

    private bool _isBlinking;
    private float _defaultAlpha;
    private float _time;

    /// <summary>
    /// 点滅を開始する
    /// </summary>
    public void BeginBlink()
    {
        // 点滅中は何もしない
        if (_isBlinking) return;

        _isBlinking = true;
        
        // 時間を開始時点に戻す
        _time = 0;
    }

    /// <summary>
    /// 点滅を終了する
    /// </summary>
    public void EndBlink()
    {
        _isBlinking = false;
        
        // 初期状態のアルファ値に戻す
        SetAlpha(_defaultAlpha);
    }

    private void Start()
    {
        _defaultAlpha = _target.color.a;
    }

    private void Update()
    {
        // 点滅していないときは何もしない
        if (!_isBlinking) return;
        
        // 内部時刻を経過させる
        _time += Time.deltaTime;

        // 周期cycleで繰り返す値の取得
        // 0~cycleの範囲の値が得られる
        var repeatValue = Mathf.Repeat(_time, _cycle);

        // 内部時刻timeにおける明滅状態を反映
        // Imageのアルファ値を変更している
        SetAlpha(repeatValue >= _cycle * 0.5f ? 1 : 0);
    }

    // Imageのアルファ値を変更するメソッド
    private void SetAlpha(float alpha)
    {
        var color = _target.color;
        color.a = alpha;
        _target.color = color;
    }

}

Unity UIのImageなどに対応させたい場合は、SpriteRendererの部分をImage等に置き換えれば良いです。

点滅の開始・終了は、呼び元から以下のようにしてメソッドを呼び出す形で行います。

var blinker = GetComponent<SpriteRendererBlinker>();

・・・(中略)・・・

// 点滅開始
blinker.BeginBlink();

・・・(中略)・・・

// 点滅終了
blinker.EndBlink();

実行結果

以下、プレイヤーが攻撃を受けたら点滅させるよう実装した例です。

どのように呼び出し元から呼び出せばよいかは、開発するコンテンツに応じて適宜設計してください。

さいごに

オブジェクトを点滅させる方法についていくつか実装例を紹介しました。

スクリプトで実装する方法から、アニメーションでノーコーディングで実装する方法など様々です。

どの方法が良いかは、状況に応じて適切に判断してください。

参考サイト

スポンサーリンク