годот создание игры AStar 2

В этом руководстве мы детально разберем код и внутреннюю структуру реализации поиска пути на сетке в Godot с использованием AStarGrid2D.Рассмотрим годот создание игры AStar 2

Здесь спользуются следующие узлы: корневой Node2D, содержащий Sprite2D (с текстурой фона), TileMapLayer, Camera2D и Marker2D, внутри которого размещены CPUParticles2D и Sprite2D с изображением корабля. Скрипты подключены к TileMapLayer и Marker2D — их мы сейчас и разберем.

Разбор кода начнем с файла character.gd, который подключен к узлу Marker2D. Рассмотрим следующую его часть:

func _physics_process(_delta: float) -> void:
if _state != State.FOLLOW:
return

var arrived_to_next_point: bool = _move_to(_next_point)
if arrived_to_next_point:
_path.remove_at(0)
if _path.is_empty():
_change_state(State.IDLE)
return
_next_point = _path[0]

 

Если мы не движемся, метод _physics_process завершает выполнение досрочно. В противном случае циклически вызывается функция _move_to до тех пор, пока она не вернет True. После успешного возврата первый элемент пути удаляется, а переменной _next_point присваивается следующий элемент. Если после перемещения массив _path оказывается пустым, вызывается _change_state. Теперь рассмотрим следующую функцию:

func _move_to(local_position: Vector2) -> bool:
var desired_velocity: Vector2 = (local_position - position).normalized() * speed
var steering: Vector2 = desired_velocity - _velocity
_velocity += steering / MASS
position += _velocity * get_physics_process_delta_time()
rotation = _velocity.angle()
return position.distance_to(local_position) < ARRIVE_DISTANCE

Функция возвращает True, когда корабль успешно проходит одну клетку в направлении следующей точки маршрута. Теперь рассмотрим эту функцию подробнее:

func _change_state(new_state: State) -> void:
if new_state == State.IDLE:
_tile_map.clear_path()
elif new_state == State.FOLLOW:
_path = _tile_map.find_path(position, _click_position)
if _path.size() < 2:
_change_state(State.IDLE)
return
_next_point = _path[1]
_state = new_state

Если состояние — IDLE,то очищаем путь.Если Игрок кликнул по кораблю,то вызываем эту же функцию со значением IDLE.Устанавливаем следующую точку для перемещение и новое состояние.
Рассмотрим функцию управления вводом

func _unhandled_input(input_event: InputEvent) -> void:
_click_position = get_global_mouse_position()
if _tile_map.is_point_walkable(_click_position):
if input_event.is_action_pressed(&"teleport_to", false, true):
_change_state(State.IDLE)
global_position = _tile_map.round_local_position(_click_position)
reset_physics_interpolation()
elif input_event.is_action_pressed(&"move_to"):
_change_state(State.FOLLOW)

 

Обработка кликов мыши реализована следующим образом: при нажатии правой кнопки мыши корабль перемещается в указанную позицию с округлением координат(телепортируется). Левая кнопка мыши, в свою очередь, изменяет текущее состояние корабля(перемещение). Теперь перейдем к разбору кода в файле pathfind_astar.gd:

func round_local_position(local_position: Vector2i) -> Vector2i:
return map_to_local(local_to_map(local_position))

Этот фрагмент кода отвечает за округление координат: сначала координаты мыши преобразуются в координаты сетки (блока), а затем — обратно в экранные координаты для позиционирования объекта:

func is_point_walkable(local_position: Vector2) -> bool:
var map_position: Vector2i = local_to_map(local_position)
if _astar.is_in_boundsv(map_position):
return not _astar.is_point_solid(map_position)
return false

Код выполняет проверку возможности движения корабля: если клик пришелся на тайл (занятую ячейку), то движение блокируется и корабль остается на месте:

func clear_path() -> void:
if not _path.is_empty():
_path.clear()
erase_cell(_start_point)
erase_cell(_end_point)
queue_redraw()

Код выше выполняет очистку тайлов начальной и конечной позиций, после чего обнуляет массив пути и запускает перерисовку сцены:

func find_path(local_start_point: Vector2i, local_end_point: Vector2i) -> PackedVector2Array:
clear_path()

_start_point = local_to_map(local_start_point)
_end_point = local_to_map(local_end_point)
_path = _astar.get_point_path(_start_point, _end_point)

if not _path.is_empty():
set_cell(_start_point, 0, TILE_START_POINT)
set_cell(_end_point, 0, TILE_END_POINT)

queue_redraw()

return _path.duplicate()

Этот код отвечает за расчет пути (вызывается из character.gd). Если путь не пуст, он устанавливает начальный и конечный тайлы перемещения, обновляет отображение и возвращает массив маршрута:

func _draw() -> void:
if _path.is_empty():
return

var last_point: Vector2 = _path[0]
for index in range(1, len(_path)):
var current_point: Vector2 = _path[index]
draw_line(last_point, current_point, DRAW_COLOR, BASE_LINE_WIDTH, true)
draw_circle(current_point, BASE_LINE_WIDTH * 2.0, DRAW_COLOR)
last_point = current_point

Данный код отвечает за визуализацию маршрута: если массив пути не пуст, он отрисовывает кружочки в узловых точках и соединяющие их линии:

годот создание игры AStar 2.Конец

Мы детально разобрали код и структуру проекта Grid-based Pathfinding with AStarGrid2D. Теперь вы можете самостоятельно повторить этот проект. Спасибо за внимание!

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх