Search Unity

WHY did I AVOID interfaces!? Discuss cool things u can do with them here.

Discussion in 'General Discussion' started by Slyder, Jul 22, 2015.

  1. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    I never realized how awesome these were. I thought I would just do everything with abstract classes and inheritance. Boy was I missing out.

    Now I can Open doors and Pick items up with a single check. I no longer need tags or layers.
    Code (csharp):
    1.  
    2. IUse use = GetComponent<IUse>();
    3. if(use is IUse)
    4.      use.Use();
    5.  
    Can equip chickens and eat cars if I want to...these are amazing!

    It also works extremely well with the new UI. I have a bunch of different "action" buttons in a scrolling list. Eat, Equip, Drop, Reload, etc...
    I can do the following to hide/show the appropriate action buttons:
    Code (csharp):
    1.  
    2. public GameObject actionEat;
    3. public GameObject actionEquip;
    4. public GameObject actionReload;
    5. public GameObject actionDrop;
    6.  
    7. actionEat.SetActive((itemRef is IUse) ? true : false);
    8. actionEquip.SetActive((itemRef is IEquip) ? true : false);
    9. actionReload.SetActive((itemRef is IReload) ? true : false);
    10. actionDrop.SetActive((itemRef is IDrop) ? true : false);
    11.  
     
    Last edited: Jul 22, 2015
  2. derf

    derf

    Joined:
    Aug 14, 2011
    Posts:
    346
    Many people avoid developing interfaces as they feel they are either A) To complex of an idea or B) Fill a very, very specific need to one type of solution and they feel their requirements DO NOT meet that need or require such a solution.

    They than spend even more time writing similar methods using similar parameters or values and basically developing the same type of code logic over and over and over again...etc.
     
    Slyder likes this.
  3. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    Tell me if this is wrong...but the way I am currently doing it is to use classes as containers to inherit the interfaces from.
    ie all "Items" PickUp the same way so we can do:
    Code (csharp):
    1.  
    2. public class Item : MonoBehaviour, IUse, IPickup {
    3.  
    4.      public void Use(someRef) {
    5.           this.PickUp(someRef.inventoryRef);
    6.      }
    7.  
    8.      public void PickUp(someInvRef) {
    9.           someInvRef.AddItem(this);
    10.           //do stuff to this item
    11.      }
    12. }
    13.  
    class Weapon...
    Code (csharp):
    1.  
    2. public class Weapon : Item, IEquip {
    3.        public void Equip(){}
    4. }
    5.  
    class Door...
    Code (csharp):
    1.  
    2. public class Door : MonoBehaviour, IUse {
    3.      public void Use(someRef) {
    4.             //open door
    5.      }
    6. }
     
  4. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    I would say the bigger disconnect is between what they are and how to properly use them. Interfaces usually get introduces as some weird inheritance thing, which I don't think too many languages have parallels to (at least the major ones). It's only when you wrap your head around how to use them, essentially as a communications framework that doesn't have the normal limitations of inheritance, that their actual usefulness comes through.

    This is one of those areas where I think it matters more (and is more useful) in Unity, with it's component architecture, than it would in normal application development. It's a fair bit more liberating to not need to inherit from something when it already needs to inherit from Monobehaviour.
     
  5. landon912

    landon912

    Joined:
    Nov 8, 2011
    Posts:
    1,572
    The strategy design pattern is a great use of interfaces.
     
  6. derf

    derf

    Joined:
    Aug 14, 2011
    Posts:
    346

    First show me an interface you have created.

    Then demo how you would implement it into a Game Object file.
     
  7. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,562
    Well this example is kind of silly because you've already gotten an object that you know is IUse so no need for the if check.

    Here's another handy dandy thing though that you might not have thought about. You can use Interfaces as "markers". We use this approach all of the time in business applications to assist with IoC (dependency injection). But here's an example. Let's say you have a whole lot of items that you can pick up. Call them "collectibles". They don't necessarily need to share any methods, but you want to store them in a uniform fashion.

    So, let's use The Witcher as an example. You have Weapons and Armor, Quest Items, Books, Consumables (we'll stop there). Now you want to build an inventory system. You will have different types of items, so you may have these interfaces:

    IBook, IWearable, IQuestItem, IConsumable. So you could have this:

    Code (csharp):
    1.  
    2. public class Inventory
    3. {
    4.     public List<IWearable> Wearables {get; set;}
    5.     public List<IQuestItem> QuestItems {get; set;}
    6.     public List<IBooks> Books {get; set;}
    7.     public List<IConsumables> Consumables {get; set;}
    8. }
    9.  
    Of course you could completely customize that and have one "marker" interface called IInventoryItem. Now look how that works:
    Code (csharp):
    1.  
    2. public class Letter: IBook, IInventoryItem
    3. {
    4.  //...
    5. }
    6.  
    7. public class Sword: IWearable, IInventoryItem
    8. {
    9.  //...
    10. }
    11.  
    12. public class ChestPlate: IWearable, IInventoryItem
    13. {
    14.  
    15. }
    16.  
    17. public class Apple: IConsumable, IInvetoryItem
    18. {
    19.  
    20. }
    21.  
    22. public class LovePotion: IConsumable, IQuestItem, IInventoryItem
    23. {
    24.  
    25. }
    26.  
    So here you have a letter, a sword, a chestplate, an apple and a love potion. All of them implement IInventoryItem. So your Inventory class looks like this:

    Code (csharp):
    1.  
    2. public class Inventory
    3. {
    4.     public List<IInventoryItem> Items {get; set;}
    5. }
    6.  
    Now you want different types:

    Code (csharp):
    1.  
    2. public List<IQuestItem> GetQuestItems()
    3. {
    4.     return Items.Where(itm => itm is IQuestITem).Select(itm as IQuestItem).ToList();
    5. }
    6.  
    7. public List<IWearable> GetWearables()
    8. {
    9.     return Items.Where(itm => itm is IWearable && !(itm is IQuestItem)).Select(itm as IWearable).ToList();
    10. }
    11.  
    You get the picture here. Best of all, if you use a serializer that supports polymorphic serialization (shameless plug - see my signature block), then you can serialize and deserialize your entire inventory very easily because you only have one collection of inventory items to worry about.
     
  8. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    The if(use is IUse) can be changed to if(use != null). you still need a check because GetComponent<IUse>() will null if it doesn't exist.

    I am currently doing something similar to what you wrote, but I am not using a full interface solution. I have
    IStack (item can stack)
    IConsume (consumable)
    IRepair (repairable)
    IEquip (can wear)
    IUse (can use...ie a door or pickup)
    IAttack

    Then I have:
    Code (csharp):
    1.  
    2. public abstract class Item : MonoBehaviour, IUse {
    3.  
    4.     public string itemName;
    5.     public int itemBulk;
    6.  
    7.     public Sprite itemIcon;
    8.  
    9.     public void Use(Blackboard b) {
    10.         this.PickUp (b.invRef);
    11.     }
    12.  
    13.     public void PickUp(InventoryController invRef) {
    14.         if(invRef.GetInvBulk () + this.itemBulk <= invRef.invBulkCap) {
    15.             invRef.itemList.Add (this);
    16.             this.SetPickedUp (true);
    17.             this.transform.SetParent(invRef.transform);
    18.             this.transform.position = this.transform.position;
    19.         }
    20.     }
    21.     public void SetPickedUp(bool isPickedUp) {
    22.         foreach(Collider c in this.GetComponents<Collider>())
    23.             c.enabled = !isPickedUp;
    24.         foreach(Renderer r in this.GetComponentsInChildren<Renderer>())
    25.             r.enabled = !isPickedUp;
    26.         this.GetComponent<Rigidbody>().isKinematic = isPickedUp;
    27.  
    28.         if(this is IStack) {
    29.             if(isPickedUp)
    30.                 this.InvokeRepeating ("UpdateStack",0.0f, 0.05f);
    31.             else
    32.                 this.CancelInvoke ();
    33.         }
    34.     }
    35.  
    36.     public void Drop() {
    37.         this.SetPickedUp (false);
    38.     }
    39. }
    40.  
    41. public abstract class Weapon : Item, IEquip, IAttack {
    42. //weapon stuff
    43. }
    44.  
    45. public interface IUse {
    46.      void Use(Blackboard b);
    47. }
    48. public interface IStack {
    49.      int stackSize {get; set}
    50.      void SplitStack(int num);
    51.      void Stack(IStack other);
    52.      void UpdateStack();
    53. }
    54.  
    I wanted my Items and Inventory to work independently so the solution I used is as follows:
    1. When an "IStack" Item is picked up or is a child of an Equipped item (ie Gun parent to Ammo inside the gun), I InvokeRepeating a function that Destroys the object when stackSize is <= 0.

    2. My InventoryController uses a List<Item> to store all inventory Items. The list is updated each frame to clean up missing GameObjects with itemList.RemoveAll(p=> p==null);

    I also use interfaces to hide/show Buttons in the following action list. I have merged Eat/Drink into "Consume" to use less interfaces.
    Capture.JPG

    If I give the NPCs their own inventories, I will probably serialize it as I can then load and randomize it as needed.
     
    Dustin-Horne likes this.
  9. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,451
    ExecuteEvents lets you call an interface on all MonoBehaviours on a GameObject. You can also do this climbing the hierarchy. Very powerful for decoupling.
     
    sluice, tango209 and Dustin-Horne like this.
  10. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    Is this for drag events and stuff? I'm not too familiar with the eventsystem yet.
     
  11. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,451
    Emphasis on the and stuff. It works for drag events and click events, but it gets even more useful when you define your own events.

    The most obvious use for it is communicating damage. In one of my projects I have a damage system where each part of the model has a different damage multiplier. There is a damage controller at the root of the model that implements an IDamagable interface. Each of the various bones has a HitBox script in place that detects the actual collisions and applies the damage multiplier. The HitBox then calls ExecuteEvents.ExecuteHierachy<IDamagable>.

    In this system the HitBox never knows about the damage controller. It simply detects damage and fires them up the chain for something else to deal with.

    Bullets have the same sort of system with the HitBox script. Intractable objects have the same system. This system also makes context sensitive input controls really easy.
     
    zombiegorilla and Master-Frog like this.
  12. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    Ah...I will have to look into that when I rebuild my damage model. I'm doing something where you can hit the Outside, Main organs, and main bones to effect the character. It doesn't do direct damage, but rather stacks injuries and effects on the player.

    I used a RayCastAll and Linq to order the hits by distance to process the hits on each hitbox.
     
  13. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    You avoided them cause you didn't know about them.

    microwave.jpg

    That's a microwave with no interface.
    ..and what most people's code looks like to me.

    I bet you could still cook your dinner with it. Just would take a little wizardry.

    In my opinion, the way people describe interfaces makes them obscure-sounding. Contracts? Decoupled constructs? Why not just call it black magick?

    "I know nothing about this object except what the buttons on the outside say it can do." That's what they are.

    Seems like some lecherous tomfoolery, the devil's handiwork... But if all you know about something is how it can be used, you can abstract around it. Supposedly that's what classes were supposed to behave like, with public and private members to restrict access to some stuff. But then every variable ends up public, anyway.

    The way you guys are using it, as a way of marking things... and literally for no other reason then to say if "apple is fruit"... I'm not 100% sure that's the purpose of interfaces.

    It's syntactically very sweet, though.

    I'm not sure it's any better than saying if (item.IsFood) in the end, where IsFood has a public get and private set. In fact, I think if (item.IsFood) is almost certainly better if you're just trying to check a property of an object, rather than using an interface to specify nothing but a single attribute for a class.
     
    GibTreaty, derf, sluice and 3 others like this.
  14. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    In addition to marking things up, I do use it for other functionality, like item stacking.
    Code (csharp):
    1.  
    2.  
    3. public interface IStack {
    4.      int stackSize {get; set}
    5.      void SplitStack(int num);
    6.      void Stack(IStack other);
    7.      void UpdateStack();
    8. }
    9.  
    10.  
    Only annoying part with this is that properties are not exposed to the editor.
     
    Last edited: Jul 23, 2015
  15. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    I would stick with class properties if you're relying on exposing values to the editor. Tweaking from the editor saves so much time... I can't see losing that feature, especially when tweaking prefabs in the editor applies changes at runtime that actually are applied permanently, allowing you to actually edit your game while you're playing it.

    However, when classes must interact with each other in code... that's when you want to use interfaces. The interface will actually result in nothing but extra code and a better decoupling of your objects, but as your project expands it will be worth it. The interfaces just sit on top of your classes, they wouldn't necessarily modify they way you would construct your classes. And probably, they shouldn't.

    Yes, Unity's editor almost necessitates making every property public, and if you try to add custom accessibility modifiers I believe it doesn't serialize automatically.

    Again, you can get around that with editor scripting, which is (again) more code, but a better solution.

    Depends on how serious you are about decoupling.

    However, if you use custom editor scripts you can have your cake and eat it, too. Using interfaces and taking advantage of the editor.

    The laziest way is just to make every property Public with no access modifiers, in case you just want to write the least possible code... and I'm rambling again.
     
    Last edited: Jul 23, 2015
  16. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,451
    [SerialiseField]. Its a lifesaver for keeping encapsulation while at the same time exposing variables to the inspector.

    It even works (somewhat hackishly) to expose the backing variable of properties.
     
    Master-Frog likes this.
  17. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    Having stacksize in editor is not a big deal. For any spawned Stack of Items, I will be randomizing it in some way. I can also set it in start() for testing.
     
  18. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    thanks!
     
  19. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    Uh, what? I assume this requires more work than just slapping on the SerializeField tag.
     
  20. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    I didn't understand that part of it, all I took away is that I can expose my privates.
     
    DMeville likes this.
  21. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,451
    Somewhat hackishly was the key term here. As in

    Code (CSharp):
    1. [SerialiseFeild] float _myFloat;
    2. public float myFloat {
    3.     get {
    4.         ...
    5.         return _myFloat;
    6.     }
    7.     ...
    8. }
    True serialisation of properties requires some editor magic with reflection. I've use it a couple of times, but mostly it's not worth the effort.
     
  22. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    Why can't I figure out what this would result in?
     
  23. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,451
    If you took out the ellipses it would result in a variable myFloat that was modifiable in the inspector, but read only from other scripts.
     
  24. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    I'm going to have to try it. Makes sense though.

    Now what about a generic hashtable that takes a string as a key, eliminating types from our consideration all together.
     
  25. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,451
    Some languages implement very weak typing, allowing you to basically add or remove members at will. I'm not a fan of it. In very general terms it makes a language slower and harder to use.

    And every string you type in is an error just waiting to bite you.
     
    tango209 and Master-Frog like this.
  26. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    i no!!

    So make it an enum key.
    480px-Troll_Face_Trollface.png

    ...this counts as a joke for me. That's what it's come to.
     
  27. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,451
    And back to interfaces, another thing you can do is

    Code (CSharp):
    1. IMyInterface myInterface = (IMyInterface) GetComponent(typeof(IMyInterface);
    I tend to use this pattern when building actor systems. Each component is dependent on interfaces instead of concrete classes. It makes swapping individual parts of a system out really easy.
     
    sluice, angrypenguin and Dustin-Horne like this.
  28. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    Okay, so nothing I haven't done en masse. I was reading between the lines and thinking that it would sort of expose it, but require a custom inspector to actually use. Something like that.

    Sadly I find properties to be the opposite of interfaces. They seem so useful in theory, but in practice end up not saving that much work. They are glorified get and set functions elevated to being language features, when they could actually be proper modifiers to field properties.
     
  29. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,562
    You did miss the point. The marker bit was I'm reference for cases of injecting dependencies, perfectly acceptable and not related to unity. But the true power was to allow for polymorphism without requiring items to implement a common base class or even any common functionality.
     
    sluice, Kiwasi and Master-Frog like this.
  30. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    I think you're a little ahead of me on this one.
     
  31. pKallv

    pKallv

    Joined:
    Mar 2, 2014
    Posts:
    772
    As I am pretty new to C# i have not used interfaces as i did not know about them. After reading this thread i have been looking around for a plain english explanation of why and how to use them that is not really code based but not found anything useful. May have been looking on the wrong sides. It sounds very good but i am afraid i still do not really understand it.

    Would someone please explain why I should use them and how to use them without code examples?
     
  32. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    I thought I did that. Look for the post with the microwave.

    It's based on the concept that "objects" have "jobs". So, like a microwave "object" has the job of heating things up. So, you know how to use your microwave, right? Well, you can use pretty much any microwave as long as the interface is the same. Meaning it has a keypad, a start button, a handle to open the door, etc.

    What would suck is if you had to know how which circuits in the microwave to trigger to make it work, and you had to build your own controller just to use it...

    Well, interfaces are the same idea. When something has an interface, they have certain methods you can call... this is enforced by the compiler, if an object wants to have an interface, it has to have certain methods. So, you know that its reliable to a degree. Think of those like buttons on the outside. The advantage to this is that any code that interacts with your object will do it the exact same way, so if you have to change the way your object works internally, it won't effect the function of anything else as long as the methods do what they say they do.

    If you've ever written a program > 1,000 lines of code, you will understand how making changes in one place can cause little errors in other places. It sucks, but good practices can help avoid these issues.

    Much the same, two completely different objects can have the same interface. So a really crappy little microwave and a mega powerful microwave have the same controls and do the same basic thing, so they have the same interface.

    I hope that helps you get a grasp of the concept.

    I've been told that using interfaces is the way the pros do it, as well. So it's definitely worth learning.
     
    Last edited: Jul 23, 2015
    GibTreaty and GarBenjamin like this.
  33. Manny Calavera

    Manny Calavera

    Joined:
    Oct 19, 2011
    Posts:
    205
    Something I would really like to do:

    Code (CSharp):
    1. public IWeapon Weapon;
    And then be able to see a Weapon slot in the inspector and drag 'n drop a gameobject that implements IWeapon into that slot.
     
  34. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    You CAN create
    Code (CSharp):
    1. Weapon : MonoBehaviour
    And then you CAN do exactly that.

    Make a GameObject and add a Weapon script to it. Then you can drag that GameObject into that slot.

    Plus this way on your Weapon you can have common properties like BaseDamage and Durability and you can actually set those in the editor. Make it a prefab, then while you're running the game you can edit the prefab, any new objects spawned will have the prefab settings, enabling editing during playtesting. I did this crap a lot when I was making my game, that's how I was able to get it done without going insane.

    You could even have a little developer debug hotkey that tells weapon to basically destroy and reinstantiate at runtime, this way you can test stuff live. You don't think about how many seconds you waste testing, running to the same spot, respawning that monster, etc. But it adds up!

    Unity is your friend. :)
     
  35. Manny Calavera

    Manny Calavera

    Joined:
    Oct 19, 2011
    Posts:
    205
    Classes and Interfaces are not the same thing.
     
  36. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    11,981
    That's really not the same, though. That creates a dependency on a class, rather than on an interface.

    I'll take a crack. An interface describes things that implementing classes have in common. This lets you manage them based on those common aspects instead of based on their types.

    For example, lets say that you have Cars and Grenades and Barrels. They all have an Explode method, and you want to explode everything in your level at once.

    One way to do this would be to individually look up every Car and call Explode(), then every Grenade and call Explode(), and then ever Barrel and call Explode(). If you add a 4th thing that can explode, you then need to go back to that bit of your code and add another bit that looks all of those up and calls Explode() on them too.

    Or, you can have them all implement IExplodable. Then you only have to find all IExplodables in your level and call Explode on those. Doesn't matter if they're Cars or Grenades or Barrels or SpaceShips. If you add another type that implements IExplodable then that'll explode too, with no code changes anywhere else. Because you're not looking things up based on the type any more, you're looking them up based on the exploding that they have in common, which you've represented as IExplodable.
     
    sluice, Slyder, Kiwasi and 1 other person like this.
  37. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    Thanks for clearing that up, I had no idea.
     
  38. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,451
    I'll have a go as well. Here is how I conceptualise things in my head.
    • Inheritance: Represents an "is a" relationship. A gun is a weapon. A bullet is a projectile. A MonoBehaviour is a Component.
    • Interfaces: Represent a "does" relationship. An array and a dictionary both do IEnumerable. A gun does IFirable. A wall and a character both do IDamagable.
    • Components: Represent a "has a" relationship. This is a special unity one.
    Interfaces should be used whenever you have classes that do the same thing, but in totally different ways. Both a wall and a character might take damage. But the damage is processed in completely different ways.
    OOP purists go one step further. In pure OOP terms, a class should not have any public methods or properties that are not part of an interface (and a class should have no public fields). This is where the whole "contract on functionality" thing comes in. But this is only worth worrying about if you are building a massive project or have multiple coders working on your code base.
     
    sluice and Taz-dragon like this.
  39. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    You can get the same end result if you write the code correctly. I'm not sure what advantage using interfaces with the unity editor presents, either, since what the editor does is specifically present you an interface for modifying an object's properties

    an interface for the interface? maybe I just don't see it
     
    Last edited: Jul 23, 2015
  40. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,451
    @angrypenguin probably has the best use case for it. If you have multiple different types of object that can take explosion damage, and multiple types of object that can be healed. And some of these objects can be healed and exploded, some can only be exploded, others only healed. The most efficient structure to handle this is an IExplodable and an IHealable interface.

    Native support for serialising interfaces would be great. Its right up on my list of things I would love to see done, right next to nested prefabs.

    You can hack it with OnValidate. Simply expose a component, check it implements the correct interface in OnValidate, and remove it if it doesn't. Its not a great system though. In most cases I tend to use an abstract base class to get the effect. This of course has severe limitations on your general inheritance chain.

    I believe there are some third party systems out there that let you serialise more stuff.
     
    Manny Calavera likes this.
  41. Manny Calavera

    Manny Calavera

    Joined:
    Oct 19, 2011
    Posts:
    205
    Code (CSharp):
    1. public class Weapon : MonoBehaviour {}
    2. ...
    3. public Weapon Weapon;
    With the above you are exposing the entire class to the script, including all public properties and methods from parent classes. The script could, for instance, say this.Weapon.enable = false.

    With an interface you only reveal to the consumer code what is absolutely necessary, i.e, this.Weapon.DamageAmount. It's cleaner, loosely coupled and easier to maintain.
     
    Kiwasi likes this.
  42. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    I just created a generic "actor" class that everything in the scene had to inherit from, and then I knew what methods everything had. So I could just add script components to do the same thing. One of my favorites was a "SelfDestruct" script that triggers after n seconds, easy way to take care of one-off things. I had other similar things, they relied on knowing that the object I put them on was of a certain class.

    You might think it's ridiculous to have a class that everything inherits from... but then, of course, there's MonoBehaviour which everything inherits from. So. I just extended the same concept.

    I can see the idea of using IThis and IThat with Unity but in practice, not sure where the big advantage shows up over the way I am doing it.

    I can add any component I desire, even one I make up on the fly... and then that component is just another script that I can apply to any object I want. And it works with multi-select editing as well.
     
  43. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    In theory. However, Unity isn't really about inheritance. It's really all about components. Add, remove, lots of generics... Unity itself has a lot of inherent coupling, think of the act of making a prefab spawn another prefab, such as a particle effect upon its destruction. This very act is tightly coupling two objects. It works well visually, though, so you can get done with your development quickly. Some people hate the way Unity encourages you to do that. Especially old timer programmers.

    One object for all the interactive, independent objects in the screen works very well, because it contains hooks for anything you want to add. Everything could receive collisions, everything could have HP, it all depends... you never know when you suddenly want a wall to be able to take damage, well I just tick off "Can be damaged" and give it an HP value, tick off "destroy at 0 hp"... it's a pretty involved deal but it works well and it's fast to assemble things

    and the answer is, it's fine, more efficient than you think... because if you're like me your first question is how does that affect performance.
     
  44. Roni92pl

    Roni92pl

    Joined:
    Jun 2, 2015
    Posts:
    262
    Nice example. But the funny thing is that you can do the same with just inheritance.
     
  45. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    Not multiple inheritance, though. :)
    Not in C#
     
    Dustin-Horne likes this.
  46. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,451
    Not really. The inheritance structure can get really messy if you want to do multiple different combinations of each interface.

    Different tools for different jobs. Inheritance is about extending the functionality of an existing class. Components are about creating little reusable modules. And interfaces are about decoupling concrete classes from one another.
     
    tango209 and Master-Frog like this.
  47. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,297
    I'm all about the modules, man.
     
  48. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,562
    No, no you can't. You can implement multiple interfaces but you cannot inherit multiple classes. The fun part however is that interfaces can also implement interfaces, so I could have implemented IInventoryItem on each of the other interfaces and not had to implement it on a class because it would have been there implicitly.
     
    angrypenguin, tango209 and sluice like this.
  49. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,451
    I wasn't aware of that. Now I'll have to make up a project where it makes sense to have multiple layers of interfaces implementing each other.
     
    Dustin-Horne likes this.
  50. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    A Door is an Actor
    An Item is an Actor but not a Door
    A Car is an Actor but not a Door or a Car.
    Another Player is an Actor but none of the above.

    Lets say I want to raycast and "Use()" whatever object is usable, but they all "Use()" in a different way. You will end up with a massive blob of code in your Actor class to handle this or you will end up with a nightmare of if(blah is Door) type checks without interfaces. You will also have to extend "Use()" through the entire hierarchy of Actors (at least an abstract declaration) so that Use() can be handled on any Actor.

    OR

    You can create an Interface IUse and implement it only on the Actors that are usable. This decouples the calling code from the "Use()" functionality and is far easier to expand and maintain.
     
    Manny Calavera likes this.