Created
April 16, 2019 12:31
-
-
Save perky/dacab19060cdcb7fc00ee2fa6cd9ab4b to your computer and use it in GitHub Desktop.
A little script for Unity that helps binding references to other components in the scene.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Linq; | |
using System; | |
using System.Reflection; | |
public class BindComponent : PropertyAttribute | |
{ | |
public enum FindMode | |
{ | |
InSelf, // Find the component on the calling GameObject. | |
InChildren, // Find the component in the children of the calling GameObjet. | |
InParent, // Find the component up the parent hierarchy of the calling GameObject. | |
InScene, // Find the 1st instance of the component anywhere in the active scene. | |
ViaPath // Find the component via a hierarchy path relative to the calling GameObject. | |
} | |
public string path; | |
public FindMode findMode = FindMode.InSelf; | |
/// <summary> | |
/// Bind a reference to a component that exists on the same game object. | |
/// </summary> | |
public BindComponent() | |
{ | |
} | |
/// <summary> | |
/// Bind a reference to a component that exists on a child game object. | |
/// Must specify exact relative path. | |
/// </summary> | |
/// <param name="childPath"></param> | |
public BindComponent(string childPath) | |
{ | |
this.findMode = FindMode.ViaPath; | |
this.path = childPath; | |
} | |
/// <summary> | |
/// Bind a reference to a component that exists in the scene. | |
/// A FindMode must be specified to indicate how to find that component. | |
/// </summary> | |
/// <param name="findMode"></param> | |
public BindComponent(FindMode findMode) | |
{ | |
if (findMode == FindMode.ViaPath) | |
{ | |
Debug.LogError("BindComponent: Do not use FindMode.ViaPath. Use [BindComponent(\"relative/path\")] instead."); | |
} | |
this.findMode = findMode; | |
} | |
/// <summary> | |
/// Performs the actual binding. Typically you want to call this in Awake(). | |
/// For example: Awake() { BindComponent.Bind(this); } | |
/// </summary> | |
/// <param name="component">The component that is collecting the references (typically you pass 'this')</param> | |
/// <typeparam name="T">The type of the component passed in the component argument. (This is automatically inferred.)</typeparam> | |
public static void Bind<T>(T component) | |
{ | |
var attributeType = typeof(BindComponent); | |
var fields = typeof(T) | |
.GetFields(BindingFlags.Instance | BindingFlags.NonPublic) | |
.Where(x => x.IsDefined(attributeType, false)) | |
.ToArray(); | |
foreach (var field in fields) | |
{ | |
var attr = field.GetCustomAttribute(attributeType, false) as BindComponent; | |
var behaviour = component as MonoBehaviour; | |
if (attr.path == null) | |
{ | |
SetValue(field, component, behaviour, attr.findMode); | |
} | |
else if (attr.findMode == FindMode.ViaPath) | |
{ | |
string[] children = attr.path.Split('/'); | |
Transform transform = behaviour.transform; | |
int childIndex = 0; | |
while (childIndex < children.Length) | |
{ | |
string childName = children[childIndex++]; | |
transform = transform.Find(childName); | |
if (transform == null) | |
{ | |
Debug.LogError("BindComponent: Could not find child named " + childName + " in path " + attr.path, behaviour); | |
} | |
} | |
SetValue(field, component, transform, attr.findMode); | |
} | |
} | |
} | |
static void SetValue<T>(FieldInfo field, T source, Component destination, FindMode findMode) | |
{ | |
Component componentToBind = null; | |
switch (findMode) | |
{ | |
default: | |
case FindMode.InSelf: | |
componentToBind = destination.GetComponent(field.FieldType); | |
break; | |
case FindMode.InChildren: | |
componentToBind = destination.GetComponentInChildren(field.FieldType); | |
break; | |
case FindMode.InParent: | |
componentToBind = destination.GetComponentInParent(field.FieldType); | |
break; | |
case FindMode.InScene: | |
componentToBind = (Component)UnityEngine.Object.FindObjectOfType(field.FieldType); | |
break; | |
} | |
if (componentToBind == null) | |
{ | |
Debug.LogError("BindComponent: Could not find component " + field.FieldType.Name + " for field " + field.Name, source as Component); | |
} | |
field.SetValue(source, componentToBind); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using UnityEngine.UI; | |
public class BindComponentExample : MonoBehaviour | |
{ | |
// Find a Button component in the child named "PlayButton". | |
[BindComponent("PlayButton")] Button playButton; | |
// Find a Button component in the child named "RestartButton". | |
[BindComponent("RestartButton")] Button restartButton; | |
// Find an AudioSource component on the same GameObject. | |
[BindComponent()] AudioSource uiAudio; | |
// Find the GameState component in the scene. | |
[BindComponent(BindComponent.FindMode.InScene)] GameState gameState; | |
void Awake() | |
{ | |
// Do the actual binding. This is exposed in case you have your own custom lifecycle | |
// and wish to perform the bind elsewhere. | |
BindComponent.Bind(this); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment