さけのさかなのブログ

同人ゲーム開発やってます。Unity使ったりする。

徐々に消えるライト

やること

 爆発エフェクトなんかを作るとき、オブジェクトにライトを持たせる場合がある。  このとき、普通にエフェクトオブジェクトにライトコンポーネントを持たせただけだと、オブジェクトがDestroyされた瞬間にライトもパッと消えてしまう。余韻をもってじわりと消えてほしい。

 (光源が消えたらその瞬間に暗くなるのがリアルではあるんだけど、忘れよう。)

コード

Unity5.3.4f1

using UnityEngine;
using System.Collections;
using System.Linq;

namespace TSKT
{
    [RequireComponent(typeof(Light))]
    public class EasingLight : MonoBehaviour
    {
        static bool applicationQuitting = false;

        Light light;
        Light Light
        {
            get
            {
                return light ?? (light = GetComponent<Light>());
            }
        }

        [SerializeField]
        [Range(0.01f, 1f)]
        float turnOnDuration = 0.1f;

        [SerializeField]
        [Range(0.01f, 1f)]
        float turnOffDuration = 1f;

        IEnumerator TurnOnCoroutine()
        {
            var initialIntensity = Light.intensity;
            var v = new EasingValue();
            v.JumpTo(0f);
            v.EaseTo(initialIntensity, 0.1f);

            Light.intensity = 0f;

            while (true)
            {
                yield return null;
                Light.intensity = v.Value;
                if (v.Completed)
                {
                    break;
                }
            };
        }

        void OnApplicationQuit()
        {
            // OnDisableはエディタでの再生終了時にも処理が走って嫌な事になるので、終了を検知する。
            applicationQuitting = true;
        }

        void OnEnable()
        {
            StartCoroutine(TurnOnCoroutine());
        }

        void OnDisable()
        {
            if (applicationQuitting)
            {
                return;
            }

            var obj = new GameObject();
            obj.transform.position = transform.position;
            obj.transform.rotation = transform.rotation;
            obj.transform.localScale = transform.lossyScale;
            Destroy(obj, turnOffDuration);

            var cloneLight = obj.AddComponent<Light>();
            // プロパティのコピーが綺麗に書けなかったので、ライトのtypeによっては上手くいかないかもしれないし、将来的にプロパティが増えたりしたら目も当てられない。
            cloneLight.type = Light.type;
            cloneLight.bounceIntensity = Light.bounceIntensity;
            cloneLight.color = Light.color;
            cloneLight.flare = Light.flare;
            cloneLight.cullingMask = Light.cullingMask;
            cloneLight.cookie = Light.cookie;
            cloneLight.cookieSize = Light.cookieSize;
            cloneLight.range = Light.range;
            cloneLight.intensity = Light.intensity;
            cloneLight.shadows = Light.shadows;

            var v = new EasingValue();
            v.JumpTo(light.intensity);
            v.EaseTo(0f, turnOffDuration);
            TaskManager.Instance.Repeat(() =>
            {
                if (!cloneLight)
                {
                    return false;
                }
                cloneLight.intensity = v.Value;
                return !v.Completed;
            });
        }
    }
}

 EasingValueとかTaskManagerは自作クラスなのでなんじゃこりゃって感じだが、値を徐々に動かすためのしくみと思いねえ。コルーチンとMathf.Lerpあたりで書き換えられる。こだわり抜くならAnimationCurveも。

 やってることは強引なもので、オブジェクトがDestoryされる瞬間にライトの複製を作ってそっちをじわじわ消すというだけ。

 ついでにライトの点き始めもじわじわ光るようにしておいた。