Search Unity

3D Enemy Target Cycling - Method Similar to Dark Souls 3

Discussion in 'Scripting' started by erpatton, Feb 14, 2019.

  1. erpatton

    erpatton

    Joined:
    Oct 6, 2014
    Posts:
    55
    Hi everyone! I've already got a system in place for targeting enemies in my game, but I'm brainstorming a good way to cycle through them. Dark Souls 3 seems to have an intuitive (to me at least) system of switching through targets. From what I can tell, it seems to choose the target relative to the player's current target, so pressing right will target the closest enemy to the right of that enemy, pressing up will target the nearest enemy behind that enemy, etc.

    I was thinking that a possible way to do this could be to look at the enemies as 2D points on the XZ plane and then do some kind of 2D collision check starting from the current target and going in the direction pressed to find the closest target. If no target is found, it just stays on the current target. I made a gif of what I'm envisioning:



    I'm just trying to figure out the best way to do the collision checks (or whatever the most efficient way of finding an enemy in the direction pressed is) to find the next target. If I do just a raycast, then you could be off by just a tiny bit and miss a target in the direction you were trying to press. If I do something like a rectangle check in that direction, then if I make it too wide, it might catch a target that's unintended just because it's too close to the current target.

    Does anyone have any ideas of a good way to do this?
     
  2. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Well is the game 1st/3rd person? Can you pick left and right to cycle or back and forward too?

    For me I'd do this way:

    1) Get player to target world rotation.
    2) Get or already have all visible enemies on camera.
    3) Cycle through each on screen enemy and compare their 'player to enemy' rotation vs the current 'player to target' rotation, using the dot product, and checking whether left or right.
    4) Set the new target as the closest dot product on either the left or right, depending on which direction the player selected.

    Rinse and repeat.

    Here's some info about the dot product and using that to get the left/right info about it.

    https://docs.unity3d.com/ScriptReference/Vector3.Dot.html

    https://forum.unity.com/threads/che...e-left-or-right-side-of-forward-vector.88996/
    https://forum.unity.com/threads/left-right-test-function.31420/
     
  3. erpatton

    erpatton

    Joined:
    Oct 6, 2014
    Posts:
    55
    The game is in 3rd person. I want it to work like Dark Souls 3's target system where you can pick targets left, right, forward, and backward relative to the currently selected target.

    I've already got a list of all of the visible enemies on the screen, so that's one step done! I'll look into using the dot product and will just add all direction combinations instead of just left and right to see how that goes. Thanks for the advice!
     
  4. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Now I know you want back and forward too as well, you might want to play with something more similar to this. Where you get all the enemies positions in the player's local space, and then compare all of them to the current target, and see which is closest on either positive/negative of x or z to get the left/right/further/closer target.

    Code (CSharp):
    1.     // VERY UNTESTED CODE TO GIVE YOU AN IDEA - AS IN COMPLETELY UNTESTED.
    2.     public class EnemyInfo : MonoBehaviour
    3.     {
    4.         public Vector3 playerEquivPosition;
    5.     }
    6.  
    7.     public class Example : MonoBehaviour
    8.     {
    9.         public enum Direction { Left, Right, Closer, Further }
    10.  
    11.         [SerializeField] private List<EnemyInfo> enemies = new List<EnemyInfo>();
    12.         [SerializeField] private EnemyInfo target;
    13.  
    14.         public void Start()
    15.         {
    16.             // Example, but won't work without target already assigned.
    17.             target = GetNearestEnemyInDirection(target, Direction.Left);
    18.         }
    19.  
    20.         public EnemyInfo GetNearestEnemyInDirection(EnemyInfo currentTarget, Direction direction)
    21.         {
    22.             // Just setting it as current so should we find no one we keep the same target. Unless you wanna cycle the other side or something.
    23.             EnemyInfo potentialTarget = currentTarget;
    24.  
    25.             // Update their local positions before carrying on.
    26.             UpdateTargetLocals();
    27.  
    28.             if(direction == Direction.Left)
    29.             {
    30.                 // Get all the enemies on the left hand side.
    31.                 var enemiesOnLeft = enemies.FindAll(x => x.playerEquivPosition.x < currentTarget.playerEquivPosition.x);
    32.  
    33.                 potentialTarget = enemiesOnLeft[0];
    34.                 for (int i=0; i < enemiesOnLeft.Count; i++)
    35.                 {
    36.                     // Find the nearest enemy to the current one on the left.
    37.                     if(Mathf.Abs(potentialTarget.playerEquivPosition.x) > Mathf.Abs(enemies[i].playerEquivPosition.x))
    38.                     {
    39.                         // This one's closer to the current target.
    40.                         potentialTarget = enemies[i];
    41.                     }
    42.                 }
    43.             }
    44.  
    45.             // Not a finished product, but you get the idea.
    46.             return potentialTarget;
    47.         }
    48.  
    49.         private void UpdateTargetLocals()
    50.         {
    51.             // Assigns all the positions of enemies in the player's local space.
    52.             enemies.ForEach(x => x.playerEquivPosition = transform.InverseTransformPoint(x.transform.position));
    53.         }
    54.     }
    55.  
    You get all the enemies and get their location in the player's local space, via this method: https://docs.unity3d.com/ScriptReference/Transform.InverseTransformPoint.html. From there measuring the closest to the left/right/up/down, of the current target. As their positions will be relative to the player's forward trajectory, which might be cleaner and easier to have in your head as well.

    I'll leave either as an option. See what feels best.
     
    Last edited: Feb 14, 2019