popcorn/Scripts/Utilities/MissingSearcher.cs

318 lines
16 KiB
C#

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<Camera> cameraList = new List<Camera>();
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<GameObject>();
cameraList.Clear();
foreach(var obj in allObjectArray){
if(obj.GetComponent<Camera>() 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" };
/// <summary>
/// Missingがあるアセットを検索してそのリストを表示する
/// </summary>
[MenuItem("Assets/MissingSearch")]
private static void ShowMissingList(){
// Missingがあるアセットを検索
Search();
}
/// <summary>
/// Missingがあるアセットを検索
/// </summary>
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<UnityEngine.Object> 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<Camera> 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<Component>();
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<Canvas>() == null && !rectTransform.FindParentsComponent<Canvas>(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
}