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
/* * 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); } }
/* * 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 }