This stuff also might be useful… it might not be. I really like it though, and use it all over the place. This code is entirely not production ready, but it should be pretty straight forward… the idea is that you can mark properties in code with attributes that point to dependent components. Again, this code should work - but might have some ideas that I haven’t fleshed out or named well.
So the usage is like this:
public class TacticalTroop : DecoratedBehaviour {
// fetch a TacticalTroopController from the game object (GetComponent<TacticalTroopController>)
[FromGameObject]
protected TacticalTroopController Controller { get; set; }
// fetch a Renderer from this game object or any child (GetComponentInChildren<Renderer>)
[FromChild]
protected Renderer Renderer { get; set ;}
The base class ‘decoratedbehaviour’ automatically resolves these inter-game object dependencies using the following mess of code. Generally this kind of thing is most useful for gui screens, but I use it all over the place. It’s really just a set of short hand for GetComponent or GetComponentsInChildren, etc. I think it makes these dependencies clear and obvious, which I tend to like.
using System;
using System.Collections.Generic;
using System.Reflection;
using P = InstancePropertyAttribute;
using C = InstanceClassAttribute;
using SP = StaticPropertyAttribute;
using SC = StaticAttribute;
public static class InstanceAttribute
{
private static readonly Dictionary<Type, D> m_CACHE = new Dictionary<Type, D>();
public static void ActUpon(Object o)
{
D d;
Type t = o.GetType();
if (m_CACHE.TryGetValue(t, out d) == false)
{
d = GetAttributes(o);
m_CACHE.Add(t, d);
}
for (int index = 0; index < d.Cs.Length; index++)
{
var c = d.Cs[index];
c.ActUpon(o);
}
for (int index = 0; index < d.Ps.Length; index++)
{
var p = d.Ps[index];
p.PropAttr.ActUpon(o, p.Info);
}
}
private static D GetAttributes(Object o)
{
Type t = o.GetType();
var members = t.GetMembers(BindingFlags.Instance
| BindingFlags.NonPublic
| BindingFlags.Public);
var properties = new List<PropMemb>();
// Roll all the instance attributes into D, since the static ones are...static, just fire them here.
C[] cls_attr = (C[])t.GetCustomAttributes(typeof(C), true);
foreach (SC sc in t.GetCustomAttributes(typeof(SC), true))
sc.ActUpon(t);
for (int index = 0; index < members.Length; index++)
{
MemberInfo member = members[index];
PropertyInfo prop = member as PropertyInfo;
if (prop == null) continue;
P[] attrib = (P[])prop.GetCustomAttributes(typeof(P), true);
if (attrib.Length == 0) continue;
properties.Add(new PropMemb(attrib[0], prop));
SP[] sps = (SP[])prop.GetCustomAttributes(typeof(SP), true);
foreach (var sp in sps) sp.ActUpon(t, prop);
}
return new D(cls_attr, properties.ToArray());
}
#region Nested type: D
private class D
{
public D(InstanceClassAttribute[] cs, PropMemb[] ps)
{
Cs = cs;
Ps = ps;
}
public readonly C[] Cs;
public readonly PropMemb[] Ps;
}
#endregion
#region Nested type: PropMemb
private class PropMemb
{
public PropMemb(InstancePropertyAttribute prop_attr, PropertyInfo info)
{
PropAttr = prop_attr;
Info = info;
}
public readonly PropertyInfo Info;
public readonly P PropAttr;
}
#endregion
}
public abstract class StaticAttribute : Attribute
{
public abstract void ActUpon(Type type);
}
public abstract class StaticPropertyAttribute : Attribute
{
public abstract void ActUpon(Type type, PropertyInfo info);
}
public abstract class InstanceClassAttribute : Attribute
{
public abstract void ActUpon(Object instance);
}
public abstract class InstancePropertyAttribute : Attribute
{
public abstract void ActUpon(Object instance, PropertyInfo attached_property);
}
and…
public class DecoratedBehaviour : MonoBehaviour {
protected void Awake() {
InstanceAttribute.ActUpon( this );
OnAwake();
}
protected virtual void OnAwake() { }
}
public class RequirementException : Exception {
public RequirementException() { }
public RequirementException( string message ) : base( message ) { }
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class FromChildByName : InstancePropertyAttribute {
private String Name;
private bool IsRequired;
public FromChildByName(String name, bool is_required = true) {
Name = name;
IsRequired = is_required;
}
public override void ActUpon( object instance, PropertyInfo attached_property ) {
Component c = instance as Component;
Debug.Assert( c != null, "c != null" );
var possibles = c.GetComponentsInChildren( attached_property.PropertyType, true );
for( int index = 0 ; index < possibles.Length ; index++ ) {
var possible = possibles[ index ];
if( possible.name == Name ) {
attached_property.SetValue( instance, possible, null );
return;
}
}
if (IsRequired) {
var msg = String.Format( "{0} ({1}) requires a {2} {3}",
instance.GetType().Name,
c.GetPathyName(),
attached_property.PropertyType.Name,
GetType().Name );
throw new RequirementException( msg );
}
}
}
[AttributeUsage( AttributeTargets.Property, AllowMultiple = false, Inherited = true )]
public class FromGameObject : InstancePropertyAttribute {
public FromGameObject( bool is_required = true ) { IsRequired = is_required; }
protected virtual object GetComponent( Component src, Type target ) { return src.GetComponent( target ); }
public override void ActUpon( object instance, PropertyInfo attached_property ) {
Type t = attached_property.PropertyType;
Component c = (Component) instance;
object target = GetComponent( c, t );
if( target != null ) {
attached_property.SetValue( instance, target, null );
} else if( IsRequired ) {
var msg = String.Format( "{0} ({1}) requires a {2} {3}",
instance.GetType().Name,
c.GetPathyName(),
t.Name,
GetType().Name );
throw new RequirementException( msg );
}
}
public bool IsRequired;
}
public class FromAllChildren : FromGameObject {
public FromAllChildren( bool is_required = true ) : base(is_required){ }
protected override object GetComponent(Component src, Type target) {
Type item_type = target.GetElementType();
if( item_type == null ) throw new ArgumentException("FromAllChildren must be array type failed in: " + src.GetType() + " attemptnig to get:" + target.Name );
Component[] targets = src.GetComponentsInChildren( item_type, true );
var ret = Array.CreateInstance( item_type, targets.Length );
Array.Copy( targets, ret, ret.Length );
return ret;
}
}
public class FromParents : FromGameObject{
public FromParents(bool is_required = true) : base(is_required) { }
protected override object GetComponent(Component src, Type target) {
return src.GetComponentInParents(target, false);
}
}
public class FromChild : FromGameObject
{
public FromChild(bool is_required = true) : base(is_required) { }
protected override object GetComponent(Component src, Type target) {
return src.GetComponentInChildren( target );
}
}
And finally, some other utilities:
public static class go_extensions {
public static bool TryGetComponent<T>( this Component go, out T target ) where T : Component {
var t = go.gameObject.GetComponent<T>();
if( t != null ) {
target = t;
return true;
}
target = null;
return false;
}
public static T GetComponentInParents<T>( this MonoBehaviour go, bool is_required = true) where T : MonoBehaviour{
return GetComponentInParents<T>(go.transform, is_required);
}
public static T GetComponentInParents<T>( this GameObject go, bool is_required = true) where T: MonoBehaviour {
return GetComponentInParents<T>(go.transform, is_required);
}
public static T GetComponentInParents< T >( this Transform go, bool is_required = true ) where T : MonoBehaviour {
T target;
var localgo = go;
do {
target = localgo.GetComponent< T >();
localgo = localgo.transform.parent;
} while (target == null localgo != null);
if( target == null is_required ) {
Debug.LogError( "Unable to find required component of type:" + typeof( T ).Name + " in objecct " + lo.GetPathyName( go.gameObject ) );
}
return target;
}
public static Component GetComponentInParents( Transform go, Type target_type, bool is_required = true ) {
// TODO: generic impl could use this guy - but then requires casting.
Component target;
var localgo = go;
do
{
target = localgo.GetComponent(target_type);
localgo = localgo.transform.parent;
} while (target == null localgo != null);
if (target == null is_required)
{
Debug.LogError("Unable to find required component of type:" + target_type.Name + " in objecct " + lo.GetPathyName(go.gameObject));
}
return target;
}
public static Component GetComponentInParents(this Component go, Type target_type, bool is_required = true ) {
return GetComponentInParents(go.transform, target_type, is_required );
}
public static T GetInterfaceInChildren< T >( this GameObject go ) {
var monos = go.GetComponentsInChildren< MonoBehaviour >();
for( int index = 0 ; index < monos.Length ; index++ ) {
var mono = monos[ index ];
if( mono is T ) {
return ( T ) ( System.Object ) mono;
}
}
return default( T );
}
public static List< T > GetInterfacesInChildren< T >( this GameObject go ) {
List< T > list = new List< T >();
var monos = go.GetComponentsInChildren< MonoBehaviour >();
for( int index = 0 ; index < monos.Length ; index++ ) {
var mono = monos[ index ];
if( mono is T ) {
list.Add( ( T ) ( System.Object ) mono );
}
}
return list;
}
public static String GetPathyName( this Component mb ) { return GetPathyName( mb.gameObject ); }
public static String GetPathyName( this GameObject go ) {
string path = "/" + go.name;
while( go.transform.parent != null ) {
go = go.transform.parent.gameObject;
path = "/" + go.name + path;
}
return path;
}
}
Not saying this stuff is the best possible way, or even a best practice. But I’ve found these tags useful and clear.