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

Best way to implement an Attack System? (Create different attacks and make them collectible)

Discussion in 'Scripting' started by moosefreak, Dec 21, 2017.

  1. moosefreak

    moosefreak

    Joined:
    Mar 4, 2015
    Posts:
    4
    I've been struggling to come up with a system that allows me to easily create many different attacks in my grid-based game. (Long range, close range, multi-tile wide, projectile, etc) I've tried using Interfaces, Composition, Inheritance and so on but everything seems so much more confusing than I expected. There's no way to run coroutines in scriptableobjects so I can't use them for attack logic and I can't store gameobjects/references in a list without keeping them all in the scene.

    I know that I can't just make one class that can hold everything an attack can do, so I'd like to use composition for this. But that confuses me a lot: how do you store the composed GameObject and have a collectible version for the over-world to store in an arsenal/list and a usable version for battle?

    Sorry if I come off as confusing, my brain is just struggling to wrap itself around this problem. Are there best practices for this sort of thing?
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Well you're describing 2 different problems here. 1 being how to have a collection of attacks, and the other is how to have a way to have a collectable that adds such an attack to some collection.

    Note the collectable doesn't necessarily have to be the same thing that performs the attack.

    You could for instance have a collectable that stores data that describes what attack aught to be added. Not saying this is the best choice, just saying it's a plausible choice.

    With that said... you could make them one and the same anyways for convenience sake. You could use ScriptableObject for this.

    Note you say:
    Sure you can, you just need to hand the attack logic a MonoBehaviour as a handle with which you perform said attack.

    Lets say for hypothetical design we go with ScriptableObject, that has an interface. You might set it up something like this (this is all just psuedo code):

    Code (csharp):
    1.  
    2. public interface IAttackLogic
    3. {
    4.  
    5.     void PerformAttack(MonoBehaviour handle);
    6.  
    7. }
    8.  
    9. public class AOEAttack : ScriptableObject, IAttackLogic
    10. {
    11.  
    12.     public float Radius;
    13.     public float Range;
    14.  
    15.     public void PerformAttack(MonoBehaviour handle)
    16.     {
    17.         handle.StartCoroutine(this.DoAttack());
    18.     }
    19.  
    20.     private IEnumerator DoAttack()
    21.     {
    22.         //do attack logic
    23.     }
    24.  
    25. }
    26.  
    27. public class AttackManager : MonoBehaviour
    28. {
    29.  
    30.     public IAttackLogic Attack;
    31.  
    32.     void Update()
    33.     {
    34.         if(Input.GetButtonDown("Attack"))
    35.         {
    36.             this.Attack.PerformAttack(this);
    37.         }
    38.     }
    39.  
    40.  
    41. }
    42.  
    Note you get coroutines where you need them.

    Also it's not tethered to being a ScriptableObject, the logic just needs to implement IAttackLogic.

    If you wanted you could also type 'handle' as AttackManager to be more specific, so the logic can get more information.

    Heck, you could make an 'IAttackManager' interface so that logic/manager are completely independent of one another.
     
    dani1996dani likes this.
  3. moosefreak

    moosefreak

    Joined:
    Mar 4, 2015
    Posts:
    4
    Oh thanks so much for the detailed writeup. Can you elaborate a little on what you mean by the above? I think this is a great approach, but won't I have to duplicate code in each of my attack classes for PerformAttack()?
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    No you shouldn't have to duplicate code.

    You'd create a single attack logic class for each kind of attack. A kind of attack meaning a unique implementation of 'PerformAttack'.

    For instance a 'RangeAttack' and an 'AOEAttack' are distinctly different. Since one is well... a range (like bow&arrow, gun, etc), and the other an AOE (like a firebomb, earthquake, etc).

    Any given AOE or Range or other attack... say the difference between a firebomb and an earthquake AOE is not a difference in implementation, but a difference in the parameters for them. Which is what the fields/properties I showed in my example are. You can different AOE's that have different Range's and Radius's.

    If you need different animations. Then you could have an 'anim' property that defines what anim gets played for it. You might also have an AudioClip for the sound that gets played, so on and so forth. Note this animation/audio/etc stuff could also be abstracted into its own stuff independent of the IAttackLogic as well. So you might have 'IAttackLogic' (the logical code of an attack) and 'IAttackFX' (the visual/audio fx of an attack).

    So yeah... no code duplication.

    ...

    As for getting more specific than this...

    Ummmmmmm

    That to me sounds like your very novice to these concepts of composition, interfaces/contracts, etc. I'm honestly not sure where to start in that case since I don't know your experience level.
     
  5. moosefreak

    moosefreak

    Joined:
    Mar 4, 2015
    Posts:
    4
    I understand what you're saying, and I am fairly new to the world of composition, I've mostly programmed in OOP, and have been trying to wrap my brain around Unity's way of doing things. I tend to hit walls when I attempt to abide by them.

    Thank you again for the guidance, you have no idea how much help you have been