Unity场景道具模型拓展自定义编辑器

(一)适用情况

当游戏主角进入特定的场景或者关卡,每个关卡需要加载不同位置的模型,道具等。这些信息需要先在unity编辑器里面配置好,一般由策划干这事,然后把这些位置道具信息保存在文件,当游戏主角进入后根据保存的文件信息加载模型道具。如 跑酷场景的金币 赛车赛道的道具

(二)实例文件格式 Json

需要导入SimpleJson 具体使用方法可以看我另外一篇《Unity游戏数据用Json保存》,有详细介绍 http://www.cnblogs.com/July7th/p/4808095.html

(三)图例

PathPropManager节点管理所有的道具信息.

(四)示例代码

//道具类型;
public enum PropsType
{
    PT_ACCEL = 0,
    PT_MISSILE = 1,
    PT_BULLET = 2,
    PT_SHIELD = 3,
    PT_NONE = 4,
}

/*
 * filename : PathPropManager.cs ;
 * function : 道具点管理编辑;
 *
*/
#if UNITY_EDITOR

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

public class PathPropManager : MonoBehaviour {

    private PathProp[] mPathPropList; // 所有的道具点;

    public TextAsset mData = null;  //道具点xml存储文件;
    public PropsType type = PropsType.PT_ACCEL; //默认道具;
    public bool AlignGround = true;     //是否对齐地面;
    public int Layer = 0;               //地面层级;
    public float GroundHeight = 0.5f;  //离地面距离;
    public bool ShowIcon = true;    //是否用图片显示路点上的道具;
    public bool ShowCube = true;    //是否用方块显示路点上的道具;

    void Awake()
    {
    }

    //刷新所有道具点;
    private void UpdateList()
    {
        mPathPropList = transform.GetComponentsInChildren<PathProp>();
    }

    //当删除某个道具点后自动重新计算序号;
    public void ResetList()
    {
        UpdateList();
        for (int i = 0; i < mPathPropList.Length; i++)
        {
            mPathPropList[i].gameObject.name = i.ToString();
            mPathPropList[i].index = i;
        }
    }

    //从json表中重新加载道具会清除当前编辑的所有道具;
    public bool CreatePropBySaveData()
    {
        if (mPathPropList == null)
        {
            return false;
        }
        string fullPath = UnityEditor.AssetDatabase.GetAssetPath(mData);
        if (string.IsNullOrEmpty(fullPath))
        {
            Debug.LogError("文件路径发生错误.");
            return false;
        }

        int startIndex = ("Assets/Resources/").Length;
        string filestr = fullPath.Substring(startIndex, fullPath.LastIndexOf(‘.‘) - startIndex);

        PathPropReader wpr = new PathPropReader(filestr);
        List<PathPropAttributes> ppsa =  wpr.Reader();

        if (ppsa == null)
        {
            Debug.LogError("加载道具配置表失败");
        }

        Transform[] trans = transform.GetComponentsInChildren<Transform>();
        foreach (Transform child in trans)
        {
            if (child == transform)
            {
                continue;
            }
            DestroyImmediate(child.gameObject);
        }
        for (int i = 0; i < ppsa.Count; i++)
        {
            GameObject go = new GameObject();
            go.transform.parent = transform;
            go.name = ppsa[i].index.ToString();
            go.transform.position = ppsa[i].position;
            go.transform.rotation = Quaternion.Euler(ppsa[i].rotation);
            go.transform.localScale = ppsa[i].scale;
            PathProp pp = go.AddComponent<PathProp>();
            pp.index = ppsa[i].index;
            pp.type = ppsa[i].type;
        }
        return true;
    }

    //保存道具点信息到json;
    public bool CreateXmlByProps()
    {
        UpdateList();
        if (mData == null)
        {
            Debug.LogError("没有找到保存的路径,请拖动一个json文件到PathPropManager的第一个参数.");
            return false;
        }
        string fullPath = UnityEditor.AssetDatabase.GetAssetPath(mData);
        if (string.IsNullOrEmpty(fullPath))
        {
            Debug.LogError("文件路径发生错误.");
            return false;
        }
        int startIndex = ("Assets/Resources/").Length;
        string filestr = fullPath.Substring(startIndex, fullPath.LastIndexOf(‘.‘) - startIndex);
        PathPropReader pr = new PathPropReader(filestr);
        return pr.SaveData(mPathPropList);
    }

    //添加新的道具点;
    public void AddProp()
    {
        UpdateList();
        GameObject go = new GameObject();
        go.transform.parent = transform;
        go.name = (mPathPropList.Length).ToString();
        PathProp pp = go.AddComponent<PathProp>();

        if (mPathPropList != null)
        {
            if (mPathPropList.Length < 1)
            {
                //生成的路标点移动到视窗内;
                Debug.Log("move to view");
                UnityEditor.EditorApplication.ExecuteMenuItem("GameObject/Move To View");
            }
            else
            {
                SetPathPropPos(go.transform, mPathPropList.Length - 1);
            }
        }
        pp.index = mPathPropList.Length;
        UnityEditor.Selection.activeTransform = go.transform;
        //UnityEditor.EditorApplication.ExecuteMenuItem("Edit/Lock View to Selected");
    }

    //生成的道具点在最后一个路标点的前方,index为前一个路标的index;
    private bool SetPathPropPos(Transform trans, int index)
    {
        if (index < 0 || index >= mPathPropList.Length)
        {
            return false;
        }
        Vector3 pos = mPathPropList[index].transform.position + Vector3.up * 10f;
        Quaternion q = mPathPropList[index].transform.rotation;

        //生成的路标点在最后一个路标点的前方;
        //当前位置 = 上个路标点位置 + 上个路标点旋转 * 世界位置前 * 最大两点距离;
        trans.position = pos + q * Vector3.forward * (10);
        trans.rotation = q;
        return true;
    }

    void OnDrawGizmos()
    {
        UpdateList();
        if (UnityEditor.Selection.activeTransform == null)
        {
            return;
        }

        //移动路点更新数据;
        if (UnityEditor.Selection.activeTransform.parent == transform)
        {
            int temIndex = int.Parse(UnityEditor.Selection.activeTransform.name);
            for (int i = 0; i < mPathPropList.Length; i++)
            {
                PathProp pp = mPathPropList[i].GetComponent<PathProp>();
                if (pp == null)
                {
                    Debug.LogError("get PathProp failed,i=" + i);
                    return;
                }
                if (temIndex == pp.index)
                {
                    if (AlignGround == false)
                    {
                        break;
                    }
                    //对齐地面;
                    RaycastHit hit;
                    if (Physics.Raycast(UnityEditor.Selection.activeTransform.position, -Vector3.up, out hit, 100.0f, 1 << Layer))
                    {
                        //float dis = Vector3.Distance(Selection.activeTransform.position, hit.point);
                        //调整高度;
                        UnityEditor.Selection.activeTransform.position = new Vector3(UnityEditor.Selection.activeTransform.position.x, hit.point.y + GroundHeight, UnityEditor.Selection.activeTransform.position.z);
                    }
                }
            }
        }

        //绘图;
        for (int i = 0; i < mPathPropList.Length; i++)
        {
            if (ShowIcon)
            {
                string str = "";
                switch(mPathPropList[i].type)
                {
                    case PropsType.PT_ACCEL: str = "js"; break;
                    case PropsType.PT_BULLET: str = "zd"; break;
                    case PropsType.PT_MISSILE: str = "dd"; break;
                    case PropsType.PT_SHIELD: str = "fy"; break;

                }
                Gizmos.DrawIcon(mPathPropList[i].transform.position, str + ".png");
            }
            if (ShowCube)
            {
                Gizmos.color = Color.red;
                Gizmos.DrawCube(mPathPropList[i].transform.position,Vector3.one);
            }
        }
    }
}

#endif

PathPropManager.cs

/*
 * filename : PathPropManagerWindow.cs ;
 * function : 道具点管理自定义编辑;
 *
*/
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;

[CustomEditor(typeof(PathPropManager))]
public class PathPropManagerWindow : Editor
{
    private PathPropManager mPathPropManager = null;

    void OnEnable()
    {

    }

    public override void OnInspectorGUI()
    {
        mPathPropManager = target as PathPropManager;
        if (mPathPropManager == null)
            return;

        EditorGUILayout.Space();
        GUILayout.BeginHorizontal();

        //刷新路标点;
        if (GUILayout.Button("ReLoad", GUILayout.Height(20)))
        {
            mPathPropManager.CreatePropBySaveData();
        }
        //获取Json文件数据
        mPathPropManager.mData = (TextAsset)EditorGUILayout.ObjectField(mPathPropManager.mData, typeof(TextAsset), false);
        GUILayout.EndHorizontal();
        EditorGUILayout.Space();

        mPathPropManager.type = (PropsType)EditorGUILayout.EnumPopup("DefaultProp", mPathPropManager.type);
        mPathPropManager.Layer = EditorGUILayout.LayerField("AligndLayer", LayerMask.NameToLayer("Floor"));
        mPathPropManager.AlignGround = EditorGUILayout.Toggle("AlignGround", mPathPropManager.AlignGround);
        mPathPropManager.GroundHeight = EditorGUILayout.FloatField("GroundHeight", mPathPropManager.GroundHeight);
        mPathPropManager.ShowIcon = EditorGUILayout.Toggle("ShowIcon", mPathPropManager.ShowIcon);
        mPathPropManager.ShowCube = EditorGUILayout.Toggle("ShowCube", mPathPropManager.ShowCube);

        EditorGUILayout.Space();
        GUILayout.BeginHorizontal();
        //保存到Json文件;
        if (GUILayout.Button("SaveToXml", GUILayout.Height(20)))
        {
            if (mPathPropManager.CreateXmlByProps())
            {
                Debug.Log("保存成功");
            }
        }

        GUILayout.EndHorizontal();
        EditorGUILayout.Space();

        //标记路点已改变;
        EditorUtility.SetDirty(mPathPropManager);
    }

}

PathPropManagerWindow.cs

/*
 * filename : PathPropReader.cs ;
 * function : 道具点l读取;
 *
*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

   public class PathPropAttributes
    {
        public int index { get; set; }
        public Vector3 position { get; set; }
        public Vector3 rotation { get; set; }
        public Vector3 scale { get; set; }
        public PropsType type { get; set; }
    }

    public class PathPropReader
    {
        private string mPath { get; set; }
        private List<PathPropAttributes> mWpAList = new List<PathPropAttributes>();

        // resources目录下的相对路径;如路径为Assets/Resouces/Xml/PathWayPoint/test.xml,path = Xml/PathWayPoint/test</param>
        public PathPropReader(string path)
        {
            if (mWpAList == null)
            {
                mWpAList = new List<PathPropAttributes>();
            }
            if (string.IsNullOrEmpty(path))
            {
                return;
            }
            mPath = path;
        }
        public List<PathPropAttributes> GetPositionList()
        {
            return mWpAList;
        }

        public List<PathPropAttributes> Reader()
        {
            if (string.IsNullOrEmpty(mPath))
            {
                Debug.LogError("没有找到读取的路径.");
                return null;
            }
            mWpAList.Clear();
            //读入;
            TextAsset ta = (TextAsset)Resources.Load(mPath);
            if (ta == null)
            {
                Debug.LogError("load props txt failed.path = "+mPath);
                return null;
            }
            string txt = ta.text;
            SimpleJSON.JSONArray jsArray = SimpleJSON.JSON.Parse(txt).AsArray;

            for (int i = 0; i < jsArray.Count; i++)
            {
                SimpleJSON.JSONNode node = jsArray[i];
                PathPropAttributes wpa = new PathPropAttributes();
                wpa.index = node["index"].AsInt;
                wpa.position = Helper.StringToVector3(Helper.DeleteChar(node["position"], ‘(‘, ‘)‘));
                wpa.rotation = Helper.StringToVector3(Helper.DeleteChar(node["rotation"], ‘(‘, ‘)‘));
                wpa.scale = Helper.StringToVector3(Helper.DeleteChar(node["scale"], ‘(‘, ‘)‘));
                wpa.type = (PropsType)System.Enum.Parse(typeof(PropsType), node["type"]);
                mWpAList.Add(wpa);
            }
            return mWpAList;
        }

#if UNITY_EDITOR
        public bool SaveData(PathProp[] pathProps)
        {
            if (string.IsNullOrEmpty(mPath))
            {
                Debug.LogError("没有找到保存的路径.");
                return false;
            }
            if (pathProps.Length <= 0)
            {
                Debug.LogError("至少有一个道具点才能保存.");
                return false;
            }
            //读入;
            TextAsset ta = (TextAsset)Resources.Load(mPath);
            string txt = ta.text;
            SimpleJSON.JSONArray jsArray = SimpleJSON.JSON.Parse(txt).AsArray;
            jsArray.RemoveAll();
            string str = "{\"index\":\"0000\", \"position\":\"0000\" , \"rotation\":\"0000\", \"scale\":\"0000\" , \"type\":\"0000\"}";
            for (int j = 0; j < pathProps.Length; j++)
            {
                SimpleJSON.JSONNode newNode = SimpleJSON.JSON.Parse(str);
                newNode["index"].Value = pathProps[j].index.ToString();
                newNode["position"] = pathProps[j].gameObject.transform.position.ToString();
                newNode["rotation"] = pathProps[j].gameObject.transform.rotation.eulerAngles.ToString();
                newNode["scale"] = pathProps[j].gameObject.transform.localScale.ToString();
                newNode["type"] = pathProps[j].type.ToString();
                jsArray.Add(newNode);
            }
            //写入;
            string fp = Application.dataPath + "/Resources/" + mPath + ".json";
            byte[] myByte = System.Text.Encoding.UTF8.GetBytes(jsArray.ToString());
            using (System.IO.FileStream stream = new System.IO.FileStream(fp, System.IO.FileMode.Create))
            {
                stream.Write(myByte, 0, myByte.Length);
            }
            return true;
        }
#endif
    }

PathPropReader.cs

时间: 2024-07-29 19:15:14

Unity场景道具模型拓展自定义编辑器的相关文章

拓展自定义编辑器窗口(EditorGUILayout类)

Unity支持自行创建窗口,也支持自定义窗口布局.在Project视图中创建一个Editor文件夹,在文件夹中再创建一条脚本. 自定义窗口需要让脚本继承EditorWindow再设置MenuItem,此时在Unity导航菜单栏中GameObjec->window就可创建一个自定义窗口. 0.窗口: using UnityEngine; using UnityEditor;//引入编辑器命名空间 publicclassMyEditor:EditorWindow { [MenuItem("Ga

Unity场景、模型等资源转UE4

   共同点: 在世界空间和模型空间内,UE4.Unity均为左手系 不同点: 轴向 模型空间内,UE4.Unity轴向的对应关系如下: 轴向 UE4 Unity 向前 +x +z 向右 +y +x 向上 +z +y 旋转 欧拉角(Euler Angles)使用三个角度值来描述物体在三维空间的任意朝向 它的基本思想是让物体开始于“标准”方位(物体坐标轴和惯性坐标轴对齐),每次让其绕着物体坐标系某个轴进行旋转,通过三次旋转就可以达到最终朝向 如果从惯性坐标系到物体坐标系,欧拉角顺序为:m-n-p:

(转)初步认识拓展UnityEditor编辑器定制

初步认识拓展UnityEditor编辑器定制 热度 9529 2015-9-4 18:50 |个人分类:Unity3d| 编辑器, 拓展 我相信无数初学者看别人游戏都经常看到他们的Inspector中出现界面和默认不同,但又不知道怎么弄出来的.下面我粗略介绍一下如何自定义这些. MenuItem属性把任意静态函数变成为一个菜单命令.仅静态函数能使用这个MenuItem属性. 用法:一般在Editor文件夹里创建代码,代码归类 [MenuItem("MyMenu/Do Something"

spring mvc 自定义编辑器

起始知识: Java标准的PropertyEditor的核心功能是将一个字符串转换为一个Java对象,以便根据界面的输入或配置文件中的配置字符串构造出一个JVM内部的java对象. 如何注册自定义的属性编辑器: 1.实现PropertyEditor接口或者继承PropertyEditorSupport类 2.在Spring上下文中声明一个org.springframework.beans.factory.config.CustomEditorConfigurer的bean <!--将bean1中

iOS开发——笔记篇&amp;关于字典plist读取/字典转模型/自定义View/MVC/Xib的使用/MJExtension使用总结

关于字典plist读取/字典转模型/自定义View/MVC/Xib的使用/MJExtension使用总结 一:Plist读取 1 /******************************************************************************/ 2 一:简单plist读取 3 4 1:定义一个数组用来保存读取出来的plist数据 5 @property (nonatomic, strong) NSArray *shops; 6 7 2:使用懒加载的方

关于Unity3D自定义编辑器的学习

被人物编辑器折腾了一个月,最终还是交了点成品上去(还要很多优化都还么做).  刚接手这项工作时觉得没概念,没想法,不知道.后来就去看<<Unity5.X从入门到精通>>中有关于自定义编辑器(自定义Inspector和自定义Scene或GUI)的一些例子,还包括看了 雨松的编辑器教程 和 自定义结构显示在Inspector的方法 看完之后也实战了一下就算入了门,就分析自己项目的人物对应的数据,如下图: 上述数据其实很简单但是对于我这种初学者来说就有点难度,首先因为Actions 和

[Unity菜鸟] FBX模型动画提取

角色已经人形化(Humanoid)了,那它的动画可以用在其它的模型上了也就是可以共用一套模型动画了,但是你有没有发现那动画是和fbx模型绑在一起的,没关系你可以选中这几个动画文件按Contrl+D就可以提取出来了,然后你可以把整个fbx模型都删掉了,新生成的动画已经不再基于fbx了,这样可以大大减小资源大小. 如果是一个程序员的话你可能会想那这个实现代码是怎样的呢 using UnityEngine; using UnityEditor; using System.Collections; us

线程池与非线程池应用场景及模型对比分析

在网络编程中经常用到线程池和连接池,今天就对其中常用的线程池的基本应用场景和模型做个简单的对比分析. 1.  业务流程对比 a.  非线程池业务流模型: 上图标识了基本的非线程池的线程模型,前端1有多少连接则前端客户端2与前端服务器端3均需建立一对一的线程数进行响应的连接.前端服务器端3与后端服务器端4也需建立响应数目的线程进行连接处理相关业务. 当一个任务处理完毕后线程退出,在下一个任务到来的时候前端服务器端创建新的线程来处理新的任务. b.线程池模型: 上图标识了基本的线程池模型.前端客户端

Toast拓展--自定义显示时间和动画

Toast拓展–自定义显示时间和动画 我们在Android应用开发中经常会需要在界面上弹出一个对界面操作无影响的小提示框来提示用户一些信息,这时候一般都会使用Android原生的Toast类 Toast.makeText(mContext, "消息内容", Toast.LENGTH_SHORT).show(); 一开始觉得,挺好用的,就有点什么消息都用Toast显示了. 但是用久了就发现,Toast的显示和消失动画不符合自己的要求,显示时间也只有SHORT和LONG两种选择,好像不太够