さけのさかなのブログ

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

Unity Matrix4x4で連立方程式を解く

        // 四点を通る三次関数を得る
        static public void CalculateCubicFunctionBy4Points(
            float x1, float y1,
            float x2, float y2,
            float x3, float y3,
            float x4, float y4)
        {
            var matrix = new Matrix4x4();
            matrix[0, 0] = x1 * x1 * x1;
            matrix[0, 1] = x1 * x1;
            matrix[0, 2] = x1;
            matrix[0, 3] = 1f;
            matrix[1, 0] = x2 * x2 * x2;
            matrix[1, 1] = x2 * x2;
            matrix[1, 2] = x2;
            matrix[1, 3] = 1f;
            matrix[2, 0] = x3 * x3 * x3;
            matrix[2, 1] = x3 * x3;
            matrix[2, 2] = x3;
            matrix[2, 3] = 1f;
            matrix[3, 0] = x4 * x4 * x4;
            matrix[3, 1] = x4 * x4;
            matrix[3, 2] = x4;
            matrix[3, 3] = 1f;

            var inversedMatrix = matrix.inverse;

            var rightMatrix = new Vector4(y1, y2, y3, y4);

            var a = Vector4.Dot(inversedMatrix.GetRow(0), rightMatrix);
            var b = Vector4.Dot(inversedMatrix.GetRow(1), rightMatrix);
            var c = Vector4.Dot(inversedMatrix.GetRow(2), rightMatrix);
            var d = Vector4.Dot(inversedMatrix.GetRow(3), rightMatrix);

            // a * x^3 + b * x^2 + c * x + d
        }

徐々に消えるライト

やること

 爆発エフェクトなんかを作るとき、オブジェクトにライトを持たせる場合がある。  このとき、普通にエフェクトオブジェクトにライトコンポーネントを持たせただけだと、オブジェクトが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される瞬間にライトの複製を作ってそっちをじわじわ消すというだけ。

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

【Unity】MultiModeなSpriteの参照を取得し直す

問題

 MultiModeなSpriteの参照をGameObjectだかScriptableObjectが持っているとき。
 画像の変更か何かがあってSliceし直すと、Spriteへの参照がMissingになる。これをなんとかする。

理屈

 Spriteが生成し直されるのはどうしようもない(多分)ので、Textureと位置からSpriteを再取得する。

雑コード

 だいたいこんなん。

Unity5.3.2p3

public class Hoge : MonoBehaviour
{
#if UNITY_EDITOR
    [SerializeField]
    Texture texture;

    [SerializeField]
    Rect spriteRect;
#endif

    [SerializeField]
    Sprite sprite;

#if UNITY_EDITOR
    void OnValidate()
    {
        if (!texture)
        {
            return;
        }
        if (sprite)
        {
            return;
        }

        var path = AssetDatabase.GetAssetPath(texture);
        var sprites = AssetDatabase.LoadAllAssetRepresentationsAtPath(path);

        foreach (Sprite sprite in sprites)
        {
            if (spriteRect == sprite.rect)
            {
                this.sprite = sprite;
                EditorUtility.SetDirty(this);
                break;
            }
        }
    }
#endif
}

用途

 マップチップなんかに使えるはず。

  • マップチップ画像はMutliSpriteで作成する。
  • マップチップ画像はあとでチップが追加されたりするので、開発中にSliceをやり直す機会が発生する。

進捗っぽいの

【Unity】範囲指定可能なブラーエフェクト

 そもそも、2Dベースなら真面目に被写界深度エフェクトをやることもないよね!

 というわけで、範囲指定ができるブラーエフェクトを作った。

理屈

 ImageEffectのDepthOfFieldエフェクトは深度バッファを参照してブラー範囲を決めることでピンボケを表現している。

 この深度バッファ参照部分を改造し、テクスチャアセット参照にすることによって、範囲限定ブラーになると。

メモ

 テクスチャはモノクロで良いので8bit形式推奨。白い箇所にブラーがかかり、黒い箇所はそのまま描画される。

 親クラスとシェーダはImageEffectのものを参照しているので、そのへんインポートしておかなければならない。

コード

Unityバージョンは5.3.2p1

gist.github.com

ブツ

【Unity】Orthographicカメラで被写界深度エフェクトを使う

やること

 ImageEffectに含まれるDepthOfFieldエフェクト。

 これはperspectiveカメラで使うことを前提にしており、orthographicカメラで使おうとするとうまくいかない。なんとかする。

Unityバージョン

5.3.2p1

手順

DepthOfFieldScatter.shaderを開けて

d = Linear01Depth (d);

となっている箇所をコメントアウトすれば良い。

どうしてこうなるのか?

 orthographicカメラとperspectiveカメラではdepthの算出方法が異なるため。

 詳しくは「深度バッファ 精度」あたりでググるとそれっぽいのが出る。