I am using the A* pathfinding in a 2D project and came across an issue. Drawing and updating the path to a target works fine on game objects that I’ve placed in a scene. However, this is not the case for instantiated prefabs. Path is drawn to the target game object, but it does not update. The original target position remains the point that the path is drawn, even when the target has changed position. Like I mentioned, this is not the case when those prefabs are manually placed in a scene. Has anyone ecountered this issue? I should mention that I’m using a InvokeRepeating() method for updating the path to the target. It is called every 0.5 seconds.
Share your code. My guess is that you are using a reference to the prefab somewhere where you should be referencing the instance that you instantiated from the prefab.
Unrelated side note, but coroutines are considered better practice and you will have more type-safety and control over their execution than InvokeRepeating.
Here is my code:
using Pathfinding;
using UnityEngine;
public class Enemy : MonoBehaviour
{
[Header("References")]
public Transform target;
// ...
public GameObject spawnPrefab;
[Header("Common AI Stats")]
// ...
public float nextWaypointDistance = 3F;
public float updatePathRate = 0.5F;
public float endReachedDistanceOnXAxis = 0.5F;
public float endReachedDistanceOnYAxis = 0.5F;
[Range(1.00F, 5.00F)]
public float safeDistance = 2.00F;
// ...
[Header("Melee AI Stats")]
// ...
[Header("Ranged AI Stats")]
// ...
[Header("Support AI Stats")]
// ...
[Header("Sounds")]
// ...
private int _currentHealth;
private bool _isDead = false;
private int _currentWaypoint = 0;
private float _originalMovingSpeed = 0.00F;
private float _originalJumpCooldownTimer = 0.00F;
private bool _reachedEndOfPath = false;
private bool _isJumping = false;
private Vector2 _originalPosition;
private Animator _animator;
private Path _path;
private Seeker _seeker;
private Rigidbody2D _rigidbody;
private AudioManager _audioManager;
public float RemainingDistanceX
{
get
{
return Mathf.Abs(target.position.x - this._rigidbody.position.x);
}
}
public float RemainingDistanceY
{
get
{
return Mathf.Abs(target.position.y - this._rigidbody.position.y);
}
}
private void Awake()
{
}
// Start is called before the first frame update
void Start()
{
// ...
_animator = GetComponentInChildren<Animator>();
_seeker = GetComponent<Seeker>();
_rigidbody = GetComponent<Rigidbody2D>();
_audioManager = FindObjectOfType<AudioManager>();
Collider2D[] hitEnemies = Physics2D.OverlapCircleAll(attackPoint.position, attackRange, enemyLayers);
// Generate path to target gameObject.
InvokeRepeating("UpdatePath", 0.00F, updatePathRate);
// ...
}
// Update is called once per frame.
void FixedUpdate()
{
// Checks if there is a valid path.
if (_path == null)
{
return;
}
UpdateGFX();
// Stop movement when the player is nearby or the enemy is playing the attack/hurt animations.
if (RemainingDistanceX <= endReachedDistanceOnXAxis || _animator.GetCurrentAnimatorStateInfo(0).IsName("Attack") || _animator.GetCurrentAnimatorStateInfo(0).IsName("Hurt"))
{
this._rigidbody.velocity = Vector2.zero;
}
// ...
// Checks waypoints in the path.
if ((_currentWaypoint >= _path.vectorPath.Count) || (RemainingDistanceX <= endReachedDistanceOnXAxis && RemainingDistanceY <= endReachedDistanceOnYAxis))
{
_reachedEndOfPath = true;
return;
}
else
{
_reachedEndOfPath = false;
}
MoveToWaypoint();
}
private void UpdatePath()
{
if (_seeker.IsDone())
{
_seeker.StartPath(_rigidbody.position, target.position, OnPathComplete);
}
}
private void OnPathComplete(Path path)
{
if (!path.error)
{
this._path = path;
_currentWaypoint = 0;
}
}
private void MoveToWaypoint()
{
// Moving GameObject to Waypoint.
Vector2 direction = ((Vector2)this._path.vectorPath[_currentWaypoint] - _rigidbody.position).normalized;
Vector2 force = direction * speed * Time.deltaTime;
_rigidbody.velocity = force;
float distance = Vector2.Distance(this._rigidbody.position, this._path.vectorPath[_currentWaypoint]);
// Checks if gameObject is at Waypoint location and if the distance to the player is low enough to move to the next waypoint and closer to player.
if (distance < nextWaypointDistance && RemainingDistanceX < safeDistance)
{
_currentWaypoint++;
}
// If Player is far enough, the enemy will play the idle animation.
else
{
_animator.SetInteger("AnimState", 0);
}
// --------------------------------------------------------
if (force.x >= 0.01F || force.x <= -0.01F)
{
_animator.SetInteger("AnimState", 2);
}
}
private void UpdateGFX()
{
// ...
}
private void AttackWithDelay()
{
// ...
}
private void Attack()
{
// ...
}
private void ShootWithDelay()
{
// ...
}
private void Shoot()
{
// ...
}
private void SpawnWithDelay()
{
// ...
}
private void Spawn()
{
// ...
}
public void TakeDamage(int damage)
{
// ...
}
private void InstantiateDamagePopup(int damage)
{
// ...
}
private void Die()
{
// ...
}
private void AggressiveJump()
{
// ...
}
private void UpwardsMove()
{
// ...
}
private void DownwardsMove()
{
// ...
}
private void OnDrawGizmosSelected()
{
// ...
}
}
I removed the methods unrelated to pathfinding.
I think I understand what you mean. I am setting the target as the player prefab, but I should try to find the Player object in the hierachy instead.
I will definitely look into it! Thanks for the advice.
I managed to fix it by using: target = gameObject.transform.Find("/Player(Samurai)");
(instead of referencing the Player prefab in the Inspector). Thanks for the tip.
Thanks. That’s worked.