持续更新中。。
复合元素 要显示在面板使用[serializable]
比如一个类 就是这么简单,但加了之后就不能为Null了
[Serializable]
public class TestClass
{
public enum TestType
{
a,
b,
}
public TestType testType;
public bool testBool = false;
public float testFloat = 1;
}
如果要重绘这个类...使用CustomPropertyDrawer,
[CustomPropertyDrawer(typeof(UScript))]
public class UScriptDrawer : PropertyDrawer
{
public override void OnGUI(Rect pos, SerializedProperty property, GUIContent label)
{
var targetObj = property.serializedObject.targetObject;
var asset = targetObj as 持有他的对象类型(如Asset);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
//如有需要返回Height
}
}
用代码重新绘制了Inspector,并赋值,所以注意不能使用Ctrl+Z,要做Undo?...
如加个按钮
[CustomEditor(typeof(UrScript))]
public class UrScriptEditor : Editor
{
UrScript script;
private void OnEnable()
{
script = (UrScript)target;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
EditorGUI.BeginChangeCheck();
if (script.boolValue)
{
EditorGUI.indentLevel++;
EditorGUILayout.LabelField("Label面板提示1");
EditorGUILayout.HelpBox("HelpBox面板提示2", MessageType.Warning);
EditorGUILayout.PropertyField(任意变量);
EditorGUI.indentLevel--;
}
else
{
EditorGUILayout.LabelField("面板提示");
数组变量.arraySize = EditorGUILayout.IntField("Length", uscript.arraySize);
for (int i = 0; i < 数组变量.arraySize; i++)
EditorGUILayout.PropertyField(数组变量.GetArrayElementAtIndex(i));
}
if (GUILayout.Button("创建对象"))
{
TestScript myScript = (TestScript)target;
Debug.LogError(myScript.self.gameObject.GetComponent());
}
if (EditorGUI.EndChangeCheck())
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
serializedObject.ApplyModifiedProperties();
}
}
Editor newEditor;
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Prop, new GUIContent("套娃对象"));
if (EditorGUI.EndChangeCheck() || assetEditor == null)
{
newEditor= CreateEditor(m_Prop.objectReferenceValue);
}
newEditor.OnInspectorGUI();
serializedObject.ApplyModifiedProperties();
}
选择层
FindProperty("xx1").FindPropertyRelative("xx2").intValue = UnityEditorInternal.InternalEditorUtility.ConcatenatedLayersMaskToLayerMask(EditorGUILayout.MaskField("", UnityEditorInternal.InternalEditorUtility.LayerMaskToConcatenatedLayersMask(xx2), UnityEditorInternal.InternalEditorUtility.layers));
发现了自定义更简洁的方法 参考Odin插件,
新增LabelText标签。可以放到属性上,显示出中文
public class LabelTextAttribute:PropertyAttribute
{
public string label;
public string showCondition;
public string editorCondition;
public LabelTextAttribute(string label, string showCondition = null, string editorCondition = null)
{
this.label = label;
this.showCondition = showCondition;
this.editorCondition = editorCondition;
}
}
还可以针对一些非Unity类
using UnityEditor;
using UnityEngine;
[CustomPropertyDrawer(typeof(LabelTextAttribute),false)]
public class LabelDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var attr = attribute as LabelTextAttribute;
label.text = attr.label;
// 如果字段为false则不显示
if (!string.IsNullOrEmpty(attr.showCondition))
{
var field = property.serializedObject.FindProperty(attr.showCondition);
if (!field.boolValue)
return;
}
// 是否能编辑
var notEdit = false;
if (!string.IsNullOrEmpty(attr.editorCondition))
{
if (attr.editorCondition == "false")
notEdit = true;
}
if (notEdit)
GUI.enabled = false;
EditorGUI.PropertyField(position, property, label);
if (notEdit)
GUI.enabled = true;
}
//如果不显示返回没间隔
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
LabelTextAttribute labelAttr = (LabelTextAttribute)attribute;
var showCondition = property.serializedObject.FindProperty(labelAttr.showCondition);
if (showCondition != null)
{
bool show = showCondition.boolValue;
if (!show)
return -EditorGUIUtility.standardVerticalSpacing;
}
return EditorGUI.GetPropertyHeight(property, label);
}
}
场景:[CustomEditor(typeof(UnityEditor.SceneAsset))]
txt/byte : TextAsset
模型 : ?
文件夹和一些unity不支持的文件类型:[CustomEditor(typeof(UnityEditor.DefaultAsset))]
public override void OnInspectorGUI()
{
string path = AssetDatabase.GetAssetPath(target);
if (path.EndsWith(".bytes") && path.Contains("Animations"))
{
using (BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open)))
{
int length = reader.ReadInt32();
EditorGUILayout.LabelField("长度:" + length);
for (int i = 0; i < length; i++)
{
EditorGUILayout.LabelField("X:" + (reader.ReadInt32() / 10000f).ToString() +
" Y:" + (reader.ReadInt32() / 10000f).ToString() +
" Z:" + (reader.ReadInt32() / 10000f).ToString());
if (i != 0 && (i + 1) % 10 == 0)
EditorGUILayout.LabelField("----" + (i + 1) + "----");
}
}
}
else
base.OnInspectorGUI();
}
各种GUI:
//继承IHasCustomMenu
public void AddItemsToMenu(GenericMenu menu) {
menu.AddItem(new GUIContent("A"), false, () => { Debug.Log("A"); });
}
GUIStyle HelpStyle = new GUIStyle("IconButton");
HelpStyle.normal.background = EditorGUIUtility.FindTexture("d__Help");
void ShowButton(Rect position)
{
if (lockButtonStyle == null)
lockButtonStyle = new GUIStyle();
lockButtonStyle.normal.background = EditorGUIUtility.FindTexture("d__Help");
if (GUI.Button(position, GUIContent.none, lockButtonStyle))
{
}
}
Scene Mode | Prefab Mode | |
Hierarchy 里操作的对象本质 | Prefab Instance 实例 | Assets里的UnityEngine.Object文件 |
Apply 保存过程 | 从Instance写入到Asset里的Prefab文件 | 直接操作Assets里的Prefab文件 |
SetDirty 脚本修改Prefab上的对象 | EditorUtility.SetDirty(Object) | 暂无(目前U3D问答上很多人在问) |
设置场景Dirty | EditorSceneManager.GetActiveScene() | PrefabStageUtility.GetCurrentPrefabStage().scene) |
Apply时的回调 | PrefabUtility.prefabInstanceUpdated | PrefabStage.prefabSaving |
Object source = PrefabUtility.GetCorrespondingObjectFromSource (TObject componentOrGameObject);
string path = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(source);
Prefab Mode string path = UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage().prefabAssetPath;
string assetPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
组件(例如Aniamtor) AssetDatabase.GetAssetPath(Object);
static void MakePrefab()
{
string selectPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
var files = Directory.GetFiles(selectPath, "*.prefab", SearchOption.AllDirectories);
foreach (var path in files)
{
GameObject prefab = AssetDatabase.LoadAssetAtPath(path);
GameObject delect = prefab.transform.Find("delect");
if (delect != null)
{
GameObject.DestroyImmediate(delect, true);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
}
问题:使用Asset如果删除一些组件要使用DestroyImmediate,当目标是一个ParticleSystem时,Material不会一同被删除,使用实例化解决。
prefab实例化不断链
首先实例化要用↓
GameObject prefab = AssetDatabase.LoadAssetAtPath(path);
GameObject go = PrefabUtility.InstantiatePrefab(prefab);//如果使用Instantiate会丢失链接 因为算是clone
判断是否有Missing代码
go.GetComponentsInChildren
保存预制
bool succe = PrefabUtility.SaveAsPrefabAsset(inst, prefabPath);
这个时候想要进行一些删除或者SetParent操作,会提示不允许。
要断链root,使用PrefabUtility.UnpackPrefabInstanceAndReturnNewOutermostRoots(go, PrefabUnpackMode.OutermostRoot);
using UnityEditor;
using UnityEngine;
public class ApplyPrefab
{
[InitializeOnLoadMethod]
static void StartInitializeOnLoadMethod()
{
PrefabUtility.prefabInstanceUpdated = delegate (GameObject instance)
{
//prefab保存的路径
Debug.Log(PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(instance));
};
}
}
[UnityEditor.MenuItem("Tools/Apply Selected Prefabs")]
static void ApplySelectedPrefabs(){
//获取选中的gameobject对象
GameObject [] selectedsGameobject= Selection.gameObjects;
GameObject prefab = PrefabUtility.FindPrefabRoot (selectedsGameobject[0]);
for (int i = 0; i < selectedsGameobject.Length; i++) {
GameObject obj=selectedsGameobject[i];
UnityEngine.Object newsPref= PrefabUtility.GetPrefabObject(obj);
//判断选择的物体,是否为预设
if(PrefabUtility.GetPrefabType(obj) == PrefabType.PrefabInstance){
UnityEngine.Object parentObject = PrefabUtility.GetPrefabParent(obj);
//string path = AssetDatabase.GetAssetPath(parentObject);//获取路径
PrefabUtility.ReplacePrefab(obj,parentObject,ReplacePrefabOptions.ConnectToPrefab);//替换预设
AssetDatabase.Refresh();//刷新
}
}
}
static void Scan()
{
ScanFolder(EditorUtil.GetSelectedFilePath());
}
static void ScanFolder(path)
{
DirectoryInfo folder = new DirectoryInfo(path);
FileSystemInfo[] files = folder.GetFileSystemInfos();
for(int i = 0;i(path);
}
}
[InitializeOnLoad]
internal class PrefabExtension
{
static PrefabExtension()
{
PrefabUtility.prefabInstanceUpdated += OnPrefabInstanceUpdate;
}
static void OnPrefabInstanceUpdate(GameObject instance)
{
//Debug.LogError("[Callback] Prefab.Apply on instance named :" + instance.name);
EditorApplication.delayCall += delegate ()
{
var prefab = PrefabUtility.GetCorrespondingObjectFromSource(instance);
if (prefab == null)
return;
var tPath = AssetDatabase.GetAssetPath(prefab);
//Do Something
};
}
}
public class Example : AssetPostprocessor
{
void OnPostprocessPrefab(GameObject g)
{
Debug.LogError(g.name);
}
[InitializeOnLoadMethod]
static void StartInitializeOnLoadMethod()
{
PrefabUtility.prefabInstanceUpdated = delegate (GameObject instance)
{
//prefab保存的路径
var sourrce = PrefabUtility.GetCorrespondingObjectFromSource(instance);
var path = AssetDatabase.GetAssetPath(sourrce);
if (PrefabUtility.IsAnyPrefabInstanceRoot(instance))
PrefabUtility.UnpackPrefabInstance(instance, PrefabUnpackMode.OutermostRoot, InteractionMode.AutomatedAction);
//do something
};
}
}
[InitializeOnLoad]
public class PrefabSaveListener : UnityEditor.AssetModificationProcessor
{
private static string[] OnWillSaveAssets(string[] paths)
{
// 在这里可以添加一些逻辑代码,比如检查预制的属性或组件等
foreach (string path in paths)
{
// Example: 更新预制体的某个属性
var asset = AssetDatabase.LoadAssetAtPath(path);
if (asset != null)
{
// 修改预制体的属性
}
}
return paths;
}
}
需要Rect、带输入、有默认间隔
不需要Rect、自动排布、不带输入、所以没有很大间隔、自定义间隔
一般结合BeginArea、BeginScrollView、BeginHorizontal、BeginVertical使用
https://github.com/756915370/UnityEditorExtension
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.EndVertical();
菜单添加与快捷键
快捷键api参考:Unity - Scripting API: MenuItem
public class testTools
{
[MenuItem("Tools/Hello %#a")]
static void Hello()
{
Debug.Log("hello");
}
[MenuItem("Assets/Hello")] //如果放在Assets下右键文件夹也会出现
static void Hello2()
{
Debug.Log("hello2");
}
}
快捷键
% - CTRL
# - Shift
& - Alt
LEFT/RIGHT/UP/DOWN - 光标键
F1…F12
HOME,END,PGUP,PDDN
Example
public static void ClearConsole()
{
Assembly assembly = Assembly.GetAssembly(typeof(SceneView));
Type logEntries = assembly.GetType("UnityEditor.LogEntries");
MethodInfo clearConsoleMethod = logEntries.GetMethod("Clear");
clearConsoleMethod.Invoke(new object(), null);
}
复制粘贴
//复制
GUIUtility.systemCopyBuffer = "test";
//粘贴 或者 Ctrl+V
string str = GUIUtility.systemCopyBuffer;
public class SaveScene :UnityEditor.AssetModificationProcessor {
static public void OnWillSaveAssets(string[]names)
{
foreach (string name in names)
{
if (name.EndsWith (".unity")) {
Scene scene = SceneManager.GetSceneByPath (name);
Debug.Log ("保存场景 :" + scene.name);
}
}
}
}
SceneView.lastActiveSceneView.Repaint();
editor 每秒/改变时绘制
在Scene预览模型 例子:
GameObject preview;
void OnDrawGizmos()
{
if(!Application.isPlaying)
{
if(preview == null)
Resources.Load("path");
else
{
MeshFilter[] meshFilters = mPreview.GetComponentsInChildren();
for(int i = 0; i < meshFilters.Length; i++)
{
Gizmos.DrawMesh(meshFilter[i].sharedMesh,
transform.position + mPreview.transform.GetChild(i).position * scale,
mPreview.transform.GetChild(i).rotation,
transform.localScale)
}
}
}
}
using UnityEngine;
[CreateAssetMenu(fileName = "FileName", menuName = "ScriptableObjects/menuName", order = 1)]
public class YourScriptObject : ScriptableObject
{
[Header("大小")]
public int Size = 16;
}
反射调用
public class ReflectionTools
{
public static void Call(string typeName, string methodName, params object[] args)
{
Call
还可以把asmdef命名为内部internal 例如Unity.InternalAPIEditorBridge.001
这样可以直接访问
InternalsVisibleToAttribute Class (System.Runtime.CompilerServices) | Microsoft Learn