Unity自定义属性面板Inspector面板和userData数据保存

最近写一个小的编辑器功能,比较简单和入门,这里做一个简单教程。

效果图

在这里插入图片描述
在这里插入图片描述
因为最后可能需要重新编辑生成好的预设文件,要返回编辑,所以一些数据要保存下来,所以利用了userData保存数据。
下面的图是保存预设后的meta文件,可以看到userData数据。
在这里插入图片描述

实现方式

本来这个在业务中,大概是通过3个图片生成一个地图预设,最后把一些关键输入保存到meta文件的userData数据中。文章中删除了业务代码,只有自定义面板部分,代码不是全面的代码。

这里只是讲解下面板是如何摆放组件并处理。

首先我们创建一个MazeEditor.cs脚本,主要用来记录我们填写的数据。

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

//UTF8 说明
public class MazeEditor : MonoBehaviour
{
    [HideInInspector]
    public GameObject OriginObject; //起点
    [HideInInspector]
    public GameObject StarObject; //星星

    [HideInInspector]
    public string mapName;

    [HideInInspector]
    public Object mapPrefab;

    [HideInInspector]
    [TooltipAttribute("地板图")]
    public Sprite plane;

    [HideInInspector]
    [TooltipAttribute("高墙")]
    public Sprite wall;

    [HideInInspector]
    [TooltipAttribute("底障碍")]
    public Sprite slow_wall;

    [HideInInspector]
    public GameObject origin;

    [HideInInspector]
    public List<GameObject> stars;

    [HideInInspector]
    public bool mapCreated;

    [HideInInspector]
    public GameObject mapRoot;
    [HideInInspector]
    public GameObject starRoot;

    //某些星星被删除掉了。视图里也要删除
    public void StarDelete()
    {
        
    }

    public void Save()
    {
        
    }

    public void Clear()
    {
        mapCreated = false;
        stars.Clear();
        if (starRoot != null)
            GameObject.DestroyImmediate(starRoot.gameObject);
        if (mapRoot != null)
            GameObject.DestroyImmediate(mapRoot.gameObject);

        starRoot = null;
        mapRoot = null;
        plane = null;
        wall = null;
        slow_wall = null;
        mapName = "";
    }
}

然后是CreateMapEditor脚本,需要放入Editor文件夹。


using System.Collections.Generic;
using Unity.Plastic.Newtonsoft.Json;
using UnityEditor;
using UnityEngine;

//UTF8 说明

[CustomEditor(typeof(MazeEditor))]
public class CreateMapEditor : Editor
{
    int layerBuild;
    SerializedProperty m_Star;

    bool showsetting;
    bool showMenu1;
    bool showMenu2;
    void OnEnable()
    {
        // 从GameObject脚本中获取对象以显示在检查器中
        m_Star = serializedObject.FindProperty("stars");
    }
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI(); //显示
        MazeEditor myScript = (MazeEditor)target;
        if (!myScript.mapCreated)
        {
            showsetting = EditorGUILayout.Foldout(showsetting, "Flag");
            if (showsetting)
            {
                EditorGUILayout.LabelField("标记位置的对象,请勿修改。");

                myScript.OriginObject = (GameObject)EditorGUILayout.ObjectField("Origin (起点标记)", myScript.OriginObject, typeof(GameObject), true);
                myScript.StarObject = (GameObject)EditorGUILayout.ObjectField("Star (星星标记)", myScript.StarObject, typeof(GameObject), true);
                EditorGUILayout.Space(10);
            }
        }
        showMenu1 = EditorGUILayout.Foldout(showMenu1, "MapSetting");

        if (showMenu1)
        {


            myScript.mapName = EditorGUILayout.TextField("MapName (地图名)", myScript.mapName);



            myScript.plane = (Sprite)EditorGUILayout.ObjectField("Plane (地板图)", myScript.plane, typeof(Sprite), false);
            myScript.wall = (Sprite)EditorGUILayout.ObjectField("Wall (高墙)", myScript.wall, typeof(Sprite), false);
            myScript.slow_wall = (Sprite)EditorGUILayout.ObjectField("Slow Wall (减速物)", myScript.slow_wall, typeof(Sprite), false);

            if (myScript.mapRoot != null)
            {
                EditorGUI.BeginChangeCheck();
                EditorGUILayout.PropertyField(m_Star, new GUIContent("Stars (星星)"));
                if (EditorGUI.EndChangeCheck())
                {
                    myScript.StarDelete();
                }
            }


            //绘制按钮
            EditorGUILayout.BeginHorizontal();


            if (GUILayout.Button("创建地图"))
            {
                myScript.mapCreated = true;
                Create();
            }

            if (myScript.mapCreated)
            {
                if (GUILayout.Button("创建星星"))
                {
                    
                }
                if (GUILayout.Button("保存"))
                {
                    SaveMap();
                    Clear();
                }
                if (GUILayout.Button("清空"))
                {
                    Clear();
                }
            }
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.Space(10);
        }


        showMenu2 = EditorGUILayout.Foldout(showMenu2, "Edit");
        if (showMenu2)
        {
            myScript.mapPrefab = (Object)EditorGUILayout.ObjectField("MapPrefab (地图预设)", myScript.mapPrefab, typeof(Object), false);
            if (GUILayout.Button("修改预设"))
            {
                Edit();
            }
        }
    }

    public void Create()
    {
        MazeEditor myScript = (MazeEditor)target;
        if (myScript.plane == null)
        {
            Debug.Log("地面图片还没有。");
            return;
        }
		//这里省略了业务代码。
    }


    public void Clear()
    {
        MazeEditor myScript = (MazeEditor)target;
        myScript.Clear();
    }

    public void SaveMap()
    {
        MazeEditor myScript = (MazeEditor)target;
        if (myScript.mapName.Length == 0)
        {
            Debug.LogWarning("请输入保存的名称。");
            return;
        }
        string pathprefab = "Assets/ResAll/maps/" + myScript.mapName + ".prefab";
        string bundlename = "reboot/maps/" + myScript.mapName;

        myScript.mapRoot.name = myScript.mapName;
        bool ok;
        PrefabUtility.SaveAsPrefabAsset(myScript.mapRoot, pathprefab, out ok);
        if (!ok)
        {
            Debug.LogError("保存失败:" + pathprefab);
            return;
        }
        AssetImporter assetImporter = AssetImporter.GetAtPath(pathprefab);  //得到Asset
        assetImporter.assetBundleName = bundlename;
        assetImporter.assetBundleVariant = "map";

        MapData md = new MapData();	//一个保存Json的数据结构。
        md.mapName = myScript.mapName;
        md.spPlane = AssetDatabase.GetAssetPath(myScript.plane);
        md.spWall = AssetDatabase.GetAssetPath(myScript.wall);
        md.spSlow = AssetDatabase.GetAssetPath(myScript.slow_wall);
        md.origin = myScript.origin.transform.position;
        md.stars = new List<Vector3>();
        for (int i = 0; i < myScript.stars.Count; i++)
        {
            md.stars.Add((Vector3)myScript.stars[i].transform.position);
        }

        var settings = new Newtonsoft.Json.JsonSerializerSettings();
        settings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        string str = Newtonsoft.Json.JsonConvert.SerializeObject(md, settings);
        assetImporter.userData = str;
        assetImporter.SaveAndReimport();
        AssetDatabase.Refresh();

        Debug.Log("保存成功:" + pathprefab);
        myScript.Save();
    }

    void Edit()
    {
        MazeEditor myScript = (MazeEditor)target;
        Object prefab = myScript.mapPrefab;
        Clear();    //必须放在获取路径的后面

        string pathprefab = AssetDatabase.GetAssetPath(prefab);

        AssetImporter assetImporter = AssetImporter.GetAtPath(pathprefab);  //得到Asset
        string str = assetImporter.userData;

        MapData md = Newtonsoft.Json.JsonConvert.DeserializeObject<MapData>(str);
        myScript.mapName = md.mapName;
        myScript.plane = (Sprite)AssetDatabase.LoadAssetAtPath(md.spPlane, typeof(Sprite));
        myScript.wall = (Sprite)AssetDatabase.LoadAssetAtPath(md.spWall, typeof(Sprite));
        myScript.slow_wall = (Sprite)AssetDatabase.LoadAssetAtPath(md.spSlow, typeof(Sprite));

        Debug.Log("预设读取完毕:" + pathprefab);
    }
}

脚本简要说明

CustomEditor

首先要声明[CustomEditor(typeof(MazeEditor))],和继承自Editor,这样才能和Maze脚本联系起来。

[CustomEditor(typeof(MazeEditor))]
public class CreateMapEditor : Editor

OnInspectorGUI

核心就是要覆写OnInspectorGUI函数。
base.OnInspectorGUI();表示显示MazeEditor.cs的布局显示。

获得脚本对象

MazeEditor myScript = (MazeEditor)target;
通过这个可以获得MazeEditor的方法和属性。

箭头收缩

showsetting = EditorGUILayout.Foldout(showsetting, “Flag”);
这个是一个箭头收放容器,点击箭头可以显示和隐藏。
在这里插入图片描述
在这里插入图片描述

LabelField

EditorGUILayout.LabelField
是文本显示。

ObjectField

myScript.OriginObject = (GameObject)EditorGUILayout.ObjectField(“Origin (起点标记)”, myScript.OriginObject, typeof(GameObject), true);
这个就是可以放入一个对象,把对象给脚本的的变量,最后的是类型,true表示可以把Hierarchy里的对象拖入,否则只能拖入Assets文件夹里的资源。

后面的图片可以是:
myScript.plane = (Sprite)EditorGUILayout.ObjectField(“Plane (地板图)”, myScript.plane, typeof(Sprite), false);
因为类型是Sprite我们可以直接看到图片选择框。
在这里插入图片描述

BeginChangeCheck和EndChangeCheck

				EditorGUI.BeginChangeCheck();
                EditorGUILayout.PropertyField(m_Star, new GUIContent("Stars (星星)"));
                if (EditorGUI.EndChangeCheck())
                {
                    myScript.StarDelete();
                }

这两个是用于检测数据变化的,当调整了面板,可以去做一切其他事情。

BeginHorizontal

EditorGUILayout.BeginHorizontal();
这个是布局,我们可以看到按钮可以横向摆放,最后结束用EditorGUILayout.EndHorizontal();

Space

EditorGUILayout.Space(10);
是间距。

userData

userData数据可以保存在meta文件中,它可以通过assetImporter.userData = str;的方式保存,因为是字符串,我这里用json转化成string的方式保存方便使用。

结束

这就是一个简单的Inspector的自定义面板。

还有很多可以使用的组件,可以参考官方文档

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值