ぐーるらいふ

底辺。

【Unity】【LWRP】 Point Light 2Dでゆらゆらさせたかっただけなのに

久しぶりのUnityです

お久しぶりです。ぐーるです。
のーらいふです。

最近Unity 2019.3.0f3まで上げて使ってて見た目が随分変わっててびっくりしました。
アイコンとか慣れないです。

LWRPで2D Lightを使ってみたいと思い、触ってみました。
そしてTwitterに投稿しました。


そこで一点気になった事があったので記事にしてみます。

LWRPを設定する

LWRPを設定するとSpriteがLightの影響を受けるようになるらしい。
おおこれはやってみたい!ということで先人の知恵をお借りします。

kan-kikuchi.hatenablog.com

(いつもお世話になっています。)

自分でも軽く設定の仕方だけ書いておきます。

1. 新規プロジェクトを作成する

Unityは2019.3.0f3を使用。(2019.2.18f1でも確認はしました)

f:id:ghoul_life:20200117174751p:plain

2. PackageManagerでLWRPをインストールする

上部メニュー Window > Package Managerで PackageManagerを開く
Lightweight RPをインストールする
(ここではv7.1.7)

f:id:ghoul_life:20200117174951p:plain
f:id:ghoul_life:20200117175011p:plain

3. 必要なAssetを作成する

ProjectのCreateからPipeline Asset , 2D Rendererを作成する

ProjectのCreate > Rendering > Universal Render Pipeline > Pipeline Asset(Forward Renderer)
ProjectのCreate > Rendering > Universal Render Pipeline > 2D Renderer(Experimental)

f:id:ghoul_life:20200117175122p:plain

を作成します。
この時
「UniversalRenderPipelineAsset」
「UniversalRenderPipelineAsset_Renderer」
「New 2D Renderer Data」
の三つのファイルが作られます。

4. LWRP Setting

UniversalRenderPipelineのGeneral > RenderList

に2D Renderer(Experimental)で作成したNew RenderDataをセット。
(特にパラメータをいじる必要はありません。)

そして
上部メニューEdit > Project Settings で Project Settingsを開き

f:id:ghoul_life:20200117175547p:plain

GraphicsのScriptable Render Pipeline Settingsに
「UniversalRenderPipelineAsset」ファイルをセットすればOKです。

f:id:ghoul_life:20200117175710p:plain

5. 2D Lightに対応したSpriteを確認する

その後
適当にSpriteRendererを作成すると、
Materialが「Sprite-Lit-Default」になっており、真っ暗になります。

GameObject > Light > 2D > Point 2D Light(Experimental)
などで2D Lightを作成すると、Lightの影響を受ける2Dスプライトが確認出来ます。

f:id:ghoul_life:20200117175834p:plain
f:id:ghoul_life:20200117175847p:plain
(上がSceneです)

2D Lightをゆらゆらさせたい!

ここからが本題。
こういうの作ったら、揺らめく炎を作りたくなるもの。

Point Light 2D
Inspectorでパラメータいじってみると以下のような関係がわかる
・Outer Radius : 光の大きさ
・intensity : 光の強さ
・falloff Intensity : 境界線の強さ

f:id:ghoul_life:20200117180107p:plain

なるほど、falloff Intensityをいじるといい感じに揺らめくな、
じゃあこれをスクリプトで触ればいいや

f:id:ghoul_life:20200117180126p:plain
(getterしかない。なんで?)

で、触ってみるとこれ、アクセサーがgetしかない。
なんでええええ?何か別の値から計算して求めているのかな?

詳しいことまではわかりませんでしたが、とにかく揺らめかしたいので、リフレクションで触ることにしました。

美味しいとこだけ抜き出し

    // Light2Dに値をセットするが、リフレクションでprivateフィールドにアクセスする…(なんでset無いんだ?)
    private void SetFalloffIntensity(Light2D light2D , float value)
    {
        System.Type light2DType = light2D.GetType();

        // "m_FalloffIntensity"は sourceコードから拾ってきた
        // https://github.com/Unity-Technologies/ScriptableRenderPipeline/blob/master/com.unity.render-pipelines.universal/Runtime/2D/Light2D.cs#L146

        // privateだからBindingFlags
        System.Reflection.FieldInfo falloutIntensity = light2DType.GetField("m_FalloffIntensity", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

        falloutIntensity.SetValue(light2D, value);
    }


ソースコード全体としてはこんな感じ。
UnityEngine.Experimental.Rendering.LWRPではなく、UnityEngine.Experimental.Rendering.Universalになります。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;

public class LightModule : MonoBehaviour
{
    // Light2D Object
    [SerializeField] private Light2D _light2D;

    [Header("暗くなる度合い")]
    [SerializeField] private float _maxIntensity = 0.8f;

    [Header("明るくなる度合い")]
    [SerializeField] private float _minIntensity = 0.3f;

    [Header("明暗変化速度")]
    [SerializeField] private float _addIntensity = 0.001f;

    private float _intensity = 0;
    private bool _isForward = true;


    // Start is called before the first frame update
    void Start()
    {
        _intensity = _minIntensity;
        _isForward = true;
    }

    // Update is called once per frame
    void Update()
    {
        if (_isForward)
        {
            _intensity += _addIntensity;
            if(_intensity > _maxIntensity)
            {
                _intensity = _maxIntensity;
                _isForward = false;
            }
        }
        else
        {
            _intensity -= _addIntensity;
            if (_intensity < _minIntensity)
            {
                _intensity = _minIntensity;
                _isForward = true;
            }
        }

        SetFalloffIntensity(_light2D , _intensity);
    }

    // Light2Dに値をセットするが、リフレクションでprivateフィールドにアクセスする…(なんでset無いんだ?)
    private void SetFalloffIntensity(Light2D light2D , float value)
    {
        System.Type light2DType = light2D.GetType();

        // "m_FalloffIntensity"は sourceコードから拾ってきた
        // https://github.com/Unity-Technologies/ScriptableRenderPipeline/blob/master/com.unity.render-pipelines.universal/Runtime/2D/Light2D.cs#L146

        // privateだからBindingFlags
        System.Reflection.FieldInfo falloutIntensity = light2DType.GetField("m_FalloffIntensity", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

        falloutIntensity.SetValue(light2D, value);
    }
}


これで揺らめく炎を2D Lightでも実現できました。

久しぶりにUnityやったなぁ~、やっぱ2D RPG風なのいいですね。