Search Unity

[HELP]Best way to implement combat in a 2D Hack N' Slash game

Discussion in '2D' started by InkaTorque, Sep 30, 2014.

  1. InkaTorque

    InkaTorque

    Joined:
    Sep 4, 2014
    Posts:
    22
    Hi , I'm on my way to implement a 2d hack n slash game with tight but heavy combat (like Dark Souls or the original castlevanias) but I have problems deciding what is the best way to implement this type of combat , especifically the way I should check collisions. I have a lot of ideas :

    1)Resizing , on each frame , the bounds of the box collider attached to my character in order to match the witdh (or height) of the sprite being displayed , and then during the whole animation check if there was a collision with an enemy.

    2)Just play the animation without resizing my player's collision box;but , create another collision box when the animation starts and then move the collision box on each frame of the animation depending on the position of the weapon and then check if there was a collision

    3)Use animation events on specific frames of the attacking animation and check , with the help of raycasting and distance calculation , if an enemy was hit.

    I think most of the ideas I have are animation dependant , and I don't know if that is a good thing to have in a game specially so early on development (attack animations can change drastically during development time).

    I want is your opinion on what should i do with the collision detection of the combat system , keeping in mind that there will be multiple weapons with multiple attack animations. I hope you can help me . Thank you in advance
     
  2. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    The colliders in Unity have been a frustration to me ever since I started using Unity last winter. Why they would be tied to a GameObject and not the actual image (for 2D at least) makes no sense to me. I do not know how they work for 3D but I imagine it is likely they are still based on the geometry of the GameObject at the time the collider is added. It is very bizarre.

    The best way I have come up with so far is to create lists of colliders, one for each image, during the initialization phase of the game. Then each game object actually will have a ton of colliders on it, one for each possible image it can show, but only one will be active at any one time (the one corresponding to the current image being displayed).

    That is what I came up last winter. For my current game I am trying to just arrange some box colliders on each object in such a way to cover them as best as possible through all animation states. It is definitely not the best way of doing it. I've been thinking about overhauling the system and going back to the way I did it before.

    It would be interesting to hear from a Unity person how they expected the current collider systems to work with animated objects. I have always tied colliders to the images themselves never to the abstract game object itself. I hope at some point they overhaul their system and do the same. When you slice sprites, generate polygon colliders for each at that time.

    Maybe someone else who has been using Unity longer will share how they do it.
     
  3. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Not sure if this will help, but I just posted a reply over here about how I handle animated colliders in my own games.
     
  4. LiberLogic969

    LiberLogic969

    Joined:
    Jun 29, 2014
    Posts:
    138
    #3 sounds like the easiest approach in my opinion. You can easily add a single empty GameObject to your character and animate it to match each frames "point of contact", like the tip of a sword or the end of your characters extended fist. Then using animation events tell your script when to raycast from your players position to the animated empty GameObject and use layers to filter enemies (or objects you want to hit). You could also use circle casting for more Area of Effect type attacks. You could get a little more complex here by adding multiple objects that represent the start and end points of the RayCast, so instead of starting at the center of the player you would create an empty GameObject at the hilt of your sword and at the tip of the blade and animate those at each frame to line up properly. This is how im going to do melee attacks for my prototype after trying to mess with 2D Colliders... It can be a pain alot of the time.
     
  5. Nijaz314

    Nijaz314

    Joined:
    May 6, 2015
    Posts:
    1
    Could you go into any more detail on how this is done? I'm having a hard time implementing a system similar to this
     
  6. LiberLogic969

    LiberLogic969

    Joined:
    Jun 29, 2014
    Posts:
    138
    In each of your attack animations you'll need to animate 2 empty GameObjects (named something like HitStart and HitEnd) to line up with the animation. These two objects are used to determine the position, direction and distance of the Raycast2D. for example if one of your attacks is a sword slash you should animate the two empty GameObjects so the "start" object is at the hilt of the sword for every frame and the "end" object is at the tip of the sword for every frame... Then you will need a script to control raycasting, something like this :

    Code (CSharp):
    1. public class PlayerAttack : MonoBehaviour
    2. {
    3.     public float Damage;
    4.     public LayerMask EnemyLayer;
    5.     public GameObject HitStart;
    6.     public GameObject HitEnd;
    7.  
    8.     private bool doHit;
    9.  
    10.     void Start()
    11.     {
    12.         doHit = false;
    13.     }
    14.     void Update()
    15.     {
    16.         if (doHit)
    17.         {
    18.             Vector3 hitDir = HitEnd.transform.position - HitStart.transform.position;
    19.             float hitDistance = hitDir.magnitude;
    20.  
    21.             hitDir.Normalize();
    22.  
    23.             RaycastHit2D hit = Physics2D.Raycast(HitStart.transform.position, hitDir, hitDistance, EnemyLayer);
    24.             if (hit)
    25.             {
    26.                 Enemy e = hit.collider.gameObject.GetComponent<Enemy>();
    27.                 e.DamageEntity(Damage);
    28.             }
    29.         }
    30.     }
    31.  
    32.     public void AttackStart()
    33.     {
    34.         doHit = true;
    35.     }
    36.     public void AttackEnd()
    37.     {
    38.         doHit = false;
    39.     }
    40. }
    The AttackStart and End methods are going to be animation events. In each of your attacks animations add an Animation Event at the first frame you want to start detecting collisions (set to AttackStart()) and another Animation Event at the last frame you want collision detection (set to AttackEnd()). And of course you will need to set the LayerMask to an appropriate value, and place a reference of the two empty Hit objects in your player into the fields in the inspector. Hope this helped you out :)