PAKit is a high-performance, context-aware navigation framework for Godot 4.x. It enables NPCs to navigate dynamic, destructible, or procedurally generated tile-based worlds using multiple search strategies.
Place a PAKitCore node in your main world scene. This node manages the mathematical grid and world-level path costs.
- Setup: Assign your
TileMapLayerto the Tilemap property in the Inspector. - Initialization: If your map is procedural or changes size, call:
pakit_core.initialize(my_tilemap_layer)
pakit_core.update_region(Rect2i(start_x, start_y, width, height))- Main Weight APIs:
pakit_core.update_cell_weight(coords, 1.0)
pakit_core.update_layer_cell_weight(&"lava", coords, 10.0)Add a PAKitNavigator node as a child of your NPC.
- Targeting: To move the NPC, simply call:
navigator.set_target(player.global_position)- Movement API: Connect to the navigator's main signals to drive character logic:
var ai_direction := Vector2.ZERO
func _on_movement_requested(direction: Vector2):
# direction is an offset to the next waypoint (not normalized speed)
ai_direction = direction
func _physics_process(delta: float) -> void:
var deadzone := 8.0
if ai_direction.x > deadzone:
velocity.x = speed
elif ai_direction.x < -deadzone:
velocity.x = -speed
else:
velocity.x = move_toward(velocity.x, 0.0, speed * delta)
# Optional: jump when next waypoint is clearly above
if ai_direction.y < -24.0 and is_on_floor():
jump()
move_and_slide()
func _on_target_reached():
ai_direction = Vector2.ZERO- Layer Preference (per NPC):
navigator.set_layer_importance(&"lava", 2.0)
navigator.set_layer_importance(&"safe_floor", 0.5)To make the AI avoid obstacles or prefer specific paths, update tile weights whenever the world changes:
# When a tile is destroyed (e.g., mining)
pakit_core.update_cell_weight(coords, 1.0) # 1.0 is the cost of empty air
# When a tile is created (e.g., building)
pakit_core.update_cell_weight(coords, 10.0) # High cost makes AI avoid itFor layer-specific behavior shared by all agents, update named layers:
# Mark dangerous cells as expensive in a specific layer
pakit_core.update_layer_cell_weight(&"lava", coords, 12.0)Note: Navigators recalculate paths continuously (based on path_refresh_time) and react to weight updates without extra manual refresh calls.
| Property | Description |
|---|---|
path_refresh_time |
Frequency (seconds) to update the path for moving targets. |
algorithm_selection_mode |
Set to FIXED, RANDOM, or CONTEXT (distance-based). |
default_algorithm |
Algorithm used when selection mode is FIXED. |
debug_tile_costs |
Visualizes the weight of tiles. |
stuck_detection_enabled |
Enables stuck detection and recovery logic. |
If you want to add a new search algorithm (for example BFS, IDA*, JPS, or a custom variant), extend the addon in these steps.
In PAKitAlgorithms, add your new value to the algorithm enum.
Also update these helper maps/functions so UI/debug remains correct:
algorithm_colorto_name()get_default_list()(optional, if you want it included by default)
In PAKitSearch, add a new method such as:
func find_path_bfs(start_grid: Vector2i, end_grid: Vector2i, layer_profile: Dictionary = {}, grid: AStarGrid2D = null, max_up_tiles: int = -1) -> PackedVector2Array:
# implement your algorithm here
return PackedVector2Array()Recommended: reuse existing helpers to keep behavior consistent with the rest of the addon:
get_neighbors(...)get_step_cost(...)reconstruct_grid_path(...)grid_path_to_world_path(...)
In PAKitCore.generate_path(...), add a new match case that calls your method:
PAKitAlgorithms.algorithm.BFS:
result_path = searcher.find_path_bfs(start_grid, end_grid, layer_profile, astar_grid, max_up_tiles)No extra code is needed in PAKitNavigator if it already uses the algorithm enum. Your new value will be selectable through:
default_algorithmrandom_algorithmsPAKitAlgorithmContextresources
If you use selection_mode.CONTEXT, you can update PAKitAlgorithms.choose_by_context(...) so your new algorithm can be picked automatically for specific distance/target patterns.
After adding the algorithm:
- Run a scene with
algorithm_selection_mode = FIXEDand your new algorithm selected. - Enable debug path color to confirm the selected algorithm path is used.
- Test with
max_up_tilesenabled to ensure climbing constraints still behave as expected.
If you want to add a new heuristic (for example Chebyshev, Octile, or a custom risk-biased estimate), extend the addon in these steps.
In PAKitAlgorithms, add your new value to the heuristic enum.
Also update these helper functions so inspector/debug names remain clear:
to_heuristic_name()get_default_heuristic_mode()(optional)
In PAKitAlgorithms.estimate_heuristic(...), add a new match branch for your enum value.
Example:
heuristic.CHEBYSHEV:
return max(dx, dy)Keep in mind:
- If you want A* to remain optimal, use an admissible heuristic.
- If you intentionally want faster but less optimal routes, allow an aggressive estimate (similar to weighted modes).
PAKitSearch already routes heuristic evaluation through:
set_heuristic_settings(...)heuristic_distance(...)PAKitAlgorithms.estimate_heuristic(...)
So if your formula only needs (a, b, mode, weight), no extra wiring is needed.
The following fields are already exported and will include your new enum value automatically:
PAKitNavigator.default_heuristic_modePAKitAlgorithmContext.heuristic_mode
If your heuristic needs extra tuning values beyond heuristic_weight, add new exported fields and pass them through the same flow used by selected_heuristic_mode and selected_heuristic_weight.
After adding the heuristic:
- Set
algorithm_selection_mode = FIXEDand forceASTAR. - Compare path result and timing against
MANHATTANandZEROon the same target pairs. - Test at least one short path and one long path scenario.
- If the path shape does not change, verify whether your map has a unique optimal route (this is normal).