2
0

More than 3 years have passed since last update.

[Unity]SerializeReferenceの使い方がわからんかったので無理やり使えるようにしてみた

Posted at

Interfaceをやっとシリアライズできる神機能が追加されたらしいですね

2019.3.0b4

ヒャアがまんできねぇシリアライズだ!

Obj.cs
public class Obj : MonoBehaviour
{
    [SerializeReference]
    public SomeInterface なんかのインターフェース;
}
Interface.cs
public interface SomeInterface
{
    void NumberChange();
    int Number { get; }
}

[Serializable]
public class SomeImplementType1 : SomeInterface
{

    [SerializeField]
    private int EditableNumber = 0;

    private readonly int ConstantNumber = 8;

    public int Number => EditableNumber + ConstantNumber;

    public void NumberChange()
    {
        EditableNumber++;
    }
}

[Serializable]
public class SomeImplementType2 : SomeInterface
{
    [SerializeField]
    private int EditableNumber = 0;
    [SerializeField]
    private byte EditableNumber_Byte = 0;

    public int Number => EditableNumber + EditableNumber_Byte;

    public void NumberChange()
    {
        EditableNumber = 0;
        EditableNumber_Byte = 0;

    }
}

interfaceda.gif

なんやこれは???????????????????????????????????????????

クリックしても右クリックしても何も起こらないのでわけがわからず
一応表示はされているのだからシリアライズはされているのだろうと踏んで
エディター拡張で無理やり使えるようにした

PermissionAttribute.cs
using System;
using UnityEngine;

[System.AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class PermissionAttribute : PropertyAttribute
{
    public readonly Type[] Types;

    public PermissionAttribute(params Type[] types)
    {
        Types = types;
    }
}
using System.Linq;
using UnityEngine;
using UnityEditor;
using System;

[CustomPropertyDrawer(typeof(PermissionAttribute))]
public class SerializeReferenceDrawer : PropertyDrawer
{

    private int ActivateTypeIndex;
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        var permission = attribute as PermissionAttribute;
        EditorGUI.BeginChangeCheck();
        //fieldInfoはリフレクションのFieldInfo、基底クラスのPropertyDrawerが描画しているPropertyが入っている
        var targetProperty = fieldInfo.GetValue(property.serializedObject.targetObject);
        //Popup用に高さを調整、調整しないとプロパティが触れなくなる
        position.height = 16;
        var popUpTypeText = permission.Types.Select(type => type.ToString()).ToArray();
        var targetObject = property.serializedObject.targetObject;
        if (targetProperty == null)
        {
            ActivateTypeIndex = EditorGUI.Popup(position, ActivateTypeIndex, popUpTypeText);
            var instance = Activator.CreateInstance(permission.Types[ActivateTypeIndex]);
            fieldInfo.SetValue(targetObject, instance);
        }
        else
        {
            ActivateTypeIndex = EditorGUI.Popup(position, Array.IndexOf(permission.Types, targetProperty.GetType()), popUpTypeText);
            if (targetProperty.GetType() != permission.Types[ActivateTypeIndex])
            {
                var instance = Activator.CreateInstance(permission.Types[ActivateTypeIndex]);
                fieldInfo.SetValue(targetObject, instance);
            }
        }
        //Popupでかさが増えた分を描画をしたへずらす
        position.y += 16;
        position.height = EditorGUI.GetPropertyHeight(property, true) + 14;
        EditorGUI.PropertyField(position, property, label, true);
        property.serializedObject.ApplyModifiedProperties();
        EditorGUI.EndChangeCheck();
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return EditorGUI.GetPropertyHeight(property, label, true) + base.GetPropertyHeight(property, label);
    }
}

使う側

Obj.cs
using UnityEngine;
using UnityEngine.UI;

public class Obj : MonoBehaviour
{
    [SerializeField]
    private Button Button;
    //使いたいものを型で指定する
    [SerializeReference, Permission(typeof(SomeImplementType1), typeof(SomeImplementType2))]
    public SomeInterface なんかのインターフェース;


    private void Awake()
    {
        Button.onClick.AddListener(() =>
        {
            なんかのインターフェース.NumberChange();
            Button.GetComponentInChildren<Text>().text = なんかのインターフェース.Number.ToString();
        });
    }
}

やったぜ

interfacedada.gif

Type1はインクリメントしてType2はリセットする挙動
Unityを再起動してもプレイしても値が残っているので使えそうです
Activatorを使っているせいか、↓は0が代入されてましたがこれは自分のコードの問題でしょうし

private readonly int ConstantNumber = 8;

挙動を変えたりいろいろ捗りそうです

違うそうじゃない

いくらなんでもこんな面倒な手順を踏ませるはずはないので
何か見落としていると思うんですが
誰かちゃんとした使い方教えてください
属性はあるけどまだ使えないとかなんでしょうかね

2
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0