- Home /
OO Design | Specific example
Hey, I started a small prototype to get a grip on OOP in Unity. I like how my system works so far but I'm not happy with some parts of the design.
Here's a simplified version of the code:
interface IPerson { IWeapon ActiveWeapon; }
class Bandit : Monobehaviour, IPerson { IWeapon ActiveWeapon;}
interface IWeapon { Attack(); }
class Revolver : Monobehaviour, IWeapon { Attack(); }
public class Controller : MonoBehaviour {
IPerson thisPerson;
void Start () {
thisPerson = (IPerson) GetComponent(typeof(IPerson));
}
void Update () {
thisPerson.ActiveWeapon.Attack();
}
}
It works just fine but...
1, I have to use non-generic way to obtain IPerson and I can't define [RequireComponent (typeof (IPerson))] as IPerson doesn't (can't) inherit from MonoBehaviour
2, I can't assign values in the editor. It would be very convenient to have public IWeapon currentWeapon; and assign different components to this but I can't as IWeapon (or IPerson) can't inherit from MonoBehaviour.
Could you please provide some feedback how to go around this? Thank you a lot for any input you might have.
I really want to use more structured way than my usual get this component, get that component ... and force some methods using interfaces. Is it possible in Unity?
It all works internally, but you'll have to do some pretty hacky things to get it working nicely with the inspector. The way I did it was to have my components inherit from both $$anonymous$$onoBehaviour, and the interface I wanted to use for them. Then, I could drag them around in the inspector, but I would then have to manually check what they actually were before using them with any of the interface-specific bits.
Thank you for the comment, it makes some things more clear
Answer by gruhm · Oct 21, 2011 at 03:17 AM
If I understand your questions correctly, I don't think interfaces are really what you want. An interface is simple going to enforce that all classes that implement it conform. You can accomplish most of what you are after using simple inheritance:
public class BasePerson: MonoBehaviour
{
public BaseWeapon ActiveWeapon;
void Start()
{
//do whatever start up is common to all derived classes
AddToStart();
}
//called by derived classes for implementation specific startup
public virtual void AddToStart()
{
}
}
public class BaseWeapon: MonoBehaviour
{
public virtual void Attack()
{}
}
public class Revolver: BaseWeapon
{
public override void Attack()
{
//instance specific attack
}
}
public class Bandit: BasePerson
{
public override void AddToStart()
{
ActiveWeapon = new Revolver();
}
void update()
{
ActiveWeapon.Attack();
}
}
This should get you most of the way there. If I'm not mistaken, if you want to set the 'Revolver" variable via the inspector, you're still going to need a public GameObject variable and then use GetComponent to access the actual class. I see no need for your Controller class as you can simple add the derived "Person" class to your character. Hope this helps.
Seems to be working fine, I will test but thank you a lot. It help a lot
Sure you can use just a base class, the only difference is that inheritance is a little bit slower (due to checking the inheritance chain) and the biggest disadvantage is that you can only inherit from one base class since C# doesn't support multiple inheritance. The best way to archieve something similar is to use interfaces since a class can implement as much interfaces as you want.
Unfortunately like syclamoth already mentioned Unity provides only limited support for interfaces due to parameter restrictions. The generic GetComponent version have a constraint to UnityEngine.Component which makes it impossible to use it with interfaces. Even worse is FindObjectOfType which checks internally the Type-object you provide and refuses operation when the supplied type is not derived from UnityEngine.Object.
You can also implement your own custom GetComponent function (e.g. GetInterface()). That's how Unity's generic version is implemented:
public T GetComponent<T>() where T : Component
{
return this.GetComponent(typeof(T)) as T;
}
Just create you own extention method and remove the constraint ;)
public static T GetInterface<T>(this GameObject aGO)
{
return aGO.GetComponent(typeof(T)) as T;
}
public static T GetInterface<T>(this Component aComponent)
{
return aComponent.GetComponent(typeof(T)) as T;
}
Amazing, thank you. That's exactly what I had in $$anonymous$$d. Unfortunately with interfaces you still can't assign values in the inspector which renders them useless in my case as they can't be used in Prefabs. Of course I can always hack it around with some switches and enums but I really don't want to do that.
Answer by Romonaga · Oct 24, 2011 at 05:21 PM
A few thoughts on this chain. The advice provided by gruhm from a professional and from a design standard is the correct answer and solution to this question. The advice being provided by Bunny83 is not a good design and will lead to performance issues due to the reliance on reflection. Let us be clear reflection is a huge performance cost and should be used with that in mind. Using reflection will always be slower than walking the inheritance chain.
If you follow what gruhm posted, it should be clear that a public GameObject property that gets and returns the actual class will allow you to assign values in the inspector, this can be done without the use of switches and or enums.
The comment that C# does not support multiple inheritance is correct yet also at the same time incorrect. It is true you can only do one inheritance per class, however if you design your classes the way gruhm suggested it should be clear that this limitation is not a limitation at all and actually can lead to better code design.
Allot of developers tend to use interfaces as a solution to the perceived notion that C# does not support multiple inheritance, in my opinion this is bad info and a bad reason to use an interface. An interface should never be used to implement multiple inheritance. An interface is implemented not inherited, it seems many people forget this fact.
Your answer
Follow this Question
Related Questions
An OS design issue: File types associated with their appropriate programs 1 Answer
C# inheritance advice 2 Answers
Using the Delegate Object design pattern in Unity 1 Answer
Need to inherit from EventArgs AND ScriptableObject 1 Answer
How to make a List that can store anything that implements an interface? 4 Answers