unity编辑器扩展#3 《Extending Unity with Editor Scripting 》笔记

本文深入探讨Unity编辑器的高级应用,包括场景管理、自定义Gizmos、Inspector、Editor Windows、Scene GUI、GUI样式与皮肤、Scriptable Objects及资源导入处理等。详细解析各功能的使用方法和注意事项,助你提升Unity开发效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

#1 编辑器创建新场景

EditorApplication.SaveCurrentSceneIfUserWantsTo();
//打开是否保存场景的对话框
EditorApplication.NewScene();
//创建新场景,方法以废弃=> EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);
//Editor里摧毁物体用DestroyImmediate不要用Destory;
GameObject.DestroyImmediate(gameObject);

#2 Gizmos In Scene

脚本内设置

public class GizmoExample : MonoBehaviour {
 //和Start()等一样自动调用
 private void OnDrawGizmos() {GUI...} //常态时显示Gizmos
 private void OnDrawGizmosSelected(){GUI...}//被选中时显示的Gizmos,不会覆盖前者,两者同时渲染
}

脚本外设置(Editor文件夹内)

[DrawGizmo(
	GizmoType.InSelectionHierarchy//自己被选中或者父级被选中时调用
	|GizmoType.NotInSelectionHierarchy//自己和父级都没被选中时调用
	|GizmoType.Selected//自己被选中时调用
	|GizmoType.NonSelected//自己没被选中时调用
	|GizmoType.Active//激活状态时调用
	|GizmoType.Pickable//可以通过点击Gizmos选择物体
	)]
	private static void Gizmos4ExampleNoSelected(GizmoExample target, GizmoType type)
	{
	...等同于OnDrawGizmos
	}

	[DrawGizmo(GizmoType.InSelectionHierarchy|GizmoType.Pickable)]
	private static void Gizmos4ExampleOnSelected(GizmoExample target, GizmoType type)
	{
        ...等同于OnDrawGizmosSelected
	}

自己写一个静态方法 方法的第一个参数类型填你想要让该方法Gizmos作用的目标类型,第二个是GizmoType类型的参数的参数,

添加特性[DrawGizmo(GizmoType gizmo)]  GizmoType类型参数gizmo指定调用的时机 。

Gizmos类

Gizmos.color;
//Gizmos的颜色,最好在换色前保存原有原色,Gizmos写完后reset原来的颜色,避免影响别的Gizmos 
//如下所示-------------------
Color oldCol=Gizmos.color;
Gizmos.color=Color.red;
...end
Gizmos.color=oldCol
//--------------------------


Gizmos.matrix;
//Gizmos的4x4矩阵 应该存了Gizmos参考的位移 旋转 大小 默认应该是原点 0旋转 1缩放(后两个猜的) 
//比如为了方便设置,你想设Gizmos作用物体的Transform作为参考的原点 同样要记得reset
//如下设置-------------------
Matrix4x4 oldMatrix = Gizmos.matrix;
Gizmos.matrix = transform.localToWorldMatrix;
...end
Gizmos.matrix = oldMatrix;
//--------------------------


Gizmos.Draw(Wire)Cube/Line/ray/Mesh/Sphere/Frustrum();
//具体可以绘制的各种Gizmos 实体/线框 的方块,线、射线、自定义mesh,球 、视锥体(camera的那个)

#3 Custom Inspectors

继承Editor类

[CustomEditor(typeof(targetType))]

[CustomEditor(typeof(targetType))]
 public class targetTypeEditor : Editor {

    private void OnEnable () {//启用时调用...}
 
    private void OnDisable () {//关闭时调用...}

    private void OnDestroy () {//摧毁时调用...}

    public override void OnInspectorGUI () {
        //...绘制Inspector面板
        DrawDefaultInspector();//默认显示
        targetType t=(targetTyp)target;//target来自Editor基类,表示目标实例
    }

}
//打开一个Unity对话框,
bool a=EditorUtility.DisplayDialog("对话框标题","对话框内容","Yes","No");


bool oldEnable=GUI.enabled;
GUI.enabled=flase;
//...enabled设为false,下面的GUI控件就无法交互和使用
GUI.enabled=oldEnable;

//GUI有变化就调用SetDirty,把Target标记为dirty,告知unity保存数据
if(GUI.changed) {
 EditorUtility.SetDirty(_myTarget);
}

Property Drawers&DecoratorDrawer

 [CustomPropertyDrawer (typeof(TypeName))]
 public class TypeDrawer : PropertyDrawer {//这里也可以选择继承DecoratorDrawer,装饰类型的编辑

     public override void OnGUI (Rect position, SerializedProperty
        property, GUIContent label) {
        //这里只能用GUI和EditorGUI,不能用GUILayout和EditorGUILayout

     }
 }

所针对的Type即可以是普通的类也可以是特性类,如果是特性类,要继承PropertyAttribute

在OnGUI里写控件布局,要注意这里只能写GUI和EditorGUI中的方法,不能用带自动布局的Layout

DecoratorDrawer:

DecoratorDrawer 和Property Drawers类似,但是它不是绘制一个属性,而是单纯的根据从PropertyAttribute中得到的数据绘制用于装饰的GUI元素。(从类的成员里看,DecoratorDrawer 只有atttibute 而PropertyDrawer还有fieldInfo

用DecoratorDrawer的特性可以叠加多个在同一个字段上。

如果是放在List 数组之类的的类型上,一个特性总共只会调用一次,不会每个数组元素都调用。

如何让自定义特性作用于自定义的Inspector里也能生效:

在OnInspectorGUI()之类的绘制方法里,我们如果直接取值,就会失去值附带的特性,为了保存附带特性我们用到SerializedObject SerializedProperty EditorGUILayout.PropertyField() 

private SerializedObject _mySerializedObject;
private SerializedProperty _serializedTotalTime;

private void InitSerialized () {
      //用target生成一个SerializedObject对象
     _mySerializedObject = new SerializedObject (target);
      //用FindProperty ("name");找到你要用的值或属性 SerializedProperty可以保留特性相关信息
     _serializedTotalTime = _mySerializedObject.FindProperty ("_totalTime");
}

private void OnInspectorGUI() {
 ...
 //这样就会显示指定特性所实现的GUI
 EditorGUILayout.PropertyField (_serializedTotalTime);
 ...
}
Unity内置编辑器特性

----Property Drawers---------------------

[Range (0, 1)]
public float floatRange = 0.5f;

[Multiline (2)]
public string stringMultiline = "This text is using a multiline
property drawer";

[TextArea (2, 4)]
public string stringTextArea = "This text \nis using \na text area \
nproperty \ndrawer";

[ContextMenu ("Do Something")]//..点Inspector右边的齿轮会多这个选项
 public void DoSomething() {
 Debug.Log ("DoSomething was called...");
 } 

[ContextMenuItem("Reset this value", "Reset")]//给指定的控件提供一个方法 和上面类似
public int intReset = 100;
public void Reset() {intReset = 0;}

----Property Drawers---------------------


----Decorator Drawers---------------------

[Header("This is a group of variables")]
public int varA = 10;

public int varC = 10;
[Space(40)]//上下隔空格
public int varD = 20;

[Tooltip("This is a tooltip")]//鼠标移在上面的提示框
public int varE = 30;

----Decorator Drawers---------------------

#4 Editor Windows

public class MyWindow : EditorWindow {
 public static MyWindow instance;

 //打开窗口的静态方法
 public static void OpenMyWindow () {
     //EditorWindow.GetWindow()创建一个窗口
     instance = (PaletteWindow) EditorWindow.GetWindow(typeof(PaletteWindow));
     instance.titleContent = new GUIContent("Palette");
 }
 private void OnEnable() {}

 private void OnDisable() {}

 private void OnDestroy() {}

 private void OnGUI() {}

 private void Update () {}

}

 面板选项的快捷键设置

在路径设置后 空格( )+前缀(_)+快捷键 如下面设置快捷键为 shift+p

 [MenuItem(" xxxx/xxx/xx _#p") ]

[MenuItem ("Tools/Level Creator/Show Palette _#p")]
private static void ShowPalette () {
 PaletteWindow.ShowPalette ();
}

特殊键位表示符号

stringkey
%Ctrl on Windows / Command on OSX
#Shift
&Alt
LEFT/RIGHT/UP/DOWNArrow keys
F1…F2F keys
HOME, END, PGUP, PGDNHome, End, Page Up, Page Down

搜寻指定资源,第一个参数是筛选的字符串,具体规则看下面,第二个是要搜索资源的文件路径,可以同时搜寻多个路径中的文件,返回值是符合要求的guid(全局唯一标识符 global unique identifier)

string[] guids = AssetDatabase.FindAssets ("t:Prefab", new string[] {path});

//得到guid后在找资源路径,再通过路径找到具体的资源

assetPath = AssetDatabase.GUIDToAssetPath (guids [i]);

asset = AssetDatabase.LoadAssetAtPath (assetPath, typeof(GameObject)) as GameObject;

FindAssets的筛选规则:可以用三种筛选方式,且可以混合用

1、通过资源名称查找,全名或部分名称度可以 比如 找一个叫“test”的资源,可以写"Test",也可以写"Te"
2、通过资源的标签(label)查找,查找的标签名前加"l:"来区分

3、通过资源的类型查找,查找的类型名前加"t:"来区分 

比如"player l:img t:Texure2D"表示搜寻Texture2D类型 label为img 名称里带有player的资源

注意:查找不分大小写,多个条件用空格分开

AssetPreview.GetAssetPreview(Object asset)得到资源的预览图片,返回Texture2D

#5 自定义Scene界面

用的是Ediotr中的中的OnSceneGUI,也就是说,当你选中目标物体,对应的SceneGUI就会被调用,绘制GUI元素到Scene视窗上

[CustomEditor(typeof(targetType))]
 public class targetTypeEditor : Editor {
    ...
    public override void OnSceneGUI () {
            
            //因为是在OnSceneGUI上调用,所以最好用这个包裹GUI
            //The start of a GUILayout sequence of function calls. As expected the EndGUI is used to close these.
            Handles.BeginGUI();

            //确定绘制的范围,不然默认整个屏幕了
            GUILayout.BeginArea(new Rect(10,10,360,30));

            //GUIlayout代码...

            GUILayout.EndArea();

            Handles.EndGUI();

            Repaint();//重新绘制,在GUI变化是可以尝试调用来更新显示
    }
}

除了一些属性字段 Handles类里还有有unity提供的各种Handle(?操控杆)

像Handles.PositionHandle就是我们移动物体时跟坐标轴一样的三个箭头 

 通过SceneView.currentDrawingSceneView 可以得到SceneView的相关信息

比如SceneView.currentDrawingSceneView.in2DMode = true;就是开启

另外SceneView.currentDrawingSceneView.camera可以得到渲染sceneView的相机,要注意这个相机不是场景中放置的相机

 通过Tools.current 得到的相关信息 

 将在SceneView中输入的控制权从Unity默认处理转到自己处理,避免点击其他地方导致物体失去焦点,GUI消失

HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));

 

Event.current 里可以得到各种键盘鼠标的输入事件

Event.current.mousePosition得到鼠标位置是基于左上的 如果想要得到基于左下,可以这样转化一下

Vector2 mousePos = Event.current.mousePosition;
mousePos=new Vector2(mousePos.x,camera.pixelHeight-mousePos.y);

 

通过InstantiatePrefab生成对象,可以让对象是预制体(物体引用的prefab改变,物体自身也会改变)

GameObject go = PrefabUtility.InstantiatePrefab(_selectedPieces.gameObject)as GameObject;

为InspectorScript生成默认的GUI  懒得为某个引用的类特意绘制自定义的GUI,可以用这个方法

Editor.CreateEditor(InspectorScript).OnInspectorGUI();

hiding flags

每个Object都有,有下面这些选择

• None: 默认

• HideInHierarchy: 物体在Hierarchy面板隐藏

• HideInInspector: 物体在Inspector面板隐藏

• NotEditable: 物体在inspector无法编辑(变灰,不可交互)

• DontSaveInEditor: It is not saved to the scene in the editor.

• DontSaveInBuild: It is not saved when a player is built.

• DontUnloadUnusedAsset: It is not unloaded by Resources. UnloadUnusedAssets.

• DontSave: It is not saved to the scene. It will not be destroyed when a new scene is loaded. It is a shortcut for HideFlags.DontSaveInBuild | HideFlags. DontSaveInEditor | HideFlags.DontUnloadUnusedAsset.

• HideAndDontSave: It is a combination of not shown in the hierarchy, not saved to scenes, and not unloaded by the object; it will not be unloaded by Resources.UnloadUnusedAssets.

#6 GUI Styles &GUI Skins 

每个GUI控件方法,基本最后都会有一个GUIStyle的选项,用来决定控件的整体显示状况

你可以用它设置控件的字体、高宽、背景图切割方式border(像边框类的九宫格式切割)等等,此外它共有8个状态,你可以设置不同状态(GUIStyleState)时显示的背景图片、字体颜色等

注:用于GUI的图片,要把它的格式变为Editor GUI and Legacy GUI 

共8个状态 normal hover active onNormal onHover onActive focused onFocused

 GUISkin

GUISkin就是聚和一套GUIStyle的可序列化资源,在Project里右键菜单的Create的菜单里就可以直接创建

 这样就不用代码来一个个给不同类型的控件创建不同的GUIStyle了,而且因为是一种序列化资源可以在不同的项目里重复使用

#7 Scriptable Objects

继承自:: ScriptableObject

[Serializable]
public class LevelSeting:ScriptableObject
{
    public float gravity;
    public AudioClip bgm;
    public Sprite background;
}

public static T CreateAsset<T>(string path) where T:ScriptableObject
{
    //创建实例
    T asset = ScriptableObject.CreateInstance<T>();
    //保存至Asset
    AssetDatabase.CreateAsset(asset,path);
    AssetDatabase.Refresh();
    AssetDatabase.SaveAssets();
    return asset;
}

[MenuItem("Tools/Level Creator/NewLevelSeting")]
static void CreateLevelSeting()
{
    //打开一个选择存储路径的窗口
    string path = EditorUtility.SaveFilePanelInProject("NewLevelSetingAsset", "LevelSetings", "asset",
"Define the name for the LevelSettings asset");

    if (path!=null)
    {
        EditorUtils.CreateAsset<LevelSeting>(path);
    }
}

#8 资源导入处理

实现资源导入预处理类应该继承:AssetPostprocessor

下面是一个例子

public class TexturePipeline:AssetPostprocessor
    {
        //Texture资源导入时调用
        private void OnPreprocessTexture()
        {
            //如果资源导入到该文件夹下,就会返回true
            if (assetPath.StartsWith("Assets/Art/Bg"))
            {
                ImportBackground();
            }
        }
    
        //Texture资源导入完成后调用
        private void OnPostprocessTexture(Texture2D texture)
        {
        
        }
    
        void ImportBackground()
        {
            Debug.Log("Loading Background...");
            TextureImporter importer = assetImporter as TextureImporter;
            //这里就是对导入的图片的一些格式信息进行修改
            importer.textureType = TextureImporterType.Sprite;
            TextureImporterSettings importSeting=new TextureImporterSettings();
            importer.ReadTextureSettings(importSeting);
            importSeting.spriteAlignment = (int)SpriteAlignment.BottomLeft;
            importSeting.mipmapEnabled = false;
            importer.SetTextureSettings(importSeting);
        }
    }

上面只是texture资源的导入调用的方法,还有很多其他的方法可以用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值