当前位置: 代码迷 >> 综合 >> unity编辑器扩展#3 《Extending Unity with Editor Scripting 》笔记
  详细解决方案

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

热度:15   发布时间:2023-12-14 21:33:00.0

 

#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...} //常态时显示Gizmosprivate 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, SerializedPropertyproperty, 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() {...//这样就会显示指定特性所实现的GUIEditorGUILayout.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 ();
}

特殊键位表示符号

string key
% Ctrl on Windows / Command on OSX
# Shift
& Alt
LEFT/RIGHT/UP/DOWN Arrow keys
F1…F2 F keys
HOME, END, PGUP, PGDN Home, 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>();//保存至AssetAssetDatabase.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(){//如果资源导入到该文件夹下,就会返回trueif (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资源的导入调用的方法,还有很多其他的方法可以用。

  相关解决方案