I have functions that will let me get component/components of some interface type. So the GetComponent thing isn’t an issue. Similarish to what you (KelsoMRK) posted. My issue isn’t getting a reference to the interface… it’s that once I have it I can’t test null evaluations.
This allows me to write components that have the shape of an interface… which commands that they follow some contract.
For instance I have an interface (the one where I found this issue) called IEntity. An IEntity is a component that is attached to a GameObject that is an intelligent entity. It can see/hear/smell/pathfind and has health/strength/etc.
interface IEntity
{
float MaxHealth { get; }
float CurrentHealt { get; set; }
float Strength { get; set; }
float CanSee(IEntity targ)
float CanSmell(IEntity targ)
float CanHear(IEntity targ)
void FollowPath(Path path)
//... other stuff too
}
Now how it implement CanSee depends on the entity. Some entities may see by overlapping some geometry in front of itself. Another, like the player, might see what ever is in the main camera’s view frustum. Another might be able to see through walls or the entire world or something.
Well that means the component for each entity is different, has different implementations. But they’re all entities. So I implement the IEntity interface and do my custom work in there. Now when I reference it I don’t care if it’s the player entity, or bear entity… it’s an entity, and that’s all I need to know.
For instance… HunterVision, this calculates some entity that another might have advantage over. If I have advantage and I’m stalking it, I can perform an ultra on it. Think like the “bite” attack in “Tokyo Jungle”.
using UnityEngine;
using System.Collections.Generic;
using com.spacepuppy.Collections;
using com.spacepuppy.Geom;
using com.spacepuppy.Timers;
using com.spacepuppy.Utils;
using Ponyo;
using Ponyo.Items;
/// <summary>
/// Represents the vision of a hunter and calculates the hunter's advantage, if any.
/// Requires IEntity interface attached to gameObject.
/// </summary>
public class HunterVision : com.spacepuppy.SPComponent
{
#region Fields
private IEntity _self;
private IEntity _targ;
private RandomSwitch _switch = new RandomSwitch(0.2f, 1.0f, 0.2f, 1.0f);
#endregion
#region Properties
public IEntity Target
{
get { return _targ; }
}
#endregion
#region Unity Events
void Start()
{
IEntity comp;
if (this.GetFirstComponentOfInterface<IEntity>(out comp))
{
_self = comp;
}
else
{
//throw new System.Exception("HunterVision must have a sibling component of type IEntity.");
Debug.Log("HunterVision must have a sibling component of type IEntity.");
Object.Destroy(this);
}
}
// Update is called once per frame
void Update ()
{
if (ObjUtil.IsNullOrDestroyed(_targ)) return;
if (!_self.HasAdvantageOver(_targ))
{
_targ = null;
_switch.Reset();
}
else
{
_switch.Update(Time.deltaTime);
}
}
#endregion
#region Public Methods
public bool FindTarget()
{
var ents = _self.GetVisibleEntities();
if (ents.Length == 0) return false;
System.Array.Sort(ents, delegate(IEntity o1, IEntity o2)
{
var d1 = (o1.transform.position - this.transform.position).sqrMagnitude;
var d2 = (o2.transform.position - this.transform.position).sqrMagnitude;
return d1.CompareTo(d2);
});
foreach (var ent in ents)
{
if (this.FocusOnTarget(ent)) return true;
}
return false;
}
public bool FocusOnTarget(IEntity targ)
{
if (targ != null !_self.HasAdvantageOver(targ)) return false;
_targ = targ;
return true;
}
public bool CanPerformUltra()
{
if (ObjUtil.IsNullOrDestroyed(_targ)) return false;
return _switch.Allow;
}
public bool CanPerformUltra(IEntity targ)
{
if (ObjUtil.IsNullOrDestroyed(targ) || ObjUtil.IsNullOrDestroyed(_targ)) return false;
return (targ == _targ _switch.Allow);
}
#endregion
}
Note all the lines where I say:
ObjUtil.IsNullOrDestroyed(targ)
That’s because ‘targ’ is referenced as IEntity, and even though it’s a Component, when it’s destroyed saying:
targ == null
still evaluates as false… but if I say:
((Component)targ) == null
it works
Currently the ObjUtil.IsNullOrDestroyed works for me because it allows me to do extra testing to it if I need to. For instance I could write an IEntity that wraps a component, but isn’t a component itself. All my interfaces that are supposed to be components inherit from an interface IComponent:
interface IComponent
{
GameObject gameObject { get; }
Transform transform { get; }
Component component { get; }
bool isDestroyed { get; }
}
interface IEntity : IComponent
{
//...
}
Then I just basically say in that funciton:
public static bool IsNullOrDestroyed(object obj)
{
if(obj is IComponent)
return (obj as IComponent).isDestroyed;
else if(obj is Component)
return (obj as Component) == null;
else
return obj == null;
}
I just don’t like that this weird oddity occurs. And thought everyone here would like to know about it. And maybe other people could share how they deal with it.