using UnityEngine; #if UNITY_EDITOR using UnityEditor; using UnityEngine.UI; using UnityEngine.Rendering; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; #endif public class MissingSearcher : MonoBehaviour { #if UNITY_EDITOR private Stopwatch stopwatch = new Stopwatch(); private GameObject[] allObjectArray; private int allObjectIndex = 0; private List cameraList = new List(); void Start(){ ResetAllObjectArray(); } void Update(){ stopwatch.Start(); try{ while(stopwatch.ElapsedMilliseconds < 8){ // およそ 1/120 UpdateSearch(); } }catch (System.Exception e){ UnityEngine.Debug.Log("MissingSearcher Update " + e); ResetAllObjectArray(); } stopwatch.Stop(); stopwatch.Reset(); } private void ResetAllObjectArray(){ allObjectIndex = 0; allObjectArray = UnityEngine.Resources.FindObjectsOfTypeAll(); cameraList.Clear(); foreach(var obj in allObjectArray){ if(obj.GetComponent() is var camera && camera != null && camera.cameraType == CameraType.Game){ cameraList.Add(camera); } } } private void UpdateSearch(){ if(allObjectIndex >= allObjectArray.Length){ ResetAllObjectArray(); } string path = AssetDatabase.GetAssetOrScenePath(allObjectArray[allObjectIndex]); while(!path.Contains(".unity") && ++allObjectIndex < allObjectArray.Length){ path = AssetDatabase.GetAssetOrScenePath(allObjectArray[allObjectIndex]); } if(allObjectIndex >= allObjectArray.Length) return ; bool result = CheckInScene(allObjectArray[allObjectIndex], path, cameraList); if(result){ EditorApplication.isPlaying = false; } ++allObjectIndex; } private static string[] extensions = { ".prefab", ".mat", ".controller", ".cs", ".shader", ".mask", ".asset" }; /// /// Missingがあるアセットを検索してそのリストを表示する /// [MenuItem("Assets/MissingSearch")] private static void ShowMissingList(){ // Missingがあるアセットを検索 Search(); } /// /// Missingがあるアセットを検索 /// private static void Search(){ // 全てのアセットのファイルパスを取得 string[] allPaths = AssetDatabase.GetAllAssetPaths(); int length = allPaths.Length; for(int i = 0; i < length; i++){ // プログレスバーを表示 EditorUtility.DisplayProgressBar("Search Missing", string.Format("{0}/{1}", i+1, length), (float)i / length); // Missing状態のプロパティを検索 if(extensions.Contains(Path.GetExtension(allPaths[i]))){ SearchMissing(allPaths[i]); } } // プログレスバーを消す EditorUtility.ClearProgressBar(); } private static void SearchMissing(string path){ // 指定パスのアセットを全て取得 IEnumerable assets = AssetDatabase.LoadAllAssetsAtPath(path); // 各アセットについて、Missingのプロパティがあるかチェック foreach(UnityEngine.Object obj in assets){ if(obj == null) continue; if(obj.name == "Deprecated EditorExtensionImpl") continue; Check(obj, string.Format("{0} {1}", path, obj.name)); } } private static bool CheckInScene(GameObject obj, string path, List cameraList){ bool result = false; string inScenePath = obj.name; Transform parent = obj.transform.parent; while(parent != null){ inScenePath = string.Format("{0}/{1}", parent.name, inScenePath); parent = parent.parent; } path = string.Format(@"{0} {1}", path, inScenePath); if(path.Contains("DebugOption")) return false; var components = obj.GetComponents(); bool hasCanvasRenderer = false; bool hasGraphic = false; bool hasMask = false; bool hasRectMask = false; foreach(var component in components){ if(component == null){ result = true; UnityEngine.Debug.LogError(string.Format("{0} に不明なコンポーネントがあるので修正して下さい。", path)); } if(component is CanvasRenderer){ hasCanvasRenderer = true; } if(component is Graphic){ hasGraphic = true; } if(component is SpriteRenderer spriteRenderer){ if(spriteRenderer.sprite == null){ result = true; UnityEngine.Debug.LogError(string.Format("{0} の SpriteRenderer の Sprite がNoneになっているので修正して下さい。", path)); }else if(spriteRenderer.drawMode == SpriteDrawMode.Tiled){ var sprite = spriteRenderer.sprite; if((sprite.texture.height <= 10 && spriteRenderer.size.y >= 1.0f) || (sprite.texture.width <= 10 && spriteRenderer.size.x >= 1.0f)){ result = true; UnityEngine.Debug.LogError(string.Format("{0} の SpriteRenderer の DrawMode がTiledなのに {1} を使用しているので修正して下さい。", path, spriteRenderer.sprite.texture.name)); } if(path.Contains("backGround_bank_b_ground_b")){ result = true; UnityEngine.Debug.LogError(spriteRenderer.size.x * sprite.pixelsPerUnit + " " + sprite.texture.width + " " + (sprite.texture.width - sprite.border.x - sprite.border.z)); } var triCount = (spriteRenderer.size.x * sprite.pixelsPerUnit - sprite.texture.width) / (sprite.texture.width - sprite.border.x - sprite.border.z) * (spriteRenderer.size.y * sprite.pixelsPerUnit - sprite.texture.height) / (sprite.texture.height - sprite.border.y - sprite.border.w) * 2; if(triCount >= 2000){ result = true; UnityEngine.Debug.LogError(string.Format("{0} の SpriteRenderer の DrawMode がTiledなのに狭いBorder設定のSprite {1} が設定されており、ポリゴン数が {2}以上 になっているので修正して下さい。", path, spriteRenderer.sprite.texture.name, (int)triCount)); } } if(obj.activeInHierarchy){ if(Mathf.Approximately(spriteRenderer.color.a, 0.0f)){ var hasPerspectiveTransparent = false; foreach(var camera in cameraList){ if(camera.orthographic){ if(camera.cullingMask == -1 || (camera.cullingMask & 1 << obj.layer) > 0){ var cameraAreaSize = camera.orthographicSize * 2 * (camera.orthographicSize * 2 * (float)Screen.width / (float)Screen.height); var spriteAreaSize = spriteRenderer.bounds.size.x * spriteRenderer.bounds.size.y; if(spriteAreaSize >= cameraAreaSize * 0.2f){ result = true; UnityEngine.Debug.LogError(string.Format("{0} の SpriteRenderer の Color.a が0なのに有効化されていて、カメラの{1}割を占めているので修正して下さい。", path, (int)(spriteAreaSize / cameraAreaSize * 10.0f))); } } }else{ hasPerspectiveTransparent = true; } } if(hasPerspectiveTransparent){ result = true; UnityEngine.Debug.LogError(string.Format("{0} の SpriteRenderer の Color.a が0なのに有効化されているので修正して下さい。", path)); } } } foreach(var material in spriteRenderer.materials){ if(material.shader.name.Contains("Particle") || material.shader.name.Contains("UGUI")){ result = true; UnityEngine.Debug.LogError(string.Format("{0} の SpriteRenderer のMaterialに3D用以外のShader {1} が設定されているので修正して下さい。", path, material.shader.name)); } } } if(component is MeshRenderer meshRenderer){ foreach(var material in meshRenderer.materials){ if(material.shader.name.Contains("Particle") || material.shader.name.Contains("UGUI")){ result = true; UnityEngine.Debug.LogError(string.Format("{0} の MeshRenderer のMaterialに3D用以外のShader {1} が設定されているので修正して下さい。", path, material.shader.name)); } } } if(component is Renderer renderer){ foreach(var material in renderer.materials){ if(material.shader.name.Contains("UGUI")){ result = true; UnityEngine.Debug.LogError(string.Format("{0} の Renderer のMaterialに3D用以外のShader {1} が設定されているので修正して下さい。", path, material.shader.name)); } } } if(component is Text text){ if(!text.enabled){ result = true; UnityEngine.Debug.LogError(string.Format("{0} に無効な Text があるので修正して下さい。", path)); } if(text.font == null){ result = true; UnityEngine.Debug.LogError(string.Format("{0} の Text の Font を設定下さい。", path)); } } if(component is Image image){ if(!image.enabled){ result = true; UnityEngine.Debug.LogError(string.Format("{0} に無効な Image があるので修正して下さい。", path)); } if(image.material != null && !image.material.shader.name.Contains("UI/Default") && !image.material.shader.name.Contains("UGUI") && !image.material.shader.name.Contains("Custom/Fade")){ result = true; UnityEngine.Debug.LogError(string.Format("{0} の Image のMaterialにUI用以外のShader {1} が設定されているので修正して下さい。", path, image.material.shader.name)); } if(image.type == Image.Type.Tiled){ var size = ((RectTransform)image.transform).sizeDelta; if((image.sprite.texture.height <= 10 && size.y >= 100.0f) || (image.sprite.texture.width <= 10 && size.x >= 100.0f)){ result = true; UnityEngine.Debug.LogError(string.Format("{0} の Image の ImageType がTiledなのに{1}を使用しているので修正して下さい。", path, image.sprite.texture.name)); } } } if(component is SortingGroup sortingGroup){ if(!sortingGroup.enabled){ result = true; UnityEngine.Debug.LogError(string.Format("{0} に無効な SortingGroup があるので修正して下さい。", path)); } } if(component is Mask mask){ hasMask = true; if(!mask.enabled || mask.transform.childCount <= 0){ result = true; UnityEngine.Debug.LogError(string.Format("{0} に無効な Mask があるので修正して下さい。", path)); } } if(component is RectMask2D rectMask){ hasRectMask = true; if(!rectMask.enabled || rectMask.transform.childCount <= 0){ result = true; UnityEngine.Debug.LogError(string.Format("{0} に無効な RectMask2D があるので修正して下さい。", path)); } } if(component is Animator animator){ if(animator.runtimeAnimatorController == null){ result = true; UnityEngine.Debug.LogError(string.Format("{0} の Animator の Controller がNoneになっているので修正して下さい。", path)); } } if(component is Outline outline){ if(!outline.enabled){ result = true; UnityEngine.Debug.LogError(string.Format("{0} に無効な Outline があるので修正して下さい。", path)); } } if(component is Joint2D joint2d){ if(!joint2d.enabled){ result = true; UnityEngine.Debug.LogError(string.Format("{0} に無効な Joint2D があるので修正して下さい。", path)); } } if(component is PlaySE playSE){ if(!playSE.enabled){ result = true; UnityEngine.Debug.LogError(string.Format("{0} に無効な PlaySE があるので修正して下さい。", path)); } } if(component is RectTransform rectTransform){ if(rectTransform.GetComponent() == null && !rectTransform.FindParentsComponent(canvas => {})){ result = true; UnityEngine.Debug.LogError(string.Format("RectTransformをCanvasの外に配置しないで下さい。 {0}", path)); } continue ; } if(component is Transform) continue ; result = result || Check(component, path); } if(hasCanvasRenderer && !hasGraphic){ result = true; UnityEngine.Debug.LogError(string.Format("{0} に無駄な CanvasRenderer があるので修正して下さい。", path)); } if(hasMask && hasRectMask){ result = true; UnityEngine.Debug.LogError(string.Format("{0} に2種の Mask があるので修正して下さい。", path)); } return result; } private static bool Check(UnityEngine.Object obj, string path){ bool result = false; // SerializedObjectを通してアセットのプロパティを取得する SerializedObject sobj = new SerializedObject(obj); SerializedProperty property = sobj.GetIterator(); while(property.Next(true)){ // プロパティの種類がオブジェクト(アセット)への参照で、 // その参照がnullなのにもかかわらず、参照先インスタンスIDが0でないものはMissing状態 if(property.propertyType == SerializedPropertyType.ObjectReference && property.objectReferenceValue == null && property.objectReferenceInstanceIDValue != 0 && property.name != "m_PrefabParentObject" && property.name != "m_PrefabInternal" && property.name != "m_CorrespondingSourceObject"){ result = true; if(obj.GetType().Name.Contains("ParticleSystemRenderer") && property.name.Contains("m_Mesh")){ UnityEngine.Debug.LogError(string.Format("{0} の {1} の RenderMode を Mesh に変更してMissingを修正して下さい。", path, obj.GetType().Name)); }else{ UnityEngine.Debug.LogError(string.Format("{0} の {1} にMissingがあるので修正して下さい。 {2}", path, obj.GetType().Name, property.name)); } } } return result; } #endif }