Pathfinding Visualization during Combat Pause

Godot Version

v4.2.2.stable.official [15073afe3]

Question

tl;dr: Navigation Path is not computed while in tactical pause

Hi! I’m learning Godot and I’m trying to create a game with party management and tactical pause (DAO like).
In any moment, the player can push a button to pause combat: characters, enemies and props are frozen, but the player can still decide where characters should move (with pathfinding, by clicking on floor) and other things like the next skill to apply.

The problem here is that I want to show navigation path even in combat pause:

  1. while in combat pause, the player clicks on floor with a character selected
  2. the new navigation map is computed
  3. the navigation map is displayed
  4. the character is still frozen while combat pause is active

To create the combat pause, I set process and physics_process of the characters to false. This for the minimal example, because there are also things like setting Animation Tree’s process mode to Node.PROCESS_MODE_DISABLED to pause animation

The navigation path is not computed while in combat pause, I tried different options, like manually enabling navigation agent’s process/physics_process and pathfinding’s specific component.
What I don’t understand is why the navigation agent’s target is indeed changed even in combat pause but the navigation path is not updated and thus can’t be rendered.
The navigation path is computed immediately after resuming combat, even if the target is chosen while in combat pause.

I recreated a minimal example (that i don’t know how to share here) and the mentioned problem still persists.
tree

The minimal example consists in a plane that act as floor, a character and a camera.
The ReferenceRect is used just to check if we’re in combat pause or not.
Ther are two custom input actions:

  1. “combat_pause”, set to Spacebar
  2. “mouse_select”, set to Mouse Left Button

Main script:

extends Node3D

@onready var character: Character = $CharacterBody3D
@onready var camera: Camera = $Camera3D
@onready var pause_rect: ReferenceRect = $ReferenceRect

var is_combat_paused := false:
	set(value):
		is_combat_paused = value
		on_combat_paused_changed(value)

func _input(event: InputEvent) -> void:
	if event.is_action_pressed("combat_pause"):
		is_combat_paused = not is_combat_paused
		pause_rect.visible = is_combat_paused

func _ready() -> void:
	camera.target_path_finding_changed.connect(func(value):
		character.navigation_agent.target_position = value
	)

func on_combat_paused_changed(value: bool):
	character.set_process(not value)
	character.set_physics_process(not value)

CharacterBody3D script:

class_name Character extends CharacterBody3D

@onready var navigation_agent: NavigationAgent3D = $NavigationAgent3D

const SPEED = 5.0

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")

func _physics_process(delta: float) -> void:
	# Add the gravity.
	if not is_on_floor():
		velocity.y -= gravity * delta
	var direction = navigation_agent.get_next_path_position() - global_position
	direction = direction.normalized()
	velocity.x = move_toward(
		velocity.x,
		direction.x * 5,
		20 * delta
	)
	velocity.z = move_toward(
		velocity.z,
		direction.z * 5,
		20 * delta
	)
	move_and_slide()

Camera3D script

class_name Camera extends Camera3D

@export var actor: Character
@onready var ray_cast_3d: RayCast3D = $RayCast3D

signal target_path_finding_changed(value: Vector3)

func update_target_position():
	# Pathfinding
	var mouse_pos = get_viewport().get_mouse_position()
	var ray_length = 100
	var from = project_ray_origin(mouse_pos)
	var to = from + project_ray_normal(mouse_pos) * ray_length
	var space = get_world_3d().direct_space_state
	var ray_query = PhysicsRayQueryParameters3D.new()
	ray_query.from = from
	ray_query.to = to
	ray_query.collide_with_areas = true
	var raycast_result = space.intersect_ray(ray_query)
	if "position" in raycast_result.keys():
		if raycast_result["collider"] is StaticBody3D:
			target_path_finding_changed.emit(raycast_result["position"])

func _input(event):
	if event is InputEventMouseButton:
		if event.is_action_pressed("mouse_select"):
			update_target_position()

Because for performance reasons the NavigationAgent node only updates its internal state, and if necessary its path and debug, on the physics process tick.

If you disable the physics process there is no update to the path or debug.

Instead of changing the agent target all the time while paused you can instead query a navigation path array with the NavigationServer map_get_path() function and use that path to display something for the player.

1 Like

Thank you! That was the solution

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.