1
\$\begingroup\$

Here is an example:

enter image description here

The left enemy robot is closer than the right enemy robot, but I want the character to attack the robot that is closest to him on his right side. I can't use Physic.SphereCast or anything of that sort because it will just find the closest enemy on both sides. How can I make this happen?

\$\endgroup\$
  • \$\begingroup\$ Is this for 2D or 3D? \$\endgroup\$ – Evorlor Jun 25 '16 at 3:17
  • \$\begingroup\$ @Evorlor It's 2D \$\endgroup\$ – Milen Jun 25 '16 at 5:42
6
\$\begingroup\$

The best way to do this depends on a few things. I'm going to assume the following - if any of these are incorrect, please let me know and I'll update my answer:

  • You want the closest enemy that is anywhere to the right of the player within a certain range, even if it is at a different height
  • You want true distance, not just the one that is closest in horizontal positioning (eg an enemy one unit to the right is closer than an enemy one unit to the right and one unit above)

Here's what I'd suggest as a solution:

using UnityEngine;
using System.Linq;
using System.Collections.Generic;

public class YourBotClass : MonoBehaviour {

    float sightRange = 15;

    //
    // Your class code here
    //

    Collider2D GetClosestEnemy(bool facingRight) {
        Vector2 pos = transform.position;
        IEnumerable<Collider2D> validTargets;

        // Get all targets in range
        // Note:  You'll probably want to add a layermask to the OverlapCircleAll call
        var targets = Physics2D.OverlapCircleAll(pos, sightRange);

        if (facingRight) {
            // Filter results to only include targets to the right                
            validTargets = targets.Where(coll => coll.transform.position.x >= pos.x);
        } else {
            // Filter results to only include targets to the left
            validTargets = targets.Where(coll => coll.transform.position.x <= pos.x);
        }

        // I've broken the return statement into multiple lines for easier readability
        return validTargets
            // Sort targets by distance
            .OrderBy(coll => Vector2.Distance(pos, coll.transform.position))
            // Return first result, or null if no valid targets
            .FirstOrDefault();

    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ might aswell mark this as the answer, cause its a very good answer. \$\endgroup\$ – Daniel Netzer Jun 25 '16 at 7:30
  • \$\begingroup\$ My answer resolved my problem in my case, since the AI in my game always knows on what side of the player it is, but this works in all cases so I'm making this the accepted answer \$\endgroup\$ – Milen Jun 25 '16 at 14:21
2
\$\begingroup\$

All the left enemy are in lower value in x-axis position. Objects on right side are on higher value. Subtract main character's x value from enemy's. Positive results are for enemies at right side.

\$\endgroup\$
  • 1
    \$\begingroup\$ What if enemy is not facing right? Incomplete answer \$\endgroup\$ – Evorlor Jun 25 '16 at 3:15
1
\$\begingroup\$

Assuming your enemies all have colliders, could you not simply fire a 2D Raycast https://docs.unity3d.com/ScriptReference/Physics2D.Raycast.html in a straight line to the right of your player and return the GameObject of the first collider it hits?

\$\endgroup\$
0
\$\begingroup\$

Made it work by assigning a "Left Side" tag to the enemies on the left and a "Right Side" tag to the enemies on the right.

After that I find the closest enemy on a particular side by checking the distance from the enemy to the player and returning the closest one:

 private GameObject FindClosestEnemy(string side) {
        GameObject[] enemies = GameObject.FindGameObjectsWithTag(side);
        GameObject closestEnemy = null;
        var distance = Mathf.Infinity;
        var playerPos = transform.position;

        foreach (var enemy in enemies) {
            var diff = enemy.transform.position - playerPos;
            var currDistance = diff.sqrMagnitude;

            if (currDistance < distance) {
                closestEnemy = enemy;
                distance = currDistance;
            }
        }

        return closestEnemy;
    }
\$\endgroup\$
  • \$\begingroup\$ This doesn't work properly while the player moves trough the level. \$\endgroup\$ – Tyyppi_77 Jun 25 '16 at 7:01
  • \$\begingroup\$ Well it's gonna move always no matter what you do. So add a bool check when you move to the enemy with Update/Coroutine. If the character position is similar to the enemy position, stop the Update/Coroutine \$\endgroup\$ – Milen Jun 25 '16 at 12:51
  • \$\begingroup\$ You are not getting the point. If I tag an enemy to be on the right side of the player, and then move the player past the enemy, the enemy is now on the player's right side. This approach simply doesn't work, and a boolean won't fix it. \$\endgroup\$ – Tyyppi_77 Jun 25 '16 at 13:53
  • \$\begingroup\$ The enemies don't move to the other side in my game, so I didn't think about that. You're right though \$\endgroup\$ – Milen Jun 25 '16 at 13:55
  • \$\begingroup\$ Does the player not move either? \$\endgroup\$ – Tyyppi_77 Jun 25 '16 at 13:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.