Simple AI Behaviors: Following and Avoiding
Now that you understand state machines and decision trees, let's put that knowledge into practice by creating simple, effective AI behaviors. In this chapter, you'll learn how to make characters follow targets and avoid obstacles - two of the most common AI behaviors in games.
What You'll Learn
By the end of this chapter, you'll be able to:
- Implement following behavior for AI characters
- Create obstacle avoidance systems
- Combine following and avoiding for realistic movement
- Optimize these behaviors for performance
Following Behavior: The Basics
Following behavior makes an AI character move toward a target. It's used for enemies chasing players, NPCs following leaders, or creatures tracking food. The core concept is simple: calculate the direction to the target and move in that direction.
Simple Following in Unity
Here's a basic following implementation:
public class FollowBehavior : MonoBehaviour
{
public Transform target;
public float speed = 5f;
public float stoppingDistance = 2f;
void Update()
{
if (target == null) return;
// Calculate distance to target
float distance = Vector3.Distance(transform.position, target.position);
// Only move if not too close
if (distance > stoppingDistance)
{
// Calculate direction to target
Vector3 direction = (target.position - transform.position).normalized;
// Move towards target
transform.position += direction * speed * Time.deltaTime;
// Optional: Rotate to face target
transform.LookAt(target);
}
}
}
Following with Smooth Rotation
For more natural movement, add smooth rotation:
public class SmoothFollowBehavior : MonoBehaviour
{
public Transform target;
public float moveSpeed = 5f;
public float rotationSpeed = 5f;
public float stoppingDistance = 2f;
void Update()
{
if (target == null) return;
float distance = Vector3.Distance(transform.position, target.position);
if (distance > stoppingDistance)
{
// Calculate direction
Vector3 direction = (target.position - transform.position).normalized;
// Smooth rotation
Quaternion lookRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(
transform.rotation,
lookRotation,
rotationSpeed * Time.deltaTime
);
// Move forward
transform.position += transform.forward * moveSpeed * Time.deltaTime;
}
}
}
Following in Godot
Here's the same behavior in GDScript:
extends CharacterBody2D
@export var target: Node2D
@export var speed: float = 200.0
@export var stopping_distance: float = 50.0
func _physics_process(delta):
if target == null:
return
var distance = global_position.distance_to(target.global_position)
if distance > stopping_distance:
var direction = (target.global_position - global_position).normalized()
velocity = direction * speed
move_and_slide()
# Rotate to face target
look_at(target.global_position)
Obstacle Avoidance: The Basics
Obstacle avoidance prevents AI characters from walking into walls, objects, or other characters. It uses raycasting or collision detection to detect obstacles and steer around them.
Simple Obstacle Avoidance in Unity
Here's a basic avoidance system using raycasting:
public class AvoidObstacles : MonoBehaviour
{
public float avoidanceDistance = 5f;
public float avoidanceForce = 10f;
public LayerMask obstacleLayer;
void Update()
{
Vector3 avoidanceDirection = Vector3.zero;
// Cast rays in multiple directions
Vector3[] rayDirections = {
transform.forward,
transform.forward + transform.right,
transform.forward - transform.right,
transform.right,
-transform.right
};
foreach (Vector3 direction in rayDirections)
{
RaycastHit hit;
if (Physics.Raycast(transform.position, direction, out hit, avoidanceDistance, obstacleLayer))
{
// Calculate avoidance direction (away from obstacle)
Vector3 avoidDir = (transform.position - hit.point).normalized;
avoidanceDirection += avoidDir;
}
}
// Apply avoidance force
if (avoidanceDirection != Vector3.zero)
{
avoidanceDirection.Normalize();
transform.position += avoidanceDirection * avoidanceForce * Time.deltaTime;
}
}
}
Advanced Avoidance with Steering
For smoother avoidance, use steering forces:
public class SteeringAvoidance : MonoBehaviour
{
public float avoidanceRadius = 3f;
public float avoidanceWeight = 2f;
public LayerMask obstacleLayer;
private Vector3 currentVelocity;
public float maxSpeed = 5f;
public float maxForce = 10f;
void Update()
{
Vector3 avoidanceForce = CalculateAvoidanceForce();
Vector3 steering = Vector3.ClampMagnitude(avoidanceForce * avoidanceWeight, maxForce);
currentVelocity = Vector3.ClampMagnitude(currentVelocity + steering, maxSpeed);
transform.position += currentVelocity * Time.deltaTime;
}
Vector3 CalculateAvoidanceForce()
{
Collider[] obstacles = Physics.OverlapSphere(transform.position, avoidanceRadius, obstacleLayer);
Vector3 avoidanceForce = Vector3.zero;
foreach (Collider obstacle in obstacles)
{
Vector3 directionAway = (transform.position - obstacle.transform.position).normalized;
float distance = Vector3.Distance(transform.position, obstacle.transform.position);
float strength = 1f - (distance / avoidanceRadius);
avoidanceForce += directionAway * strength;
}
return avoidanceForce.normalized;
}
}
Combining Following and Avoiding
The real power comes from combining both behaviors. Here's how to make an AI that follows a target while avoiding obstacles:
public class FollowAndAvoid : MonoBehaviour
{
public Transform target;
public float followSpeed = 5f;
public float avoidanceRadius = 3f;
public float avoidanceWeight = 2f;
public float stoppingDistance = 2f;
public LayerMask obstacleLayer;
void Update()
{
if (target == null) return;
Vector3 followDirection = CalculateFollowDirection();
Vector3 avoidanceDirection = CalculateAvoidanceDirection();
// Combine directions (avoidance has priority)
Vector3 finalDirection = (followDirection + avoidanceDirection * avoidanceWeight).normalized;
// Move
float distance = Vector3.Distance(transform.position, target.position);
if (distance > stoppingDistance)
{
transform.position += finalDirection * followSpeed * Time.deltaTime;
transform.LookAt(transform.position + finalDirection);
}
}
Vector3 CalculateFollowDirection()
{
return (target.position - transform.position).normalized;
}
Vector3 CalculateAvoidanceDirection()
{
Collider[] obstacles = Physics.OverlapSphere(transform.position, avoidanceRadius, obstacleLayer);
Vector3 avoidance = Vector3.zero;
foreach (Collider obstacle in obstacles)
{
Vector3 directionAway = (transform.position - obstacle.transform.position).normalized;
float distance = Vector3.Distance(transform.position, obstacle.transform.position);
float strength = 1f - (distance / avoidanceRadius);
avoidance += directionAway * strength;
}
return avoidance.normalized;
}
}
Pro Tips for Simple AI Behaviors
Performance Optimization
- Use object pooling for frequently created/destroyed AI
- Limit raycast frequency (don't cast every frame if not needed)
- Use spatial partitioning for obstacle detection
- Cache frequently accessed values (like target position)
Making Behaviors Feel Natural
- Add slight randomness to movement speed
- Use smooth interpolation for rotation
- Add acceleration and deceleration
- Consider adding "personality" through speed variations
Common Mistakes to Avoid
- Don't update every frame if the target isn't moving
- Don't forget to normalize direction vectors
- Don't make avoidance too aggressive (creates jittery movement)
- Don't ignore performance with too many raycasts
Mini Challenge: Create a Following Enemy
Try creating an enemy that:
- Follows the player when within detection range
- Avoids walls and obstacles while following
- Stops at a safe distance before attacking
- Smoothly rotates to face the player
Use the code examples above as a starting point, and experiment with different speeds, distances, and avoidance weights.
Troubleshooting Common Issues
AI Gets Stuck on Obstacles
- Increase avoidance radius
- Add multiple raycast directions
- Use sphere casting instead of raycasting
- Add a "backing up" behavior when stuck
AI Moves Too Jerkily
- Smooth the avoidance direction over time
- Reduce avoidance weight
- Add movement smoothing/damping
- Use steering forces instead of direct position changes
AI Doesn't Follow Properly
- Check that target reference is set
- Verify distance calculations
- Ensure movement speed is appropriate
- Check for conflicting movement scripts
What's Next?
You've learned how to create basic following and avoiding behaviors. In the next chapter, you'll discover how to use AI tools to generate game assets like sprites, textures, and music - expanding your toolkit for game development.
Resources
- Unity AI Navigation
- Steering Behaviors for Autonomous Characters
- Game AI Pro Book Series
- Godot AI Navigation
Community Support
- Discord Server: Get help with AI behavior implementation
- GitHub Repository: Share your following/avoiding code
- AI Game Development Forums: Learn from other developers
- Course Discussion: Share your progress and get feedback
Ready to learn how AI can help create game assets? Continue to Chapter 5: AI Asset Generation: Sprites, Textures, and Music and discover how to use AI tools for content creation.