天天看點

Unity自定義資訊儲存工具(存檔工具,可按照需求拓展為關卡編輯器),編輯器拓展樣式查找工具

2021.3.12更新tips:今天發現使用LitJson存字典的key值隻能使用string型,想起來這邊我還沒有試過不同key類型,就測試了一波,發現這邊就算把key值設為string,免去内部序列化的類型轉化,還是不能被寫入,看來序列化這一趴的坑點挺多的,mark一下準備找時間深入研究一下序列化方面的源碼。

2021.3.4更新,之前研究了忘記同步上來了,上結論,不可以用dictionary以及list<list<>>這種嵌套的結構,不然不會被unity的setdirty方法所儲存,但是可以使用系列化的類的嵌套,類中有單層的List<>是可以被序列化的。

2021.1.25更新,加入退出視窗時儲存功能。

先說一下這個工具可以幹神馬呢~~~它可以讓策劃愉快的對遊戲資料邊調試邊修改,友善他們調整資料用滴。也可以作為工程的自定義資料的存檔,它會将資料存到.asset檔案裡面。

首先是自定義資料的資料結構,這裡全部都可以自定義,但是類型最好用基礎類型,數組或者list,因為本人踩了Dictionary的坑,貌似是無法識别到dictionary的改變導緻不能儲存,不知道有木有大神指點迷津= =

然後是最清晰明了的上圖(如果偷懶不想改的話就按這個位置好了),靈活修改的部分已經在代碼中标記出來了,直接删掉寫入自己需要的排版和資料就完事了:

1.在resources下建立檔案夾:

Unity自定義資訊儲存工具(存檔工具,可按照需求拓展為關卡編輯器),編輯器拓展樣式查找工具

2.右鍵建立.asset檔案:

Unity自定義資訊儲存工具(存檔工具,可按照需求拓展為關卡編輯器),編輯器拓展樣式查找工具

3.不想找的話可以直接在edit欄找到對應的檔案:

Unity自定義資訊儲存工具(存檔工具,可按照需求拓展為關卡編輯器),編輯器拓展樣式查找工具

4.然後就可以添加對應的新資訊:

Unity自定義資訊儲存工具(存檔工具,可按照需求拓展為關卡編輯器),編輯器拓展樣式查找工具

5.點選對應的資料就可以在彈出的視窗中進行資料的修改了,新視窗就可以免得策劃點來點去點沒了。。。

Unity自定義資訊儲存工具(存檔工具,可按照需求拓展為關卡編輯器),編輯器拓展樣式查找工具
Unity自定義資訊儲存工具(存檔工具,可按照需求拓展為關卡編輯器),編輯器拓展樣式查找工具

視窗樣式自己按需排版就完事了

修改資料之後直接就可以實時儲存,也寫了提示視窗,避免點錯帶來删庫跑路的後果~接下來就是代碼了。

LevelData:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "GameDataAsset", menuName = "Creat GameData Asset")]
public class LevelData : ScriptableObject
{
    [Header("關卡字典")]
    [HideInInspector]
    [SerializeField] public List<InsideData> preinstallLevels;
    /// <summary>
    /// 找資料
    /// </summary>
    /// <param name="id">關卡id</param>
    /// <param name="data">資料</param>
    /// <returns></returns>
    public static bool FindData(int id,out InsideData data)
    {
        LevelData datas = Resources.Load("LevelDataAssest/LevelData") as LevelData;
        if (datas.preinstallLevels == null || datas.preinstallLevels.Count < 1)
        {
            //Debug.LogError("沒資料");
            data = null;
            return false;
        }
        foreach (var v in datas.preinstallLevels)
        {
            if(v.levelID == id)
            {
                data = v;
                return true;
            }
        }
        //Debug.LogError("沒這個ID的資料");
        data = null;
        return false;
    }

}
/// <summary>
/// 關卡資訊,根據需要修改
/// ps.注意不可以用dictionary以及list<list<>>這種嵌套的結構,不然不會被unity的setdirty方法所儲存,可以用序列化過的類嵌套
/// </summary>
[System.Serializable]
public class InsideData {
    [SerializeField] public int levelID;//章節ID
    //這裡寫入需要存的資料結構
}


           

然後是放在Editor下的LevelDataEditor:

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
[CustomEditor(typeof(LevelData))]
public class LevelDataEditor : Editor
{
    public LevelData data;
    public LevelDataWindow addWindow;
    SerializedProperty dataProperty;
    ReorderableList nowList;
    private void OnEnable()
    {
        data = target as LevelData;
        if (data.preinstallLevels == null || data.preinstallLevels.Count < 1)
        {
            //Debug.Log("沒資料");
        }
        dataProperty = serializedObject.FindProperty("preinstallLevels");
        nowList = new ReorderableList(serializedObject, dataProperty, false, true, true, true);
        nowList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
        {
            var element = dataProperty.GetArrayElementAtIndex(index);
            rect.height -= 4;
            rect.y += 2;
            GUIContent content = new GUIContent();
            content.text = "Level" + (index + 1);
            EditorGUI.PropertyField(rect, element, content);
        };
        nowList.onAddCallback = (ReorderableList l) =>
        {
            if (addWindow != null)
                addWindow.Close();
            addWindow = (LevelDataWindow)EditorWindow.GetWindow(typeof(LevelDataWindow), true, "LevelDataWindow");
            addWindow.minSize = new Vector2(200, 800);
            addWindow.Init(this);
            addWindow.Show();
        };

        nowList.onRemoveCallback = (ReorderableList l) =>
        {
            if (addWindow != null)
                addWindow.Close();
            if (EditorUtility.DisplayDialog("删除關卡資訊", "你想删除選中的關卡資訊嗎?", "是", "否"))
            {
                data.preinstallLevels.RemoveAt(l.index);
            }
        };
        nowList.drawHeaderCallback = (rect) =>
            EditorGUI.LabelField(rect, "關卡資料");
        nowList.onSelectCallback = (ReorderableList l) =>
        {
            if (addWindow != null)
                addWindow.Close();
            addWindow = (LevelDataWindow)EditorWindow.GetWindow(typeof(LevelDataWindow), true, "LevelDataWindow");
            addWindow.minSize = new Vector2(200, 800);
            addWindow.Init(this, data.preinstallLevels[l.index]);
            addWindow.Show();
        };
    }

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        if (GUILayout.Button("新增關卡資訊", GUILayout.Height(25)))
        {
            if (addWindow != null)
                addWindow.Close();
            addWindow = (LevelDataWindow)EditorWindow.GetWindow(typeof(LevelDataWindow), true, "LevelDataWindow");
            addWindow.minSize = new Vector2(200, 800);
            addWindow.Init(this);
            addWindow.Show();
        }
        if (data.preinstallLevels == null || data.preinstallLevels.Count < 1)
        {
            EditorGUILayout.HelpBox("沒有資料", MessageType.Warning);
            return;
        }
        nowList.DoLayoutList();
        if (GUILayout.Button("清除所有資訊", GUILayout.Height(25)))
        {
            if (EditorUtility.DisplayDialog("删除關卡資訊", "你想删除所有的關卡資訊嗎?", "是", "否"))
            {
                data.preinstallLevels.Clear();
                Save();
            }           
        }
        serializedObject.Update();
        if (GUI.changed)
        {
            Save();
        }
    }
    public void Save()
    {
        EditorUtility.SetDirty(target);
        serializedObject.ApplyModifiedProperties();
        EditorUtility.SetDirty(data);
    }
    [MenuItem("Edit/Level data")]
    private static void SelectLevelData()
    {
        Selection.activeObject = Resources.Load("LevelDataAssest/LevelData") as LevelData;
    }
}

           

以及修改資料的彈窗:LevelDataWindow

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class LevelDataWindow : EditorWindow 
{
    private LevelData datas;//存檔資料集
    private InsideData data;//新加的資料
    private LevelDataEditor now;
    private void OnEnable()
    {
        datas = Resources.Load("LevelDataAssest/LevelData") as LevelData;
        minSize = new Vector2(0, 700f);        
    }
    private void OnGUI()
    {
        //這裡加入GUI排版以及資料指派
        if (GUI.changed)
        {
            if (IsAlreadyHaveID(data.levelID))
            {
                for(int i = 0; i < datas.preinstallLevels.Count; i++)
                {
                    if (datas.preinstallLevels[i].levelID == data.levelID)
                    {
                        datas.preinstallLevels[i] = data;
                    }
                }
            }
            else
            {
                datas.preinstallLevels.Add(data);
            }
            now.Save();
            Undo.RecordObject(datas, "儲存資料");
            EditorUtility.SetDirty(datas);
        }
    }
    private void OnDisable()
    {
        now.Save();
        Undo.RecordObject(datas, "儲存資料");
        EditorUtility.SetDirty(datas);
        AssetDatabase.SaveAssets();
    }
    public void Init(LevelDataEditor now,InsideData data = null)
    {
        this.now = now;
        if (data != null)
        {
            this.data = data;
        }      
    }
    public bool IsAlreadyHaveID(int id)
    {
        foreach(var v in datas.preinstallLevels)
        {
            if(id == v.levelID)
            {
                return true;
            }
        }
        return false;
    }
}

           

最後再附上一個實用滴樣式查找工具,同樣是放在Editor下的GUIStyleViewer:

using UnityEngine;
using UnityEditor;

public class GUIStyleViewer : EditorWindow {
    private Vector2 scrollVector2 = Vector2.zero;
    private string search = "";

    [MenuItem("Tools/GUIStyle檢視器")]
    public static void InitWindow()
    {
        EditorWindow.GetWindow(typeof(GUIStyleViewer));
    }

    void OnGUI()
    {
        GUILayout.BeginHorizontal("HelpBox");
        GUILayout.Space(30);
        search = EditorGUILayout.TextField("", search, "SearchTextField", GUILayout.MaxWidth(position.x / 3));
        GUILayout.Label("", "SearchCancelButtonEmpty");
        GUILayout.EndHorizontal();
        scrollVector2 = GUILayout.BeginScrollView(scrollVector2);
        foreach (GUIStyle style in GUI.skin.customStyles)
        {
            if (style.name.ToLower().Contains(search.ToLower()))
            {
                DrawStyleItem(style);
            }
        }
        GUILayout.EndScrollView();
    }

    void DrawStyleItem(GUIStyle style)
    {
        GUILayout.BeginHorizontal("box");
        GUILayout.Space(40);
        EditorGUILayout.SelectableLabel(style.name);
        GUILayout.FlexibleSpace();
        EditorGUILayout.SelectableLabel(style.name, style);
        GUILayout.Space(40);
        EditorGUILayout.SelectableLabel("", style, GUILayout.Height(40), GUILayout.Width(40));
        GUILayout.Space(50);
        if (GUILayout.Button("複制GUIStyle名字"))
        {
            TextEditor textEditor = new TextEditor();
            textEditor.text = style.name;
            textEditor.OnFocus();
            textEditor.Copy();
        }
        GUILayout.EndHorizontal();
        GUILayout.Space(10);
    }
}
           

沖~

繼續閱讀