Enemy AI problems (2D Platformer)

Hi all,

I’ve been working on this for way too long now and realise now my code is very messy having tried so many things in order to try and make this work. All I’ve been trying to do is make my enemy patrol on the platform, which it does fine, then when in range of the player to turn around and increase velocity towards the player but getting the enemy to actually turn around without the flip function being called constantly has been really hard. I had used a ray cast in the opposite direction to the way the enemy was facing but this didn’t seem to work and instead tried to just compare x positions. The difficulty is stopping the enemy from flipping when it is going for the player (having just flipped to change direction towards the player) while still being able to detect if the player has jumped over (as the enemy will need to flip again to pursue) . Here is all the code…

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyController : MonoBehaviour {
    public float velocity;
    public float attackVelocity;
    public GameObject EndOfPlatform;
    bool facingRight = true;
    bool inAttackRange = false;
    public int maxRange;
    public GameObject player;
    Vector3 playerPos;
    public bool correctDirection = true;
    public float preventMultipleCalls;
    void Flip()
    {
        facingRight = !facingRight;
        Vector3 theScale = transform.localScale;
        theScale.x *= -1;
        transform.localScale = theScale;
    }
    // Use this for initialization
    void Start () {
        playerPos = player.transform.position;
        preventMultipleCalls = 2;
        facingRight = true;
    }
    // Update is called once per frame
    void Update (){
        if (Vector3.Distance (transform.position, player.transform.position) > maxRange)
            inAttackRange = false;
        if (Vector3.Distance (transform.position, player.transform.position) <= maxRange)
            inAttackRange = true;
        if (inAttackRange == false) {
            if (facingRight == true){
                GetComponent<Rigidbody2D> ().velocity = new Vector2 (velocity, GetComponent<Rigidbody2D> ().velocity.y);   
            } if (facingRight != true){
                GetComponent<Rigidbody2D> ().velocity = new Vector2 (-velocity, GetComponent<Rigidbody2D> ().velocity.y);
            }
    }
    else if (inAttackRange == true) {
            if (preventMultipleCalls == 2) {
                if (facingRight == true) {
                    //correctDirection = Physics.Raycast (transform.position, transform.TransformDirection (Vector3.left), 12f);
                    if (transform.position.x < player.transform.position.x)
                        correctDirection = false;
                }
                if (facingRight == false){
                    if (transform.position.x > player.transform.position.x)
                    correctDirection = false;
                }
                    //correctDirection = Physics.Raycast (transform.position, transform.TransformDirection (Vector3.right), 12f);
                if (correctDirection == false) {
                    Flip ();
                }
                if (transform.position.x > player.transform.position.x)
                    preventMultipleCalls = 3;
                if (transform.position.x < player.transform.position.x)
                    preventMultipleCalls = 1;
            }
            if (transform.position.x > player.transform.position.x)
                preventMultipleCalls = 3;
            if (transform.position.x < player.transform.position.x)
                preventMultipleCalls = 1;
            if (facingRight == true) {
                GetComponent<Rigidbody2D> ().velocity = new Vector2 (attackVelocity, GetComponent<Rigidbody2D> ().velocity.y);    
            } if (facingRight != true) {
                GetComponent<Rigidbody2D> ().velocity = new Vector2 (-attackVelocity, GetComponent<Rigidbody2D> ().velocity.y);
            }
            if (transform.position.x > player.transform.position.x && preventMultipleCalls == 3)
                preventMultipleCalls = 2;
            if (transform.position.x < player.transform.position.x && preventMultipleCalls == 1)
                preventMultipleCalls = 2;
        }
    }
    void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "EndOfPlatform")
        {
            Flip ();
        }
    }
}

I have pasted the entire enemy script in here just so you can understand what all the functions and variables all do.

Thanks in advance to anyone who helps me out <3

Please take a quick peek at the first post in the forum on how to format C# code to make it readable. :slight_smile:

That better Kurt? :slight_smile: have never posted before as you can probably tell hahah

I think comparing the x position is a perfectly good solution. When I read your post, you said there were some issues with that for you, but I couldn’t understand what they were, in your opinion.

Also, here are a few thoughts:

  1. your distance check only requires an ‘else’ for the second portion, rather than calling that method a second time.
    1b) this could be improved slightly by changing Vector3.Distance to (target.position - player.position).sqrMagnitude > maxDistance * maxDistance
  2. get your rigid body component in start or awake, and cache it in a variable for re-use rather than calling get component over and over, again.
  3. your else if (inAttackRange) could again be simply an ‘else’
  4. you have several other contenders for ‘else’ in place of the code you have, but I won’t list them all lol

Please clarify your issue with the x position check , if you can… :slight_smile:

Are you using a sprite? Because if you are there’s the SpriteRenderer.flipX boolean already there’s no need to change the object’s scale.

You’re using the Flip() as a toggle which isn’t ideal in this case, you want to know which direction you using .flipX = true / false when you need it gives you more control, or just add a parameter to the Flip() function to determine if you have to flip left or right, then compare X positions to see if you need to flip or not.

Thanks both of you for the help <3

i am using a sprite i believe everything in the scene is 2d so i would assume so. I’ve tried to use a number to identify whether the enemy needs to flip again but its not working as planned. I tried comparing the 2 positions and the value of the facing right Boolean so if the enemy was to the left of the player and facing right the float would be one, and then after checking if they swapped places but the facing right bool was the same it would change the float to 2 and allow the first bit to call again but the float never changes from 2 for some reason. How would you use the .flipX in my scenario and how do you access the sprite from the script?

else  {
            if (preventMultipleCalls == 2) {
                if (facingRight == true) {
                    //correctDirection = Physics.Raycast (transform.position, transform.TransformDirection (Vector3.left), 12f);
                    if (transform.position.x < player.transform.position.x)
                        correctDirection = false;
                }
                else{
                    if (transform.position.x > player.transform.position.x)
                        correctDirection = false;
                }
                //correctDirection = Physics.Raycast (transform.position, transform.TransformDirection (Vector3.right), 12f);
                if (correctDirection == false) {
                    Flip ();
                }
                if (transform.position.x > player.transform.position.x)
                    preventMultipleCalls = 3;
                else
                    preventMultipleCalls = 1;
            }
   
            if (facingRight == true)
                rb2d.velocity = new Vector2 (attackVelocity, rb2d.velocity.y);   
            else
                rb2d.velocity = new Vector2 (-attackVelocity, rb2d.velocity.y);
           
            if (transform.position.x > player.transform.position.x && preventMultipleCalls == 3)
                preventMultipleCalls = 2;
            if (transform.position.x < player.transform.position.x && preventMultipleCalls == 1)
                preventMultipleCalls = 2;
        }

With this code at the moment the enemy will be facing away and the player will go into range and when this happens the enemy speeds up but doesn’t turn, when he turns automatically at the end of the platform and is facing the player and in range the flip() is called continuously and preventMultipleCalls doesnt change from 2

Thanks again for the help :slight_smile:

Whether you flip the sprite or change the scale, that is obviously not your main issue.
I would think that just checking if the player’s x position is greater and you’re facing left, then flip

if (player.transform.position.x >= transform.position.x) {
   if(!facingRight) Flip();
   }
else if(facingRight) Flip();

I think that is correct?

as if you just fixed the whole thing with 4 lines hahahahahaa can’t believe it was so simple, Thanks so much :slight_smile: :slight_smile: :slight_smile: :slight_smile: :slight_smile: :slight_smile:

No problem :slight_smile: