💊

Godotのドラッグ&ドロップの挙動を修正する

2021/08/19に公開
2

はじめに

Godot EngineのControl系ノードにはget_drag_data()などの、ドラッグ&ドロップを簡単に実装するための組み込み関数があります[1]

1から実装するとなると意外と手間なので、とても便利です。ただしGodot3.3.2時点では、この方法は特定の条件においてプレビューがうまく表示されないというバグがあります。この記事では、そのバグを回避するコードを紹介します。

ドラッグ&ドロップのプレビューがズレる問題

get_drag_data()関数を利用したドラッグ&ドロップは、プレビュー時にCamera2Dの位置などが参照されないというバグ[2]があります。このバグのせいで、Camera2Dを使用していると、ドラッグ&ドロップ中のプレビューがあらぬ場所に表示されてしまうことになります[3]

この問題について議論されているGodot-docsのGithubのIssueでは、先人たちがバグの回避方法について書き込んでくれています。方法はシンプルで、Camera2Dの位置やViewportのサイズなどから算出した値をプレビューの初期位置に設定するだけです。

先人のコードを分かりやすく書き換えたものを以下に紹介しますので、参考にしてみてください。

var _camera = get_node("Camera2D") #Camera2Dのパスに書き換えてください

func get_drag_data(position):
	var drag_data = {}
	
	var drag_texture = TextureRect.new()
	drag_texture.expand = true
	drag_texture.texture = load("res://icon.png") #D&Dのプレビューで表示されるテクスチャ
	drag_texture.rect_size = Vector2(32, 32)

	var drag_container = Control.new()
	drag_container.add_child(drag_texture)
	drag_texture.rect_position = _camera.global_position - (get_viewport_rect().size / 2) #ここで、プレビューの初期位置を設定します
	set_drag_preview(drag_container)
	
	return drag_data

これで解決できない場合もあります

実はマウスカーソルに追従するプレビューは、その移動距離もViewportの倍率のみを参照しています。つまり、Camera2DのZoomを変更している場合には、上記のトリックも効きません。プレビューの表示される初期位置のみならず、移動距離もカーソルとズレるなど、なす術がありません[4]

Viewportを使い分けてみよう

get_drag_data()を使うGUI部分と、キャラクターやマップなどが表示される部分で別個のViewportを用意し、GUIを格納したViewportの方ではCamera2DのZoomを使わないようにすれば、この問題は発生しません[5]

例として、以下の画像のような階層構造のシーンを挙げます。この例では "GUI" というノードの中にGUI用のViewportやドラッグ&ドロップ用のパーツを格納しています。GUI側の表示倍率を変更したい場合にはCamera2Dではなく、ViewportContainerstretch_shrinkプロパティを使うようにします。

どうしてもCamera2DのZoomと併用したい?

ドラッグ&ドロップのプレビューがCamera2Dを参照しない問題は、すぐに改善される状況にはありません。ですので、どうしてもCamera2DのZoomと併用したい場合には、ドラッグ&ドロップのプレビューの挙動のみ、別途実装するのが現実的かと思われます。

脚注
  1. 具体的な実装方法などは、この動画が非常に分かりやすいです。 ↩︎

  2. GithubにもIssueが複数あります(godot2.x/#11281, godot-docs/#4086)。なお、2021年8月現在、関連するプルリクもなく、すぐに改善される状況ではありません。 ↩︎

  3. 幸いにもズレるのはプレビューのみで、D&D自体の挙動はしっかりマウスカーソルに追従します。 ↩︎

  4. 私はこの挙動の原因がわからず、半日ほど悩みました。 ↩︎

  5. Viewportを(デフォルトの)1個しか使わない場合と比較して少し複雑ですが、ゲーム画面の構造としては比較的ポピュラーかと思います。 ↩︎

Discussion

Silc RenewSilc Renew

脚注2につきまして、
#11281 の issue は 2.x に対しての issue なので閉じられています。また、#4086 はそもそも docs 用のリポジトリなので、基本的にバグ修正者がそれを発見して修正に取り掛かろうとすることはないと思います。#11281 がアーカイブされた後、その issue の投稿者に 3.x のプロジェクトを用いて新たに issue を立てるように提案がされていますが...もしその issue が立て直されておらず、まだバグが発生しているようであれば、Godot 本体のリポジトリに issue を立てることをお勧めします。

SLMNLLSLMNLL

ありがとうございます。#11281はおっしゃる通りClosed → 3.2以降でもバグが再現されるというコメント → 新しいIssueのサジェストがされておりますね。
#4086には#11281から辿り着いたのですが、恥ずかしながらご指摘いただいてdocs用のリポジトリなことに気づきました。記事執筆時に調べた限りでは、現在このバグ・挙動について他にOpenなIssueや、関連するPull Requestなどがないようなので、暇を見つけてIssueを立ててみます。

追記:本文・脚注ともに、該当箇所がどのリポジトリかが分かりやすいようリライトしました。