I’m currently developing a flying AI enemy in Unity, and I’m experiencing significant issues with its movement. The chase behavior performs well when no obstacles are present, but once the obstacle avoidance feature is activated, the AI starts to stutter and jitter wildly.
It seems to be caused by the AI rapidly toggling between chase and avoidance modes. Below is the code I have implemented so far:
using UnityEngine;
using System.Collections;
public class FlyingBot : MonoBehaviour
{
[Header("Configuration")]
public float moveSpeed = 5f;
public float scanRange = 10f;
public Vector3 offsetDistance = Vector3.zero;
public LayerMask wallLayer;
[Header("References")]
public Transform player;
private RaycastHit obstacle;
void Update()
{
float playerDistance = Vector3.Distance(transform.position, player.position);
if (playerDistance <= 50f)
{
Vector3 toPlayer = player.position - transform.position;
if (Physics.SphereCast(transform.position, 1f, toPlayer.normalized, out obstacle, scanRange, wallLayer))
{
Vector3 dodgeDirection = Vector3.Cross(obstacle.point, obstacle.normal);
transform.position = Vector3.MoveTowards(transform.position, dodgeDirection, moveSpeed * Time.deltaTime);
}
else
{
Vector3 targetPos = player.position + offsetDistance;
transform.position = Vector3.MoveTowards(transform.position, targetPos, moveSpeed * Time.deltaTime);
}
}
}
}
Does anyone have suggestions on how to achieve a more fluid movement?
your dodge direction math is a bit off. try using Vector3.Reflect(-toPlayer.normalized, obstacle.normal) instead of the cross product. it should give you smoother avoidance. also, maybe add some lerp for smoothing instead of just MoveTowards.
Had this exact problem on a drone nav system a few years ago. Your spherecast hits obstacles at weird distances, so the bot keeps recalculating its path.
Add a buffer zone around obstacles. Don’t switch modes the second you detect something - use a threshold instead. Start avoidance at scanRange but only switch back to chase when obstacles are at scanRange + 2f.
Your Cross product dodge calculation sends the bot in random directions. I blend the avoidance vector with chase direction to keep forward momentum:
Your movement calculations are happening in world space without proper direction vectors. When you use Cross product for dodgeDirection, you get a perpendicular vector that doesn’t necessarily point away from the obstacle effectively.
I had the same jittering issue with a patrol AI system. Fixed it with weighted steering - don’t hard switch between chase and avoidance. Calculate both forces at the same time and blend them based on how close the obstacle is.
Store your desired velocity as a separate variable instead of directly modifying transform.position. Calculate chase velocity toward the player, then add avoidance force perpendicular to the obstacle surface when detected. Closer obstacle = stronger avoidance weight.
Also, use FixedUpdate for physics movement. Variable deltaTime in Update causes inconsistent behavior during rapid state changes. Fixed timesteps make movement way more predictable.
The stuttering happens because your AI makes frame-by-frame decisions without smoothing. Everyone’s giving good manual fixes, but automation handles this way better.
I built something similar last year with dozens of AI agents that needed dynamic behavior switching. Instead of hardcoding state transitions and thresholds in Unity, I used Latenode for the decision logic externally.
Set up an HTTP endpoint that takes your AI’s position, obstacle data, and player position. Latenode runs this through a state machine with built-in delays and smoothing. It sends back movement commands your Unity script executes.
Best part? You can tweak behavior without recompiling. Want different avoidance patterns? Update the Latenode workflow. Need squad coordination? Easy to expand.
Your Unity code stays clean - send position data, get movement vectors back. No more frame-by-frame chaos.
The stuttering happens because your AI makes hard binary decisions every frame. I fixed this by adding a state persistence system - once the bot starts avoiding, it’s locked into that mode for at least 0.5-1 seconds before it can switch back to chasing. Another huge help was ditching the single spherecast for multiple raycasts at different angles. Way better spatial awareness and stops the constant flip-flopping between states. You might also try using coroutines for avoidance instead of cramming everything into Update. Gives you much better control over timing transitions.