【Godot】Navigation2Dを使用した経路探索の実装方法

この記事では「Navigation2D」を使用した経路探索の方法について紹介します。

Navigation2Dを使用した経路探索

素材画像

今回使用する素材は以下の画像ファイルとなります。

map.png

こちらのマップ画像は「ぴぽや倉庫」様よりお借りしました。

これをプロジェクトファイルに追加しておきます。

ルートには Node2D を作成し、名前を「Main」にリネームし、その下に Mapスプライトを追加しました。

Navigation2D / NavigationPolygonInstanceノードを追加

次にルートシーンの下に “Navigation2D” ノードを追加します。

さらにその下に “NavigationPolygonInstance” ノードを追加します。

ここで、それぞれの役割説明です。

  • Navigation2D: 2Dでの経路探索を管理する
  • NavigationPolygonInstance: 通過できる領域を定義する

ということで、マップ画像の通過できる領域を “NavigationPolygonInstance” で定義していきます。

通過可能な領域の設定

通過可能な領域を設定するには、インスペクターから「NavigationPolygonInstance > Navpoly > [空]」をクリックして、「新規 NavigationPolygon」を選びます。

そうするとキャンバス内をクリックでポリゴンを配置できるので、通過可能な場所をポリゴンで囲むようにします。

クリックを開始したポイントを最後にクリックすると、このようにナビのポリゴンの領域が明るい緑色となります。

Godotくんと Line2Dノードの作成

Godotくん(icon.png) をキャンバスにドラッグ&ドロップで配置し、スプライトの名称を “Godot” に変更しておきます。それと Godotくんのサイズがやや大きいので、スケールの値 (Transform > Scale) を 0.5 に変更しておきました。

合わせて “Line2D” ノードも配置しておきます。

経路探索処理を行う

ようやく準備ができたので、経路探索処理を行います。

“Main” ノードを選択して Main.gd スクリプトを追加します。

extends Node2D

# 経路探索2D
onready var nav_2d := $Navigation2D

# 経路を視覚的に表示する線
onready var line_2d := $Line2D

# キャラクター
onready var godot := $Godot

# 左マウスクリックを処理する
func _unhandled_input(event: InputEvent) -> void:
	if not event is InputEventMouseButton:
		return # マウスイベントでない
	if event.button_index != BUTTON_LEFT or not event.pressed:
		return # 左クリックでない
	
	# キャラクターから左クリックした位置までの経路を求める
	var new_path = nav_2d.get_simple_path(godot.position, event.position)
	
	# 経路を線で視覚的に表示する
	line_2d.points = new_path

経路探索を実行している部分は「Navigation2D::get_simple_path()」です。この関数の第1引数に開始座標を指定し、第2引数に終点を指定すると、移動経路を表現する座標のリストが作られます。

では実行して動作を確認します。マウスで左クリックをするとその地点までの経路が表示されます。

メニューの「デバッグ > ナビゲーションの表示」にチェックを入れると、デバッグ実行中でも「ナビゲーションのエリア」が視覚的に表示されるようになります。

もし以下のように経路が正しく表示されない場合は、Line2Dの設定を見てみると良いかもしれません。

Godotくんに LIne2D をぶら下げてしまった場合には、その設定が反映されてしまい、Line2Dのオフセット位置やスケール値が適用されてしまっているためです。

Line2DのTrasformの値を初期値である、 (x, y) = (0, 0) スケール値「1.0」にすれば正しく経路が表示されるはずです。

ここまでのプロジェクトファイル

念のためここまでのプロジェクトファイルを添付しておきます。うまく動作しない場合はこちらのデータと比較してみてください。

作成した経路に沿ってGodotくんを移動させる

Godotノードを選択して以下のスクリプトをアタッチします。

extends Sprite

# 移動速度
var speed := 400.0

# パス情報
var path := PoolVector2Array() setget set_path

func _ready() -> void:
	# _process()を起動時は無効にする
	set_process(false)

func _process(delta: float) -> void:
	# 移動速度を計算する
	var move_distance := speed * delta

	# パスに沿って移動する
	move_along_path(move_distance)
	
# パスに沿って移動する
# @param distance 移動距離
func move_along_path(distance:float) -> void:
	# 開始座標
	var start_point := position
	for i in range(path.size()):
		# 次の移動ポイント
		var next_point := path[0]
		
		# 開始座標から次の移動先への距離を求める
		var distance_to_next := start_point.distance_to(next_point)
		if distance <= distance_to_next and distance >= 0.0:
			# 移動距離が次への距離距離より小さい、かつ移動距離が残っていれば
			# 次の移動ポイントまで移動する
			position = start_point.linear_interpolate(next_point, distance / distance_to_next)
			# 移動できた
			break
		elif distance < 0.0:
			# 移動完了
			position = next_point
			set_process(false)
			break
		
		# 移動した距離だけ差し引く
		distance -= distance_to_next
		
		# 開始地点を移動させる
		start_point = next_point
		
		# 移動完了したパス(先頭の座標)を消しておく
		path.remove(0)
		
# setter
func set_path(v:PoolVector2Array) -> void:
	path = v
	if v.size() > 0:
		# 移動可能
		# _process()を開始する
		set_process(true)

少し長いですが、ここで行っているのは以下のとおりです。

  • 移動経路が設定されるまで _process() を停止しておく
  • pathのsetterで移動経路が設定されたら、_process() を有効にする
  • move_along_path() で移動処理を行う
  • 移動処理は、現在座標(position)からパスの先頭の座標への距離を計算し、その移動可能な距離の範囲で移動を行う

それと Main.gd で移動経路を設定する処理を追加しておきます。

extends Node2D

# 経路探索2D
onready var nav_2d := $Navigation2D

# 経路を視覚的に表示する線
onready var line_2d := $Line2D

# キャラクター
onready var godot := $Godot

# 左マウスクリックを処理する
func _unhandled_input(event: InputEvent) -> void:
	if not event is InputEventMouseButton:
		return # マウスイベントでない
	if event.button_index != BUTTON_LEFT or not event.pressed:
		return # 左クリックでない
	
	# キャラクターから左クリックした位置までの経路を求める
	var new_path = nav_2d.get_simple_path(godot.position, event.position)
	
	# 経路を線で視覚的に表示する
	line_2d.points = new_path
	
	# キャラクターに移動経路を設定しておく
	godot.path = new_path

追加したのは最後の行、「Godotくんに移動経路を設定する」部分だけです。

では実行して動作を確認します。

完成プロジェクトファイル

今回の記事で作成したプロジェクトファイルを添付しておきます。

参考

今回の記事を作成するにあたり以下の記事と動画を参考にしました。