I have been working on my Enemy movement script. I have watched a couple of Youtube videos studying various different methods of Enemy movement.
I have found this video very helpful that explains how to make enemy move while avoiding obstacles using 8 directions vector:
My enemy setup:
For my Enemy I use a simple sprite with Rigidbody 2D(Dynamic) and Collider attached
My complete code:
using System.Collections;
using System.Collections.Generic;
using JetBrains.Annotations;
using Unity.VisualScripting;
using UnityEditor.Callbacks;
using UnityEngine;
//Very simple to PlayerFollower_simple script but with lerp smoothing enabled.
public class PlayerFollower_simple_smooth : MonoBehaviour
{
public Transform target; //drag and stop player object in the inspector
private Rigidbody2D rb;
public float stoppingDistance = 0.1f;
public float speed = 2.0f;
public float player_detectionDistance = 5.0f; // for player detection
public float obstacle_detectionDistance = 1.0f; // for obstacle detection
//if smoothing is enabled, allow the smoothness factor to be set in the inspector
[Range(0.0f, 1.0f)]
public float smoothness = 0.0f; // Add this variable to control the smoothness of steering
private Vector2 currentVelocity = Vector2.zero; // Keep track of current velocity
[SerializeField]
private bool showGizmos = true;
public float[] interest = new float[8];
public float[] danger = new float[8];
public float[] result = new float[8];
public List<Vector2> eightDirections = new List<Vector2>
{
new Vector2(0,1).normalized, // top
new Vector2(1,1).normalized, // top-right
new Vector2(1,0).normalized, // right
new Vector2(1,-1).normalized, // bottom-right
new Vector2(0,-1).normalized, // down
new Vector2(-1,-1).normalized, // bottom-left
new Vector2(-1,0).normalized, // left
new Vector2(-1,1).normalized // top-left
};
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
Vector2 current_position = transform.position;
Vector2 target_position = target.position;
Vector2 direction = (target_position - current_position).normalized;
Debug.Log("Direction: " + direction);
// Set the layer mask to ignore the enemy layer
LayerMask obstacleLayerMask = LayerMask.GetMask("Obstacle");
// Initialize danger values to zero at the beginning of each frame. This ensure we can calculate sum of danger values for each direction
for (int i = 0; i < danger.Length; i++)
{
danger[i] = 0;
}
for (int i = 0; i < eightDirections.Count; i++)
{
// Calculate the interest based on the dot product
interest[i] = Vector2.Dot(direction, eightDirections[i]);
RaycastHit2D hit = Physics2D.Raycast(transform.position, eightDirections[i], obstacle_detectionDistance, obstacleLayerMask);
if (hit.collider != null && hit.collider.CompareTag("Obstacle"))
{
Debug.DrawRay(transform.position, eightDirections[i] * obstacle_detectionDistance, Color.yellow);
// Accumulate the danger values in the danger array
if (i == 0)
{
danger[i] += 2;
danger[i + 1] += 1;
danger[7] += 1;
}
else if (i == 7)
{
danger[i] += 2;
danger[0] += 1;
danger[i - 1] += 1;
}
else
{
danger[i] += 2;
danger[i - 1] += 1;
danger[i + 1] += 1;
}
}
}
// Calculate the result vector
for (int i = 0; i < result.Length; i++)
{
result[i] = interest[i] - danger[i];
}
// Calculate the resulting direction by summing up the result vectors
Vector2 resultingDirection = Vector2.zero;
for (int i = 0; i < result.Length; i++)
{
resultingDirection += eightDirections[i] * result[i];
}
resultingDirection.Normalize();
// Calculate the desired velocity based on the resulting direction
Vector2 desiredVelocity = resultingDirection * speed;
// Smooth the velocity based on the smoothness factor
currentVelocity = Vector2.Lerp(currentVelocity, desiredVelocity, smoothness);
rb.MovePosition(current_position + currentVelocity * Time.fixedDeltaTime);
for (int i = 0; i < interest.Length; i++)
{
Debug.DrawLine(transform.position, (Vector2)transform.position + eightDirections[i] * interest[i], Color.green);
Debug.DrawLine(transform.position, (Vector2)transform.position + eightDirections[i] * danger[i], Color.red);
Debug.DrawLine(transform.position, (Vector2)transform.position + eightDirections[i] * result[i], Color.yellow);
}
}
//Draw the gizmo in the scene view
void OnDrawGizmos()
{
if (showGizmos == false)
{
return;
}
else
{
if (target == null)
{
return;
}
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, player_detectionDistance);
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(transform.position, obstacle_detectionDistance);
}
}
}
As you can see from the code above, I am assigning interest and danger values and then calculating the result. The enemy is following player quite nicely and avoiding obstacles in its way.
My problem
This method has 1 issue. If an enemy stands directly in parallel with the player in between an obstacle, the enemy will get stuck and never reach the player.
My goal
In my game, I want to ensure that the enemy always follows the player and enemy should always find a way to reach the player regardless of any obstacles it might find along the way.
See the short gif that showcase the problem:
gd8y2bY4WAX4mQzIC6
I would be really grateful if someone could share their insights on how this particular problem could be solved ensuring the enemy finds a way to overcome an obstacle if its directly in parallel with the player