Building 3D Enemy AI in Godot

Updated June 2026
Building 3D enemy AI in Godot follows the same state machine architecture as 2D, but adds the complexity of three-dimensional navigation, vertical awareness, gravity handling, and spatial reasoning in full 3D space. This guide covers setting up 3D enemy scenes, baking navigation meshes, implementing volumetric detection, 3D raycasting for line of sight, and combat behaviors suited to first-person and third-person games.

The jump from 2D to 3D enemy AI in Godot is less about learning new concepts and more about adapting existing patterns to an additional dimension. State machines, detection zones, navigation agents, and raycasts all have 3D equivalents that work on the same principles. The main new challenges are handling gravity and vertical movement, working with 3D navigation meshes that account for slopes and stairs, and managing performance as 3D scenes tend to be more computationally expensive than their 2D counterparts.

Set Up the 3D Enemy Scene

Create a new scene with CharacterBody3D as the root node. Unlike 2D where sprites represent your character, 3D enemies use MeshInstance3D nodes for their visual mesh (or an imported 3D model scene), and CollisionShape3D for their physics body. A capsule collision shape is the standard choice for humanoid enemies because it slides along surfaces predictably and handles stairs and slopes well.

Add a NavigationAgent3D as a child. This works identically to its 2D counterpart but operates in 3D space. The key properties to configure are path_desired_distance, target_desired_distance, and path_max_distance which controls how far the agent can deviate from the computed path before requesting a repath. For 3D games, set these values in meters rather than pixels, with typical values between 0.5 and 2.0 meters.

Add an AnimationPlayer and optionally an AnimationTree. If you are using imported 3D models with animations (from Blender, for example), the AnimationPlayer will contain all the imported animations. An AnimationTree with a blend tree or state machine lets you smoothly transition between idle, walk, run, and attack animations based on AI state, with blend spaces handling the transition between walking and running based on current speed.

For the facing direction, 3D enemies need to rotate toward their movement direction or toward the player during combat. Use look_at() to face a target position, or compute the rotation manually using atan2() on the direction vector and apply it to the enemy's rotation.y. Smooth rotation with lerp_angle() prevents instant snapping and looks much more natural.

Bake the Navigation Mesh

3D navigation in Godot requires a NavigationRegion3D node in your level scene with a NavigationMesh resource. The NavigationMesh defines where AI characters can walk by analyzing the static geometry in your level. Add the NavigationRegion3D as a child of your level's root node, create a new NavigationMesh resource in its properties, configure the agent parameters (radius, height, max climb, max slope), and click "Bake NavigationMesh" in the editor toolbar.

The baking process examines all StaticBody3D collision shapes within the region and generates a walkable mesh. The agent radius determines how far from walls the mesh extends, the agent height filters out areas with low ceilings, max climb controls the maximum step height the agent can traverse, and max slope sets the steepest angle the agent can walk on. For a standard humanoid enemy, an agent radius of 0.4 meters, height of 1.8 meters, max climb of 0.3 meters, and max slope of 45 degrees work well as starting values.

If your level uses CSG geometry, MeshInstance3D nodes, or imported scenes, you may need to add static collision shapes for the baking process to detect them. The navigation mesh bake only considers physics collision shapes by default, not visual meshes. You can change this in the NavigationMesh resource settings under "Geometry" to also parse visual meshes, but collision shapes give you more precise control over which surfaces are walkable.

For levels with multiple floors or disconnected areas, use multiple NavigationRegion3D nodes. The NavigationServer automatically connects regions that share edges, creating a seamless navigation graph. Navigation links can bridge disconnected areas, like a jump point between two platforms or a teleporter between rooms, allowing the pathfinding system to route agents through these special transitions.

Build 3D Detection Volumes

Area3D nodes with CollisionShape3D children serve as 3D detection volumes. A SphereShape3D creates an omnidirectional detection range, equivalent to the CircleShape2D in 2D. Set the radius to match your desired detection distance. For most 3D games, detection ranges between 10 and 30 meters produce reasonable awareness without enemies detecting players from unrealistically far away.

To simulate a field of view rather than omnidirectional detection, combine the area detection with an angle check. When a body enters the detection sphere, calculate the angle between the enemy's forward vector (-transform.basis.z in Godot's coordinate system) and the direction to the detected body. If the angle is within half the desired FOV (typically 60 to 90 degrees for a realistic forward cone), the detection is valid. If not, the body is behind the enemy and should not trigger awareness.

Vertical awareness adds another layer to 3D detection. Enemies on a ground floor should not automatically detect players on a floor above or below them, even if the horizontal distance is within range. Add a vertical angle check or height difference threshold to prevent detection through floors. This is especially important in multi-story buildings, tower structures, and any level with significant vertical separation between gameplay areas.

Implement 3D Line of Sight

RayCast3D works like RayCast2D but in three dimensions. Add one as a child of the enemy, positioned at eye height (typically 1.5 to 1.7 meters above the ground for humanoid enemies). To check line of sight, set the raycast's target_position to the local-space offset toward the player and check if it collides with world geometry.

For more flexibility, use direct space state queries with PhysicsRayQueryParameters3D. This lets you cast rays from any point to any point without needing a RayCast3D node. Call get_world_3d().direct_space_state.intersect_ray(query) where the query specifies the origin, destination, and collision mask. This approach is useful when you need multiple ray checks per frame, like testing visibility to multiple targets or casting rays at different heights to account for partial cover.

A robust 3D line-of-sight system casts multiple rays rather than just one. A single ray from the enemy's eyes to the player's center might be blocked by a small obstacle even though the player is mostly visible. Casting three rays, one to the player's head, one to the center mass, and one to the feet, and considering the player visible if any ray succeeds, produces more forgiving and realistic detection that feels fair to the player.

Create the 3D State Machine

The state machine structure for 3D enemy AI is the same enum-and-match pattern used in 2D, but the movement code in each state must handle gravity and three-dimensional velocity. In _physics_process, always apply gravity to the velocity's y component unless the enemy is on the floor: if not is_on_floor(): velocity.y -= gravity * delta. Then handle the horizontal movement based on the current AI state, and finish with move_and_slide().

Navigation in 3D produces path points in 3D space, but you typically only use the horizontal components for movement direction. Get the next path position from the NavigationAgent, calculate the direction vector from the enemy to that position, zero out the y component for horizontal-only steering, normalize the vector, and multiply by the desired speed. This prevents the enemy from trying to fly upward toward a path point on a ramp ahead of it, since gravity and floor collision handle the vertical component.

Rotation is a more significant concern in 3D than in 2D. In 2D, you simply flip the sprite. In 3D, the enemy needs to physically rotate to face its movement direction or combat target. Use lerp_angle() on rotation.y to smoothly rotate the enemy over several frames rather than snapping instantly. The rotation speed affects how the enemy feels: fast rotation makes enemies seem alert and aggressive, while slow rotation creates opportunities for the player to flank.

Add 3D Combat Behaviors

Melee combat in 3D uses the same principles as 2D: check if the player is within attack range using an Area3D or distance calculation, play the attack animation, and apply damage at the right moment using an AnimationPlayer method call track or a signal. The hitbox can be an Area3D that is only active during the attack frames, preventing damage from connecting before the animation visually shows the strike.

Ranged combat introduces projectile trajectory calculations. For straight-line projectiles like bullets, spawn the projectile at the enemy's weapon position and set its direction toward the player. For arcing projectiles like grenades, calculate a ballistic arc using the projectile's initial velocity, gravity, and the distance to the target. Godot does not have a built-in ballistic solver, but the math involves solving a quadratic equation for launch angle given a desired speed and target position, which is well-documented in game physics resources.

Cover-seeking behavior makes 3D combat feel significantly more intelligent. When the enemy takes damage or needs to reload, it should move to a position that blocks the player's line of sight. Find potential cover positions by raycasting from nearby navigation mesh positions toward the player. If the ray hits world geometry before reaching the player, that position provides cover. The enemy navigates to the best cover position, waits or performs an action, then peeks out to re-engage. This pattern requires the NavigationMesh to have positions near walls and obstacles, which happens naturally if the level geometry is designed with combat spaces in mind.

Optimize for Performance

3D AI is more expensive than 2D because of the additional dimension in all calculations, larger navigation meshes, more complex collision detection, and heavier rendering. The first optimization is reducing the AI update frequency for enemies that are far from the player. Enemies within 20 meters might update every physics frame, enemies between 20 and 50 meters update every third frame, and enemies beyond 50 meters update once per second or not at all.

Stagger AI updates across frames to prevent all enemies from computing paths on the same frame. If you have 20 enemies, and each one calls navigation_agent.target_position = player.global_position on the same frame, that triggers 20 simultaneous pathfinding queries. Instead, use a modulo on the enemy's instance ID or a rotating index to spread updates across frames. This smooths the CPU load and prevents frame spikes during combat encounters with many enemies.

Group coordination can replace individual pathfinding for squads of enemies. Instead of each enemy computing its own path to the player, a group manager computes one path and assigns formation positions around it. Each enemy then navigates to its assigned position, which is a much simpler query than a full path to the player. This technique is used in many commercial games and scales well to large groups, since adding more enemies to a squad does not proportionally increase the pathfinding cost.

Key Takeaway

3D enemy AI in Godot builds on the same state machine and node composition patterns as 2D, adding gravity handling, 3D navigation mesh baking, volumetric detection with field-of-view checks, and performance optimization strategies that become essential as scene complexity grows.