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

Feature Request Suggestion for Combat System

Discussion in 'Scripting' started by cheleRV15, Aug 5, 2023.

  1. cheleRV15

    cheleRV15

    Joined:
    Aug 19, 2021
    Posts:
    4
    Hi,

    I am doing 2D videogame project. In this game I want to implement a simple combat system that consists of a light attack, heavy attack, and a ultimate. Each attack will have a different type of element like fire, water, etc.
    What I want to make in my game is that when pressing "q" for example it will change the element and with that it will change the attacks that you have. What I mean is that you will have a set of attacks for each element that change depending what element you select. Does anyone have a suggestion where to start or a tutorial that can be useful as a guide. This is what I have right now.



    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PlayerAttacks : MonoBehaviour
    6. {
    7.     private bool isAttacking;
    8.  
    9.     // Start is called before the first frame update
    10.     void Start()
    11.     {
    12.        
    13.     }
    14.  
    15.     // Update is called once per frame
    16.     void Update()
    17.     {
    18.         if (isAttacking)
    19.         {
    20.             return;
    21.         }
    22.  
    23.         if (Input.GetKey(KeyCode.Mouse0))
    24.         {
    25.             LightAttack();
    26.         }
    27.  
    28.         else if (Input.GetKey(KeyCode.Mouse1))
    29.         {
    30.             HeavyAttack();
    31.         }
    32.  
    33.         else if (Input.GetKeyDown(KeyCode.R))
    34.         {
    35.             Ultimate();
    36.         }
    37.  
    38.     }
    39.  
    40.     void LightAttack()
    41.     {
    42.         if (!isAttacking)
    43.         {
    44.             isAttacking = true;
    45.             Debug.Log("Light attack");
    46.         }
    47.         isAttacking = false;
    48.     }
    49.  
    50.     void HeavyAttack()
    51.     {
    52.         if (!isAttacking)
    53.         {
    54.             isAttacking = true;
    55.             Debug.Log("Heavy attack");
    56.         }
    57.         isAttacking = false;
    58.     }
    59.  
    60.     void Ultimate()
    61.     {
    62.         if (!isAttacking)
    63.         {
    64.             isAttacking = true;
    65.             Debug.Log("Ultimate attack");
    66.         }
    67.         isAttacking = false;
    68.     }
    69. }
    70.  
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    Pick a popular existing game that has a mechanic similar to what you contemplate.

    Go to Youtube and look for tutorials for making it.

    Alternatively, step towards it by constantly asking "Can I ...?" like this guy:

    Imphenzia: How Did I Learn To Make Games:



    Tutorials and example code are great, but keep this in mind to maximize your success and minimize your frustration:

    How to do tutorials properly, two (2) simple steps to success:

    Step 1. Follow the tutorial and do every single step of the tutorial 100% precisely the way it is shown. Even the slightest deviation (even a single character!) generally ends in disaster. That's how software engineering works. Every step must be taken, every single letter must be spelled, capitalized, punctuated and spaced (or not spaced) properly, literally NOTHING can be omitted or skipped.

    Fortunately this is the easiest part to get right: Be a robot. Don't make any mistakes.
    BE PERFECT IN EVERYTHING YOU DO HERE!!


    If you get any errors, learn how to read the error code and fix your error. Google is your friend here. Do NOT continue until you fix your error. Your error will probably be somewhere near the parenthesis numbers (line and character position) in the file. It is almost CERTAINLY your typo causing the error, so look again and fix it.

    Step 2. Go back and work through every part of the tutorial again, and this time explain it to your doggie. See how I am doing that in my avatar picture? If you have no dog, explain it to your house plant. If you are unable to explain any part of it, STOP. DO NOT PROCEED. Now go learn how that part works. Read the documentation on the functions involved. Go back to the tutorial and try to figure out WHY they did that. This is the part that takes a LOT of time when you are new. It might take days or weeks to work through a single 5-minute tutorial. Stick with it. You will learn.

    Step 2 is the part everybody seems to miss. Without Step 2 you are simply a code-typing monkey and outside of the specific tutorial you did, you will be completely lost. If you want to learn, you MUST do Step 2.

    Of course, all this presupposes no errors in the tutorial. For certain tutorial makers (like Unity, Brackeys, Imphenzia, Sebastian Lague) this is usually the case. For some other less-well-known content creators, this is less true. Read the comments on the video: did anyone have issues like you did? If there's an error, you will NEVER be the first guy to find it.

    Beyond that, Step 3, 4, 5 and 6 become easy because you already understand!

    Finally, when you have errors, don't post here... just go fix your errors! Here's how:

    Remember: NOBODY here memorizes error codes. That's not a thing. The error code is absolutely the least useful part of the error. It serves no purpose at all. Forget the error code. Put it out of your mind.

    The complete error message contains everything you need to know to fix the error yourself.

    The important parts of the error message are:

    - the description of the error itself (google this; you are NEVER the first one!)
    - the file it occurred in (critical!)
    - the line number and character position (the two numbers in parentheses)
    - also possibly useful is the stack trace (all the lines of text in the lower console window)

    Always start with the FIRST error in the console window, as sometimes that error causes or compounds some or all of the subsequent errors. Often the error will be immediately prior to the indicated line, so make sure to check there as well.

    Look in the documentation. Every API you attempt to use is probably documented somewhere. Are you using it correctly? Are you spelling it correctly?

    All of that information is in the actual error message and you must pay attention to it. Learn how to identify it instantly so you don't have to stop your progress and fiddle around with the forum.
     
  3. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    FYI: I removed the 2D physics and documentation tags as neither are appropriate.
     
  4. Chubzdoomer

    Chubzdoomer

    Joined:
    Sep 27, 2014
    Posts:
    105
    I spent some time thinking about this at work today and here's the solution I came up with:
    https://www.mediafire.com/file/pi01cvgid011kgp/Elemental_Combat_System_Example.unitypackage/file

    (To use the package, just start a new Unity project and then go to Assets->Import Package.)

    I ultimately chose to use ScriptableObjects and take a Strategy Pattern-esque approach.

    Here's the entire Player script:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Player : MonoBehaviour
    4. {
    5.     [SerializeField] ElementSO[] elements;
    6.  
    7.     int currentIndex = 0;
    8.  
    9.     void Update()
    10.     {
    11.         if (Input.GetKeyDown(KeyCode.Q))
    12.             ChangeElement();
    13.  
    14.         if (Input.GetKeyDown(KeyCode.Mouse0))
    15.             elements[currentIndex].PerformLightAttack();
    16.  
    17.         if (Input.GetKeyDown(KeyCode.Mouse1))
    18.             elements[currentIndex].PerformHeavyAttack();
    19.  
    20.         if (Input.GetKeyDown(KeyCode.R))
    21.             elements[currentIndex].PerformUltimate();
    22.     }
    23.  
    24.     void ChangeElement()
    25.     {
    26.         currentIndex++;
    27.  
    28.         if (currentIndex >= elements.Length)
    29.             currentIndex = 0;
    30.  
    31.         Debug.Log("Changed element to " + elements[currentIndex].InGameName + "!");
    32.     }
    33. }
    There are actually four different elements in my example--Fire, Ice, Electricity, and Water--but you would never know that from the Player class above because there isn't a single specific element referenced. That's by design! All of the juicy details about what each element does (and even is) are stored inside each "Element" ScriptableObject.

    Here is the base Element ScriptableObject class:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public abstract class ElementSO : ScriptableObject
    4. {
    5.     [field: SerializeField] public string InGameName { get; private set; }
    6.  
    7.     public abstract void PerformLightAttack();
    8.     public abstract void PerformHeavyAttack();
    9.     public abstract void PerformUltimate();
    10. }
    This was also kept very simple--again, by design. The intention is for you to create a brand new class that derives from this one, and then have it implement the actual functionality for Light Attack, Heavy Attack, etc.

    Just as an example, here is the derived Fire Element ScriptableObject containing the actual functionality:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. [CreateAssetMenu(menuName = "Elements/Fire Element")]
    4. public class FireElementSO : ElementSO
    5. {
    6.     public override void PerformLightAttack()
    7.     {
    8.         Debug.Log("Launched a fireball!");
    9.     }
    10.  
    11.     public override void PerformHeavyAttack()
    12.     {
    13.         Debug.Log("Launched three fireballs!");
    14.     }
    15.  
    16.     public override void PerformUltimate()
    17.     {
    18.         Debug.Log("Summoned a firestorm!  It's raining fire!");
    19.     }
    20. }
    Now of course that is one very unexciting Fire element because all it's doing is displaying messages in the Console. In an actual project, however, this obviously wouldn't be the case. You would populate the PerformLightAttack() method with the actual code pertaining to a Light Attack being performed, and same for the Heavy Attack and Ultimate. This could involve playing sound effects, spawning projectiles, emitting particles, etc.

    The main idea is this:
    Inside the Player script, when LMB/RMB/R are pressed, the actual attack functionality is taken care of by whatever Element ScriptableObject the current index in that script happens to be pointing to.

    If you want to add a new element to the player's arsenal, all you have to do is write a new script for it that derives from ElementSO, create the ScriptableObject via the right-click Create menu, and then add it to the Player script's "elements" array. If you want to remove an element from the player's arsenal, all you have to do is remove it from the "elements" array in the Inspector.

    Notice that when it comes to adding/removing elements, you're never going inside the Player script itself and making changes. You're really only ever populating its "elements" array with new elements you'd like the player to have, or removing those you don't want him to have. This is what's referred to as the Open-Closed Principle, in which a class is "open for extension, but closed for modification," meaning you can extend a class's capabilities without ever needing to pop it open and cram more and more logic/lines of code inside it.

    Hopefully I didn't confuse you with any of this, and I really hope my example gets you on the right track. Just as an exercise, try creating your own fifth element (perhaps Earth?) and seeing if you can allow the player to switch to it (by adding it to the Player script's "elements" array). Also try removing an element or two to limit his arsenal. Just play around with it and try to get the hang of how it all comes together! Again, hope this helps and best of luck.
     
    Last edited: Aug 6, 2023
    cheleRV15 likes this.