Unity场景道具模型拓展自定义编辑器
(一)适用情况
当游戏主角进入特定的场景或者关卡,每个关卡需要加载不同位置的模型,道具等。这些信息需要先在unity编辑器里面配置好,一般由策划干这事,然后把这些位置道具信息保存在文件,当游戏主角进入后根据保存的文件信息加载模型道具。如 跑酷场景的金币 赛车赛道的道具
(二)实例文件格式 Json
需要导入SimpleJson 具体使用方法可以看我另外一篇《Unity游戏数据用Json保存》,有详细介绍 http://www.cnblogs.com/July7th/p/4808095.html
(三)图例
PathPropManager节点管理所有的道具信息.
(四)示例代码
//道具类型;
public enum PropsType
{
PT_ACCEL = ,
PT_MISSILE = ,
PT_BULLET = ,
PT_SHIELD = ,
PT_NONE = ,
}
/*
* 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 = ; //地面层级;
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 = ; 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 = ; 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 < )
{
//生成的路标点移动到视窗内;
Debug.Log("move to view");
UnityEditor.EditorApplication.ExecuteMenuItem("GameObject/Move To View");
}
else
{
SetPathPropPos(go.transform, mPathPropList.Length - );
}
}
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 < || 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 * ();
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 = ; 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, << 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 = ; 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()))
{
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()))
{
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 = ; 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 <= )
{
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 = ; 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, , myByte.Length);
}
return true;
}
#endif
}
PathPropReader.cs
Unity场景道具模型拓展自定义编辑器的更多相关文章
- 拓展自定义编辑器窗口(EditorGUILayout类)
Unity支持自行创建窗口,也支持自定义窗口布局.在Project视图中创建一个Editor文件夹,在文件夹中再创建一条脚本. 自定义窗口需要让脚本继承EditorWindow再设置MenuItem, ...
- Unity3D研究院之拓展自定义编辑器窗口
Unity支持自行创建窗口,也支持自定义窗口布局.在Project视图中创建一个Editor文件夹,在文件夹中在创建一条脚本. 自定义窗口需要让脚本继承EditorWindow在设置MenuItem, ...
- 【Unity】自定义编辑器窗口——拓展编辑器功能
最近学习了Unity自定义编辑器窗口,下面简单总结,方便用到时回顾. 新建一个脚本: using UnityEngine; using System.Collections; using UnityE ...
- (转)初步认识拓展UnityEditor编辑器定制
初步认识拓展UnityEditor编辑器定制 热度 9529 2015-9-4 18:50 |个人分类:Unity3d| 编辑器, 拓展 我相信无数初学者看别人游戏都经常看到他们的Inspector中 ...
- spring mvc 自定义编辑器
起始知识: Java标准的PropertyEditor的核心功能是将一个字符串转换为一个Java对象,以便根据界面的输入或配置文件中的配置字符串构造出一个JVM内部的java对象. 如何注册自定义的属 ...
- 关于Unity3D自定义编辑器的学习
被人物编辑器折腾了一个月,最终还是交了点成品上去(还要很多优化都还么做). 刚接手这项工作时觉得没概念,没想法,不知道.后来就去看<<Unity5.X从入门到精通>>中有关于 ...
- Web Essentials之Markdown和自定义编辑器(Web Essentials完结)
返回Web Essentials功能目录 本篇目录 功能 自定义编辑器 开源项目都会在项目的根目录放一个Readme.md文件来告诉读者一些重要的说明,那么就可以在VS中直接编辑Markdown文件. ...
- Unity正式发布首个“实验性”VR编辑器,支持HTC Vive和Oculus Rift
Unity今天正式推出"实验性"VR编辑器.据悉,EditorVR是Unity游戏引擎中的一个组件,可让开发者在虚拟现实环境中开发游戏.为何要称之为"实验性"? ...
- PowerDesigner(九)-模型文档编辑器(生成项目文档)(转)
模型文档编辑器 PowerDesigner的模型文档(Model Report)是基于模型的,面向项目的概览文档,提供了灵活,丰富的模型文档编辑界面,实现了设计,修改和输出模型文档的全过程. 模型文 ...
随机推荐
- D3.js 让图表动起来
D3 支持制作动态的图表.有时候,图表的变化需要缓慢的发生,以便于让用户看清楚变化的过程,也能给用户不小的友好感. 一.什么是动态效果 绘制完成后不再发生变化的,这是静态的图表. 动态的图表,是指图表 ...
- x265
1.编译库 https://bitbucket.org/multicoreware/x265/src/tip/build/README.txt?at=default 2.无法定位程序输入点x265_e ...
- mysql:恢复mysql表结构
mysql,frm格式恢复mysql表结构,以tuser.frm格式为例 新增数据库,如下,创建数据库名为ab 打开数据库,双击打开数据库 点右键新建表结构 新增表,里面只添加一个字段 ...
- 2016年GitHub排名前20的Python机器学习开源项目(转)
当今时代,开源是创新和技术快速发展的核心.本文来自 KDnuggets 的年度盘点,介绍了 2016 年排名前 20 的 Python 机器学习开源项目,在介绍的同时也会做一些有趣的分析以及谈一谈它们 ...
- 多路由器环境下路由器的入口IP地址及DHCP设置探讨
多路由器环境下路由器的入口IP地址及DHCP设置探讨 这里把路由器的LAN口管理IP地址称为路由器的入口地址,把直接接入互联网的路由器称为主路由器,其他路由器称为从路由器.在多路由器环境下路由器的设置 ...
- WebDriver 页面等待
selenium2.4.0版本提供了页面等待处理. 显示等待元素可见: protected void WaitElementVisible(By by,int timeOutInSeconds, lo ...
- DBUtils学习
1. DBUtils是JDBC的简单封装,可以和JDBC混合使用. 2. DBUtils对结果集自动封装为JavaBean是有着苛刻要求的:必须满足JavaBean的规范,其次 ...
- Android Snackbar
使用Snackbar我们可以在屏幕底部(大多时候)快速弹出消息,它和Toast非常相似,但是它更灵活一些. 当它显示一段时间后或用户与屏幕交互时它会自动消失. 可以自定义action-可选操作. sw ...
- Qt之QSizePolicy
简述 QSizePolicy类是一个描述布局水平和垂直方向调整策略的属性. 大小策略会影响布局引擎处理部件的方式,部件加入布局以后,会返回一个QSizePolicy,描述了其水平和垂直方向的大小策略. ...
- Android 编程下的代码混淆
什么是代码混淆 Java 是一种跨平台的.解释型语言,Java 源代码编译成中间”字节码”存储于 class 文件中.由于跨平台的需要,Java 字节码中包括了很多源代码信息,如变量名.方法名,并且通 ...