Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Using interfaces for "dealing damage"

Discussion in 'Scripting' started by unity_LfADgBYmxPAQnQ, Dec 30, 2020.

  1. unity_LfADgBYmxPAQnQ

    unity_LfADgBYmxPAQnQ

    Joined:
    Nov 16, 2019
    Posts:
    5
    I am fairly new to unity (and programming in general) and started building a small game/demo for android.
    I have been following a tutorial to implement a damage system which contains hitbox-detection (via overlap)
    .
    In the last step an interface is implemented and I thought for a whole while that this interface is for all the objects that can be hit by the hitbox and receive damage. But after a lot of trouble I finally understood, that this interface is for those classes that use the hitbox.

    After realising that, I still didnt know how to tell the enemies that were hit by the hitbox to take damage. It seemed intuitive for me to use an interface for that but I could not make it work. After one day of trying and reading about interfaces I gave up and went like this:

    For context:
    Code (CSharp):
    1. public class Attack              
    2. {
    3.     public bool success = false;                                   //Some enemie hit
    4.     public Collider2D[] targets = new Collider2D[5];  
    5.     public int hitEnemies;                                              //how many enemies are hit?
    6. }

    in the managerscript:

    Code (CSharp):
    1.             for (int i = 0; i < playerAttack.hitEnemies; i++)
    2.             {
    3.                 Collider2D aCollider = playerAttack.targets[i];
    4.                 aCollider.GetComponent<Enemy>().takeDamage(10);
    5.             }
    This solution works. For now there only is one enemy, so thats no problem.
    But will this be a problem if there are more enemies? By my understanding every future enemy I plan on implementing would need to have the same <Enemy> - script as a component to reuse my hitbox-script. But what if I want future enemies to move differently for example? Is the solution to use different scripts on my gameObjects (in this case enemies): One for receiving damage, one for moving etc. ?
    Lets say I have my enemy and he has the "Moving" script and the "hurtbox" script. Future enemies would use the hurtbox-script as well then but would need different values for health, defense maybe... So the solutions of a interface seems good: every hurtbox of the different enemies will just have to implement a common core and have its own other values which can still be different. Is that the "right" way?

    But how do I reference that common core in my manager then?

    I also read that "GetComponent" is quite expensive as a method (which is bad because I program the demo for mobile) and that one can use interfaces... But how? Im sorry that I cannot really be more precise about my questions regarding interfaces, I just cannot really wrap my head around them.


    For more context, the tutorial I followed is that one:
    https://www.gamasutra.com/blogs/NahuelGladstein/20180514/318031/Hitboxes_and_Hurtboxes_in_Unity.php
    I did not really understand the last parts in which the interface is implemented.


    Thanks alot in advance for your time and help
     
  2. dani-unity-dev

    dani-unity-dev

    Joined:
    Feb 22, 2015
    Posts:
    174
    You can use GetComponent<IMyInterface> and it will just work. Do not worry about performance till it becomes a problem. You could have an interface called IDamageReceiver or something and have any number of scripts implementing it. Or you could just have a base class for all of them and use GetComponent<BaseClass>.
     
  3. MDADigital

    MDADigital

    Joined:
    Apr 18, 2020
    Posts:
    2,198
    You can have a lookup for example Dictionary<Collider, IMyInterface>.
     
  4. dani-unity-dev

    dani-unity-dev

    Joined:
    Feb 22, 2015
    Posts:
    174
    I wouldn't bother till it becomes an issue though. Always keep your code as simple as possible, saves you debugging time.
     
  5. unity_LfADgBYmxPAQnQ

    unity_LfADgBYmxPAQnQ

    Joined:
    Nov 16, 2019
    Posts:
    5
    In that case, would I have for example:

    IDamageReceiver {takeDamage(int)}

    gameObject enemy1
    with components: (script1: IDamageReceiver), (Script2)...

    gameObject enemy2
    with components: (script3: IDamageReceiver), (Script4)...

    and change my code to:

    Code (CSharp):
    1.                 for (int i = 0; i < playerAttack.hitEnemies; i++)
    2.                 {
    3.                     Collider2D aCollider = playerAttack.targets[i];
    4.                     aCollider.GetComponent<IDamageReceiver>().takeDamage(10);
    5.                 }
    6.  
    ?


    Thanks for the help and the tip not to worry too much about performance + I will look into Dictionarys as I have not worked with them before.
    I will try the ideas and come back here if new problems arise.
     
  6. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,887
    Interfaces are just a way to call methods on an unknown Type. It lets you not care if its an 'Enemy' or a 'Vehicle', so long as it's an IMove.

    You can use components in a similar way, except the component will have the same code for Enemy and Vehicle, so you will have to subclass it, or use events.
    • Move : MonoBehaviour
      • MoveThisWay : Move
      • MoveAnotherWay : Move
    Now if you attach the MoveThisWay component,
    GetComponent<Move>()
    will be able to use the subclasses' overridden movement function instead.

    Or Move can just call a
    UnityEvent<Vector3> OnMove
    event, then you can call any function from any component that has a Vector3 parameter.

    Basically, yes. This is a more compositional approach, but there are many ways to do it.
     
  7. dani-unity-dev

    dani-unity-dev

    Joined:
    Feb 22, 2015
    Posts:
    174
    Yes to everything. script1 and script3 in your example would be classes inheriting MonoBehaviour and implementing IDamageReceiver. When I can I prefer working with interfaces because they are easier to test and also make the code clearer IMHO.