extends NavigationRegion3D # Запекает навмеш при старте игры по статическим коллизиям уровня (пол, здания, деревья). const SOURCE_GROUP := "nav_source" @export var exclude: Array[NodePath] = [] # Узлы, которые не должны попасть в навмеш (например, сам камень) @export var bake_half_size := 300.0 # Половина стороны квадрата запекания вокруг центра мира @export var cell_size := 1.0 # Крупная ячейка — быстрое запекание (в вебе нет потоков) @export var agent_radius := 2.0 @export var agent_height := 5.0 func _ready() -> void: var nm := NavigationMesh.new() nm.cell_size = cell_size nm.cell_height = 0.25 nm.agent_radius = agent_radius nm.agent_height = agent_height nm.agent_max_climb = 0.5 nm.geometry_parsed_geometry_type = NavigationMesh.PARSED_GEOMETRY_STATIC_COLLIDERS nm.geometry_source_geometry_mode = NavigationMesh.SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN nm.geometry_source_group_name = SOURCE_GROUP nm.filter_baking_aabb = AABB(Vector3(-bake_half_size, -10, -bake_half_size), Vector3(bake_half_size * 2, 60, bake_half_size * 2)) navigation_mesh = nm var map := get_world_3d().navigation_map NavigationServer3D.map_set_cell_size(map, nm.cell_size) NavigationServer3D.map_set_cell_height(map, nm.cell_height) # Ждём кадр, чтобы процедурные узлы (лес) успели создать коллизии await get_tree().process_frame var excluded: Array[Node] = [] for p in exclude: var n := get_node_or_null(p) if n: excluded.append(n) _mark_sources(get_parent(), excluded) bake_navigation_mesh(true) # Помечает всё дерево сцены как источник геометрии, обходя исключённые узлы func _mark_sources(node: Node, excluded: Array[Node]) -> void: for c in node.get_children(): if c == self or c in excluded: continue if excluded.any(func(e): return c.is_ancestor_of(e)): _mark_sources(c, excluded) else: c.add_to_group(SOURCE_GROUP)