Detection if player left or right of enemy

I am trying to write a script on the enemy, that will determine if the player is to the right, left, behind or in front of the enemy so I can do sprite changes. I thought doing a simple check of the players position and comparing it to the enemy pos would work, but the code just causes MORE to be printed to the console, and i cant figure out why it is doing such.

This is the code I am trying to use, I only have the left and right code set up, but like i said, it doesnt seem to be working properly. If anyone could point out why, or give a better way of doing this detection, it would be appreciated :slight_smile:

    public Sprite[] sprites;
    public Sprite[] spritesU;
    public float framesPerSecond;

    public Transform player;
    private Vector2 EnemyPos;

    private SpriteRenderer spriteRenderer;

    private void Start()
    {
       spriteRenderer = GetComponent<SpriteRenderer>();
    }
   

        private void Update()
    {
        PositionCheck();
        int index = (int)(Time.timeSinceLevelLoad * framesPerSecond);
        index = index % sprites.Length;
        spriteRenderer.sprite = sprites[index];
    }
    void PositionCheck()
    {
        if (player.position.x < EnemyPos.x)
        {
            Debug.Log("LESS");
        }
        else
       {
            Debug.Log("MORE");
        }
    }

}

Hi there.
All we need to do is to convert position from world to local space by using

transform.InverseTransformPoint(Vector3 position)

Vector3 position represents a player position in your case

If we are talking about 2D space, this is how it’s going to work.

Here comes a little bit of code:

using UnityEngine;

public class ExampleScript : MonoBehaviour {
   
    public Transform player;
    public Vector2 relativePoint;

    void Update () {
        relativePoint = transform.InverseTransformPoint(player.position);
        if(relativePoint.x < 0f && Mathf.Abs(relativePoint.x) > Mathf.Abs(relativePoint.y))
        {
            Debug.Log("Right");
        }
        if(relativePoint.x > 0f && Mathf.Abs(relativePoint.x) > Mathf.Abs(relativePoint.y))
        {
            Debug.Log("Left");
        }
        if(relativePoint.y > 0 && Mathf.Abs(relativePoint.x) < Mathf.Abs(relativePoint.y))
        {
            Debug.Log("Under");
        }
        if (relativePoint.y < 0 && Mathf.Abs(relativePoint.x) < Mathf.Abs(relativePoint.y))
        {
            Debug.Log("Above");
        }
    }
}
1 Like

that’s a lot of math functions when you can just calculate the vector between the two and check the sign of the two components of the vector… OP was on the right track to do so, just was working with their world position and not the relative vector.

//not real code :P
r = A.pos - B.pos
r.x < 0 left
r.x > 0 right
r.y < 0 up
r.y > 0 down

etc. (might be off, can never remember which coords 2d use but you get the idea).

1 Like

I see no code that actually assigns EnemyPos. If it is always (0,0) then of course “MORE” is printed. You have to reference your enemy and take its position jsut like you did for the player.

When your player does not rotate then you can continue like you did. Otherwise you need to make transformations as describred in the previous posts.

Indeed. Simply use ‘transform.position.x’ instead of EnemyPos.x

Right, it already is the enemy script :slight_smile:

Yep, looked twice before I posted :wink: heh.

I would solve this differently by using the dot product combined with some other defintions you might need.

First problem is that a trivial sequential check might short-circuit the evaluation…
Suppose you’re at P(0,0,0), first enemy is at E1(100, 0, 1), second is at E2(1, 0, 100).

If you tested front first, both would be mathematically in front of you even though you (as a human being) might rather say the first enemy is on your left. You’d need to combine and test everything and choose a result. In order to do that, you need priorities / a defintion for the subjective perception of the relative position.

If you isolate an axis and project any object onto this axis, for instance the local Z axis, any other object is always either in front of you, behind you or at the same position.

This problem does also exist when you work with angles instead, but it’s easier, shorter and cleaner to implement some additional logic.

Thanks a lot everyone, all your answers worked, very much appreciated :):slight_smile:

No problem. :slight_smile: Enjoy your game.

Sorry, hoping someone will be able to help with another issue im having with this. I used Williams code above (pasted below for easy reading) and i assigned my player prefab to the transform player. But the location of the player does not seem to update when the game in running. The only way I can get the enemy script to update the player position, is to run the game then assign the player from the hierarchy. Do i need to create a prefab object in the code below then use the transform from that?

using UnityEngine;
public class ExampleScript : MonoBehaviour {
  
    public Transform player;
    public Vector2 relativePoint;
    void Update () {
        relativePoint = transform.InverseTransformPoint(player.position);
        if(relativePoint.x < 0f && Mathf.Abs(relativePoint.x) > Mathf.Abs(relativePoint.y))
        {
            Debug.Log("Right");
        }
        if(relativePoint.x > 0f && Mathf.Abs(relativePoint.x) > Mathf.Abs(relativePoint.y))
        {
            Debug.Log("Left");
        }
        if(relativePoint.y > 0 && Mathf.Abs(relativePoint.x) < Mathf.Abs(relativePoint.y))
        {
            Debug.Log("Under");
        }
        if (relativePoint.y < 0 && Mathf.Abs(relativePoint.x) < Mathf.Abs(relativePoint.y))
        {
            Debug.Log("Above");
        }
    }
}

Is the enemy instantiated during game play? :slight_smile:

Yes, the enemy is instantiated during play. The player is already in the scene when the game starts

I think a decent solution is you make a reference to the player transform on the script that spawns your enemies, and then you pass the reference to the enemies that you spawn. :slight_smile:
If you need some help, ask. :slight_smile:

So I make the transform player, in the enemy spawning script and assign that the player prefab, How would I pass that though? Not sure if thats done withe the getComponent code? Thanks for the help :slight_smile:

Well, just so we don’t mix up our words, you should assign the player game object from the scene. That is important, as otherwise the reference won’t be valid for what you want.
Next, when you spawn the enemy, you use some code with Instantiate, right? That method returns the newly created object, so :

// somewhere outside of the method, you have:
[SerializeField]
Transform player;
public SpawnEnemy() {
   GameObject newEnemy = Instantiate(enemyPrefab);
   newEnemy.GetComponent<ExampleScript>().player = player; // here, we send them the valid reference
  }

Something like that. :slight_smile:

Sorry still having some issue with this.

So i create the gameObject player, should I put that in the player code, and assign the player from the hierarchy in there. Then do your code in the enemyspawning script. The enemy spawning script it different from the animation sprite script, which is where i have the code that checks where the player is so the sprite can be determined, should I also pass the player transform to that script too?

I am a little unsure of the explanation. I realize that the spawning script is different from the one checking the relative location. Is that what you meant?

Yes, the scripts are different. When I tried what you said, it didnt seem to work, so think i done something wrong.

In the player script it have simply

 public GameObject player;

Then the spawnerScript

Transform PlayerObject;


 void SpawnEnemy()
    {
if (!EnemyIsCreated)
        {
            var enemySpawnPoint = GameObject.Find("Rm_1_Spawner1").transform;
            SpawnedEnemy = Instantiate(Enemy, enemySpawnPoint.position, enemySpawnPoint.rotation) as GameObject;
            SpawnedEnemy.GetComponent<Player>().player = PlayerObject;
   
            EnemyCounter += 1;

            var enemySpawnPoint1 = GameObject.Find("Rm_1_Spawner2").transform;
            SpawnedEnemy1 = Instantiate(Enemy, enemySpawnPoint1.position, enemySpawnPoint1.rotation) as GameObject;
            EnemyCounter  += 1;

            EnemyIsCreated = true;
          
        }

Is this what you mean or am i misunderstanding?

Well, maybe I’m misunderstanding you. For instance, I do not know why your player script needs a reference to itself.
I thought we were trying to get a reference of the player to the enemy script :slight_smile:

If I got that wrong, please just explain a few simple things:

  • which script needs which information (eg: is the script on the enemy or player), and does it need to know about (enemy / player)?

Then, we should be able to work out any confusion.