Search Unity

Question How should i store / handle abilities

Discussion in 'Getting Started' started by Seekei, Dec 5, 2022.

  1. Seekei

    Seekei

    Joined:
    Dec 5, 2022
    Posts:
    3
    I'm currently making an ability system that uses a combination of inputs in order to use specific abilities. For example, if the user pressed "C", "V" and "B" in quick succession then a fireball would be shot. The issue I'm currently facing is that I'm not sure how to store the data of the abilities. I've tried using a list of dictionaries, I was unsure how to make it work. I'm now trying to use a dictionary that has the ability name as the key and the entered inputs (in string format) as the value. Currently, I'm adding these pairs via .Add() in the start method, for example jutsuDictionary.Add("Fireball","CVB"), however it seems wildly inefficient. I don't know if there is a better way of handling this data, should I make an object for each ability and use that somehow? The issue with every solution i have so far is the fact that every time the user enters a string of inputs the script with have to iterate through a dictionary of data with possibly hundreds of values in it. I'm relatively new to Unity, and have only recently started to take programming seriously and am unsure of the best ways of handling data like this. Sorry if this post doesn't provide enough information, this is my first post.

    Current Script:
    -Doesn't functionally do anything yet as I'm trying to figure out the best way to handle the ability data.
    Code (CSharp):
    1. [Header("Key Binds")]
    2.     [SerializeField] private List<KeyCode> inputsList = new List<KeyCode>() {KeyCode.X, KeyCode.C, KeyCode.V, KeyCode.B};
    3.  
    4.     [Header("General Data")]
    5.     [SerializeField] private List<KeyCode> storedInputs = new List<KeyCode>();
    6.     [SerializeField] private float inputEndTime = 0f;
    7.  
    8.     private Dictionary<string, string> jutsuDictionary = new Dictionary<string, string>();
    9.  
    10.     private void Start()
    11.     {
    12.         jutsuDictionary.Add("Fireball", "CVB");
    13.         jutsuDictionary.Add("Water Bullet", "XVC");
    14.         jutsuDictionary.Add("Electric Field", "BXV");
    15.         // Would be many more but only for testing
    16.     }
    17.  
    18.     void Update()
    19.     {
    20.         inputFunction();
    21.     }
    22.  
    23.     private void inputFunction()
    24.     {
    25.         bool keyPressed = false;
    26.         foreach (KeyCode input in inputsList)
    27.         {
    28.             if (Input.GetKeyDown(input))
    29.             {
    30.                 keyPressed = true;
    31.                 storedInputs.Add(input);
    32.                 inputEndTime = (float)(Time.time + 0.5);
    33.             }
    34.         }
    35.         if (keyPressed == false && storedInputs.Count > 0)
    36.         {
    37.             if (Time.time > inputEndTime)
    38.             {
    39.                 inputsToString(storedInputs);
    40.                 storedInputs = new List<KeyCode>();
    41.             }
    42.         }
    43.     }
    44.  
    45.     private void inputsToString(List<KeyCode> inputs)
    46.     {
    47.         string stringInputs = "";
    48.         foreach (KeyCode input in inputs)
    49.         {
    50.             stringInputs += input.ToString();
    51.         }
    52.         Debug.Log(stringInputs);
    53.     }
     
  2. tleylan

    tleylan

    Joined:
    Jun 17, 2020
    Posts:
    620
    It sounds like you should consider parsing as you go. You can't wait for the C, V and B to be entered to go look up the action. Consider if they press C, V, 8 and that isn't a valid combo. When they press C a subset of the available combos are all you need to consider. If they then press V then it is down to a very few. And when they press the B you execute it and if they press the 8 you cancel it and they start over.
     
  3. Seekei

    Seekei

    Joined:
    Dec 5, 2022
    Posts:
    3
    Thanks for the response ill try that out when I get the chance!
     
  4. AngryProgrammer

    AngryProgrammer

    Joined:
    Jun 4, 2019
    Posts:
    490
    Your approach is not wrong, but I suggest creating skills as separate components. This will keep your code clean. Consider this approach.
    1. You create the AbilityFireball script.
    2. In this class, you specify the key that triggers the skill, store parameters e.g. damage dealt, projectile speed generated, projectile prefab, etc.
    3. Drag and drop on the player object, disable AbilityFireball in the inspector if the skill is not to be available from the beginning.
    4. From the class you quoted above, you make a skill manager. So if the character has unlocked the ability to throw fireballs, you only do GetComponent<AbilityFireball>().enabled = true;
    5. At this point, your player character has become a list of skills that you can unlock or disable according to the rules of the game. If the component is inactive, the invoking key will not work, so redundant checks are eliminated.
     
  5. Seekei

    Seekei

    Joined:
    Dec 5, 2022
    Posts:
    3
    Thanks I never even thought of this tbh although one thing I have to bring up is that I plan on there being possibly hundreds of abilities. Would this still be viable or would I have to have some other script add the ability scripts that the player has unlocked to the player object to avoid having a lot of scripts on the player, or is this not that much of an issue? Also how would i trigger the ability script?
     
  6. AngryProgrammer

    AngryProgrammer

    Joined:
    Jun 4, 2019
    Posts:
    490
    1. The above example is more for a couple of skills. If you are planning hundreds, prefabs are an alternative.
    2. Consider how large number of skills are handled in other games. Most often there is some book with a list of skills. However, if you want to call them with a keyboard shortcut, there is usually a handy skill bar.
    3. So now instead of dragging the skill onto the player character, drag it to a separate game object. And then drag the entire object from the hierarchy to the Prefabs folder. I don't know if you've worked with prefabs in Unity before (a file with a blue square will be created).
    4. From your description, however, it seems that you are planning something more like in fighting games (e.g. Street Fighter) to trigger some combo from the keys.
    5. Whichever approach you choose, remember that you can store game objects. For example, the list of available skills could be List<GameObject> as a parameter of another component that will be responsible for managing skills. In the inspector then you just drag prefabs to this list, you can put hundreds like that.
    6. The rest of the work is mainly interconnecting the game objects with each other. If you are planning the approach from point 3, you create some game object that will be your skill bar (more work will be required) or if you prefer the approach from point 4, you call Instantiate, where in the parameters you specify the prefab from the list and where it is to be placed (including case on the player character).
    1. Every object in the game (including prefabs) have their own Update method. They work independently of each other. If in one object you specified that the A key runs something, and in the other object you specified the B key. You don't need to have the keyboard support in one place.
    2. The above rule applies to active objects. The prefab doesn't have to exist at the start of the game. Only when you drag the prefab to the hierarchy or add it via Instantiate (unlocking skills) then you gain the possibilities locked in this object.
    3. Sorry if I'm using too much simplification. You might as well code everything in one long method, but if you're using Unity, it's worth learning how to manipulate game objects. How to add something during the game that didn't exist before (skills, items, characters) and how to combine these objects in a hierarchy (what you see in the inspector).
    Edit:
    If you want to call the skill after a specific key combination, where the order matters, e.g. A + S + D, you can use the example below.

    Code (CSharp):
    1. void Update()
    2. {
    3.     if(Input.GetKey(KeyCode.A))
    4.     {
    5.         if(Input.GetKey(KeyCode.S) && Input.GetKeyDown(KeyCode.D))
    6.         {
    7.             Debug.Log("Fireball!");
    8.         }
    9.     }
    10. }
    At this point, if each skill is a separate prefab, I don't even need to check what skill it is. Because it's in a class that only handles Fireball.
     
    Last edited: Dec 9, 2022