Skip to content

Instantly share code, notes, and snippets.

@kokeiro001
Last active March 28, 2021 14:22
Show Gist options
  • Save kokeiro001/828b80045d1bbb0d0d5bb8012c7a2ea9 to your computer and use it in GitHub Desktop.
Save kokeiro001/828b80045d1bbb0d0d5bb8012c7a2ea9 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Unity.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Assets.Scripts.Editor
{
static class BindComponentUtil
{
[MenuItem("CONTEXT/MonoBehaviour/BindComponensFromChildren")]
private static void BindComponentsFromChildren(MenuCommand menuCommand)
{
BindComponents(menuCommand.context as MonoBehaviour, true, false);
}
[MenuItem("CONTEXT/MonoBehaviour/BindComponensFromScene")]
private static void BindComponentsFromScene(MenuCommand menuCommand)
{
BindComponents(menuCommand.context as MonoBehaviour, true, true);
}
private static void BindComponents(MonoBehaviour context, bool skipBinded, bool findScene)
{
Debug.Log($"context.name: {context.name}");
var contextType = context.GetType();
Debug.Log($"contextType.Name: {contextType.Name}");
BindFields(context, skipBinded, findScene);
BindProperty(context, skipBinded, findScene);
EditorUtility.SetDirty(context);
AssetDatabase.SaveAssets();
}
private static void BindFields(MonoBehaviour context, bool skipBinded, bool findScene)
{
var contextType = context.GetType();
Debug.Log($"contextType.Name: {contextType.Name}");
var bindableFields = GetBindableFields(contextType).ToArray();
foreach (var field in bindableFields)
{
var value = field.GetValue(context);
if (skipBinded && value != null)
{
Debug.LogWarning($"Name {field.Name} value != null");
continue;
}
var foundInChildren = false;
// 子要素から探す
foreach (var gameObject in context.gameObject.Descendants())
{
var component = GetBindableComponent(gameObject, field.Name, field.FieldType);
if (component != null)
{
Debug.Log($"component {field.FieldType.Name} {field.Name} found.", gameObject);
field.SetValue(context, component);
foundInChildren = true;
break;
}
}
// 見つからなかった場合、シーン全体から探す
if (!foundInChildren && findScene)
{
var allGameObjects = SceneManager.GetActiveScene().GetRootGameObjects()
.SelectMany(x => x.DescendantsAndSelf());
foreach (var gameObject in allGameObjects)
{
var component = GetBindableComponent(gameObject, field.Name, field.FieldType);
if (component != null)
{
Debug.Log($"component {field.FieldType.Name} {field.Name} found.", gameObject);
field.SetValue(context, component);
break;
}
}
}
}
}
private static IEnumerable<FieldInfo> GetBindableFields(Type contextType)
{
var serializableFields = new List<FieldInfo>();
var publicFields = contextType.GetFields(BindingFlags.Instance | BindingFlags.Public);
serializableFields.AddRange(publicFields);
// NonPublicなフィールドのうち、SerializeField属性が設定されてるものを取得する
var nonPublicFields = contextType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (var field in nonPublicFields)
{
var serializeFieldAttribute = field.GetCustomAttribute<SerializeField>();
if (serializeFieldAttribute != null)
{
serializableFields.Add(field);
}
}
foreach (var field in serializableFields)
{
var isComponentField = field.FieldType.IsSubclassOf(typeof(Component));
if (isComponentField)
{
yield return field;
}
}
}
private static void BindProperty(MonoBehaviour context, bool skipBinded, bool findScene)
{
var contextType = context.GetType();
Debug.Log($"contextType.Name: {contextType.Name}");
var bindableProperties = GetBindableProperties(contextType).ToArray();
foreach (var property in bindableProperties)
{
var value = property.GetValue(context);
if (skipBinded && value != null)
{
Debug.LogWarning($"Name {property.Name} value != null");
continue;
}
var findInChildren = false;
// 子要素から探す
foreach (var gameObject in context.gameObject.Descendants())
{
var component = GetBindableComponent(gameObject, property.Name, property.PropertyType);
if (component != null)
{
Debug.Log($"component {property.PropertyType.Name} {property.Name} found.", gameObject);
property.SetValue(context, component);
findInChildren = true;
break;
}
}
// 見つからなかった場合、シーン全体から探す
if (!findInChildren && findScene)
{
var allGameObjects = SceneManager.GetActiveScene().GetRootGameObjects()
.SelectMany(x => x.DescendantsAndSelf());
foreach (var gameObject in allGameObjects)
{
var component = GetBindableComponent(gameObject, property.Name, property.PropertyType);
if (component != null)
{
Debug.Log($"component {property.PropertyType.Name} {property.Name} found.", gameObject);
property.SetValue(context, component);
break;
}
}
}
}
}
private static IEnumerable<PropertyInfo> GetBindableProperties(Type contextType)
{
var serializableProperties = new List<PropertyInfo>();
var publicProperties = contextType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
serializableProperties.AddRange(publicProperties);
// NonPublicなフィールドのうち、SerializeField属性が設定されてるものを取得する
foreach (var property in publicProperties)
{
if (property.Name == "transform"
&& property.PropertyType == typeof(Transform))
{
Debug.LogWarning("skip transform");
continue;
}
var serializeFieldAttribute = property.GetCustomAttribute<SerializeField>();
if (serializeFieldAttribute != null)
{
serializableProperties.Add(property);
}
}
foreach (var field in serializableProperties)
{
var isComponentField = field.PropertyType.IsSubclassOf(typeof(Component));
if (isComponentField)
{
yield return field;
}
}
}
private static Component GetBindableComponent(GameObject gameObject, string name, Type type)
{
// フィールドとオブジェクトの名前を大文字小文字無視して比較する
if (string.Compare(gameObject.name, name, true) != 0)
{
// 一致しなかったらスキップ
return null;
}
var childComponent = gameObject.GetComponent(type);
return childComponent;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment