Unity编辑器的源码 例子:查找ManagedStaticReference()
静态引用 在Profiler里面,有的时候会看到某个资源一直无法GC,其原因可能是被static静态引用了。
首先在Assets里面新建Resources文件夹,在里面放一个hierarchy的游戏对象,命名为“prefab”,然后在Scripts文件夹里新建一个名为HardReferenceResourse
脚本并挂载在场景中的某个物体上
1 2 3 4 5 6 7 8 9 10 using UnityEngine;public class HardReferenceResourse : MonoBehaviour { public static GameObject prefab; private void Start () { prefab = Resources.Load<GameObject>("prefab" ); } }
运行游戏后 ,使用下面的脚本在Unity中使用反射来递归查询这个资源被代码哪里static强引用了。
在Editor文件夹中新建CheckoutWhichStatic
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEditor;using System.Reflection;using System;using System.Text;public class CheckoutWhichStatic : Editor { [MenuItem("Root/Report/脚本Static引用" ) ] static void StaticRef () { LoadAssembly("Assembly-CSharp-firstpass" ); LoadAssembly("Assembly-CSharp" ); } static void LoadAssembly (string name ) { Assembly assembly = null ; try { assembly = Assembly.Load(name); } catch (Exception ex) { Debug.LogWarning(ex.Message); } finally { if (assembly != null ) { foreach (Type type in assembly.GetTypes()) { try { HashSet<string > assetPaths = new HashSet<string >(); FieldInfo[] listFieldInfo = type.GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); foreach (FieldInfo fieldInfo in listFieldInfo) { if (!fieldInfo.FieldType.IsValueType) { SearchProperties(fieldInfo.GetValue(null ), assetPaths); } } if (assetPaths.Count > 0 ) { StringBuilder sb = new StringBuilder(); sb.AppendFormat("{0}.cs\n" , type.ToString()); foreach (string path in assetPaths) { sb.AppendFormat("\t{0}\n" , path); } Debug.Log(sb.ToString()); } } catch (Exception ex) { Debug.LogWarning(ex.Message); } } } } } static HashSet<string > SearchProperties (object obj,HashSet<string > assetPaths ) { if (obj != null ) { if (obj is UnityEngine.Object) { UnityEngine.Object[] depen = EditorUtility.CollectDependencies(new UnityEngine.Object[] { obj as UnityEngine.Object }); foreach (var item in depen) { string assetPath = AssetDatabase.GetAssetPath(item); if (!string .IsNullOrEmpty(assetPath)) { assetPaths.Add(assetPath); } } } else if (obj is IEnumerable) { foreach (object child in (obj as IEnumerable )) { SearchProperties(child, assetPaths); } } else if (obj is System.Object) { if (!obj.GetType().IsValueType) { FieldInfo[] fieldInfos = obj.GetType().GetFields(); foreach (FieldInfo fieldInfo in fieldInfos) { object o = fieldInfo.GetValue(obj); if (o != obj) { SearchProperties(fieldInfo.GetValue(obj), assetPaths); } } } } } return assetPaths; } }
在开发中,一定要避免A引用B,B引用C,C引用A这种情况 ,使用上文的方式检测不出这种情况
UIElements 这一 部分现在已经整合到UI Toolkit 里面,今后单独学习整理
查询系统窗口 使用以下脚本来找到Unity Editor某些窗口的源码位置,方便自定义窗口时阅读引用这些窗口(结合前面的Unity源码阅读工具)
演示代码
1 2 3 4 5 6 7 8 [MenuItem("Root/GetEditorWindow" ) ] static void GetEditorWindow (){ foreach (var item in Resources.FindObjectOfTypeAll<EditorWindow>()) { Debug.Log(item.GetType().ToString()); } }
比如在Unity运行游戏时,打开Profiler窗口,会在Console窗口中弹出“UnityEditor.ProfilerWindow”我们就能知道这个窗口的代码是写在哪里的
自定义资源导入类型 Unity可以自动识别大部分的文件,不过我们可以手动创建自己的文件类型比如说.atao
、.atao2017
等,首先我们先新建一个文件,键入以下内容并把后缀改为.atao
,这本质上属于Json文件,我们在Unity中使用JSON的方式读取它
在Editor文件夹中新建CustomFileImporter
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 using UnityEngine;using UnityEditor.AssetImporters;using System.IO;[ScriptedImporter(1, "atao" ) ] public class CustomFileImporter : ScriptedImporter { public override void OnImportAsset (AssetImportContext ctx ) { var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); var position = JsonUtility.FromJson<Vector3>(File.ReadAllText(ctx.assetPath)); cube.transform.position = position; cube.transform.localScale = Vector3.one; ctx.AddObjectToAsset("obj" , cube); ctx.SetMainObject(cube); var material = new Material(Shader.Find("Standard" )); material.color = Color.red; ctx.AddObjectToAsset("material" , material); var tempMesh = new Mesh(); DestroyImmediate(tempMesh); } }
将自己的.atao
文件放进Unity里面,