さけのさかなのブログ

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

紹介ページ兼オンラインマニュアル作成しました

幻想四倍剣^2 悔悟棒の謎 オンラインマニュアル

 というわけでこんな感じ。

 締め切り前の修羅場で取説つくるのはキツいナー、と毎回思ってたので、今回は細かい説明をオンラインマニュアルに放り投げて、入稿後に後付けで作れるようにしたのだった。

 意外と疲れた。

【Unity】 カスタムコルーチンでこのコールバックからの卒業

 Unity5.3からカスタムコルーチンを使えるぞ!これで何ができるの?いろんな条件で処理を止められるぞ
 ……という他に、コルーチンの戻り値を受け取るのにコールバックを使ってる部分をマシにできたりする。

 普通のコルーチンメソッドで書くとこんな感じなのを

    IEnumerator Routine(System.Action<int> callback)
    {
        var result = 0;
        for (int i = 0; i < 100; ++i)
        {
            yield return null;
            ++result;
        }

        callback(result);
    }

 こう書ける。

public class Keisan : CustomYieldInstruction
{
    public int Result { get; private set; }
    IEnumerator routine;

    public Keisan()
    {
        routine = Routine();
    }

    public override bool keepWaiting
    {
        get
        {
            return routine.MoveNext();
        }
    }

    IEnumerator Routine()
    {
        for(int i=0; i<100; ++i)
        {
            yield return null;
            ++Result;
        }
    }
}

 メソッドで書くほうが短いやんけ!

 まあ、コールバックとかツライときありますし。

 呼び方を比較するとこんな感じ。

IEnumerator Start()
{
    int kekka = 0;
    yield return Routine(result => kekka = result);
    Debug.Log(kekka);

    var keisan = new Keisan();
    yield return keisan;
    Debug.Log(keisan.Result);
}

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をやり直す機会が発生する。

進捗っぽいの