My game is still in the very early stages, but basically I have an enemy that follows the player if it can see them and will continue to follow the player for a few seconds after the player leaves its sight. This works fine, except that the enemy moves slower and slower the more it moves.

I put a Debug.Log within FixedUpdate that logs the enemies movement and the more the enemy moves, the less often the Debug.Log is logged in the console, until eventually the enemy is pretty much at a stand-still. Sooo basically I think this means that FixedUpdate is being called more and more slowly over time.

Anyone have any ideas why this is happening? It's driving me crazy! Is there a problem with the way I'm applying force to move the enemy? Most of this code is from a tutorial that I followed.

Here's my code (I'm using A* Pathfinding btw):

using UnityEngine;
using System.Collections;
using Pathfinding;

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(Seeker))]

public class EnemyMovement : MonoBehaviour {

 // What to chase
 public Transform target;

 // How many times each second we will update our path
 public float updateRate = 2f;

 // Caching
 private Seeker seeker;
 private Rigidbody2D rb;

 // The calculated path
 public Path path;

 // The AI's speed per second
 public float speed = 300f;
 public ForceMode2D fMode;

 [HideInInspector]
 public bool pathIsEnded = false;

 // The max distance from the AI to a waypoint for it to continue to the next waypoint
 public float nextWaypointDistance = 1f;

 // The waypoint we are currently moving towards
 private int currentWaypoint = 0;

 private bool searchingForPlayer = false;

 public bool stillSearching = false;

 public bool facingRight = true;

 float horizontalVelocity;

 float timer = 0f;

 public float continueSearchingTime = 15f;

 Vector3 enemyLocation;

 Player player;
 EnemySenses enemySenses;

 public static EnemyMovement instance;

 void Awake()
 {
     if (instance == null)
     {
         instance = this;
     }
 }

 void Start()
 {
     player = Player.instance;
     enemySenses = EnemySenses.instance;

     enemyLocation = transform.localScale;

     seeker = GetComponent<Seeker>();
     rb = GetComponent<Rigidbody2D>();

     if (target == null)
     {
         if (!searchingForPlayer)
         {
             searchingForPlayer = true;
             StartCoroutine(SearchForPlayer());
         }
         return;
     }

     // Start a new path to the target position, return the result to the OnPathComplete method
     seeker.StartPath(transform.position, target.position, OnPathComplete);

     StartCoroutine(UpdatePath());
 }

 void FixedUpdate()
 {
     if (target != null && player.isDead == true || target != null && enemySenses.CanPlayerBeSeen() == false && stillSearching == false)
     {
         searchingForPlayer = false;
         return;
     }
     else if ((target != null && stillSearching == true && enemySenses.CanPlayerBeSeen() == false) || (target != null && stillSearching == false && enemySenses.CanPlayerBeSeen() == true))
     {
         if (!searchingForPlayer)
         {
             searchingForPlayer = true;
             StartCoroutine(UpdatePath());
         }
     }
     else if (target == null)
     {
         if (!searchingForPlayer)
         {
             searchingForPlayer = true;
             StartCoroutine(SearchForPlayer());
         }
         return;
     }

     if (path == null)
         return;

     if (currentWaypoint >= path.vectorPath.Count)
     {
         if (pathIsEnded)
             return;

         // Debug.Log("End of path reached.");
         pathIsEnded = true;
         return;
     }
     pathIsEnded = false;

     // Direction to the next waypoint
     Vector2 dir = (path.vectorPath[currentWaypoint] - transform.position).normalized;
     dir *= speed * Time.fixedDeltaTime;

     // Move the AI
     rb.AddForce(dir, fMode);

     // Debug.Log("Dir: " + dir);

     float dist = Vector3.Distance(transform.position, path.vectorPath[currentWaypoint]);
     if (dist < nextWaypointDistance)
     {
         currentWaypoint++;
         return;
     }
 }

 void LateUpdate()
 {
     Flip();
 }

 void Flip()
 {
     horizontalVelocity = rb.velocity.x;

     if (facingRight == true && horizontalVelocity < -.10) // Facing right and moving left
     {
         enemyLocation.x *= -1;
         transform.localScale = enemyLocation;
         facingRight = false;
     }
     else if (facingRight == false && horizontalVelocity > .10) // Facing left and moving right
     {
         enemyLocation.x *= -1;
         transform.localScale = enemyLocation;
         facingRight = true;
     }
 }

 IEnumerator SearchForPlayer()
 {
     GameObject searchResult = GameObject.FindGameObjectWithTag("Player");
     if (searchResult == null)
     {
         yield return new WaitForSeconds(0.5f);
         StartCoroutine(SearchForPlayer());
     }
     else if (player.isDead == false)
     {
         target = searchResult.transform;
         searchingForPlayer = false;
         StartCoroutine(UpdatePath());
         yield return false;
     }
 }

 IEnumerator UpdatePath()
 {
     if (target == null)
     {
         if (!searchingForPlayer)
         {
             searchingForPlayer = true;
             StartCoroutine(SearchForPlayer());
         }
         yield return false;
     }

     // Start a new path to the target position, return the result to the OnPathComplete method
     seeker.StartPath(transform.position, target.position, OnPathComplete);

     yield return new WaitForSeconds(1f / updateRate);
     StartCoroutine(UpdatePath());
 }

 public void OnPathComplete (Path p)
 {
     // Debug.Log("We got a path. Did it have an error? " + p.error);
     if (!p.error)
     {
         path = p;
         currentWaypoint = 0;
     }
 }

 IEnumerator OnTriggerExit2D()
 {
     stillSearching = true;

     while (timer < continueSearchingTime)
     {
         yield return new WaitForSeconds(1f);
         timer++;
         // Debug.Log("Timer: " + timer);
     }

     if (timer >= continueSearchingTime)
     {
         stillSearching = false;
     }
 }

 void OnTriggerEnter2D()
 {
     timer = 0.0f;
     stillSearching = false;
 }
 }
  • I should add that my frame-rate stays consistent and I'm able to move the player character around just fine. It's just the enemy that's affected. – Jay Cuellar Jun 27 at 1:55

My first guess is your coroutine. Having a coroutine in any update sequence could result in this type of behavior if it's getting inside somehow.

I would suggest taking a peak at your profiler and see what script is bogging the game, you should be able to spot the problem with your scripts in there.

Edit:

I took another look. You're calling a the same coroutine inside the coroutine which would cause a memory leak over time if not careful.

If you want to loop over your game searching for the character use a while look and start a single coroutine at the start function. It's not a great idea to do it this way, but that would probably solve your delay issues. You've done something similar with your OnTriggerExit2D.

Also your OnTriggerExit2D should not be a coroutine, do this instead

void OnTriggerEnter2D(Collider2D other){

     if(other.name == "Player")
         playerInZone = true;
 }

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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