プログラム実行時、モデルの動きにCollisionPolygon2Dを追従させたい
creeper-0910 opened this issue · comments
@creeper-0910
CollisionPolygon2Dの目的というのは、例えば口をクリックするとしゃべって、頭をドラッグすると笑顔になるといったインタラクションを目的とするものでしょうか。
もしそうでしたら、 CollisionPolygon2D を用意しなくても指定した座標とLive2D内のHitAreaやTriangleとのコリジョンを取る事が出来ますよ。
こちらはまだ API として公開する形を決めかねているのですけど、実装自体は完了していまして、 0.3系のデモに demo_effect_hit_area.tscn というサンプルを追加しています。
こちらは以下の要望を満たすために用意しました。
- あらかじめ Live2DのEditor 上で HitArea を用意しておき、 Godot Engine 上でその場所をクリックしたらシグナルを発行できるようにしたい。
- HitArea かどうかは関係なく、 Live2Dモデル上の任意の場所をクリック出来る様にしたい。
画面としては以下の様なものなのですが、動かしてみてもらえると理解しやすいと思います。お手元の 0.3 をアップデートして再ビルドを行うと利用可能になります。
![hitarea](https://private-user-images.githubusercontent.com/5047878/275367597-5461042d-df2c-423a-8ce8-c19df77d22a3.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjIzNTc2NTcsIm5iZiI6MTcyMjM1NzM1NywicGF0aCI6Ii81MDQ3ODc4LzI3NTM2NzU5Ny01NDYxMDQyZC1kZjJjLTQyM2EtOGNlOC1jMTlkZjc3ZDIyYTMucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDczMCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA3MzBUMTYzNTU3WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9MTc1ODk5ZWRjNmVlNmRiN2QyMjJmNDIyYjEwZjIyMWFmM2I3N2Q4MjdkNjczNWVjMDczMzgyYTk1M2RiMmQ4YiZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.NleOQUEmPiLmrzt_SEqU2AmnkUpU5utrFn4xisoszpo)
そうではなく、単純に塗りつぶしたポリゴンが欲しいという場合でしたら、 GDCubismUserMode の get_meshes から計算で得る方法もあります。 get_meshes で得られるデータは実際に描画に使用した頂点情報が格納されていますので、パーツ単位でよければ完全に一致した情報が得られます。
Live2Dの場合ですと、毎フレーム頂点を作り直しますので非力な環境だと辛いかもしれません。
もうすこし具体的な目的が教えていただければ、より適した方法が提案出来るかもしれません。
返信ありがとうございます、
デスクトップマスコットを作成しており、ウィンドウ全体の背景透過とクリックスルーのために、CollisionPolygon2Dを利用したいと考えております。
デスクトップマスコット風にしたいのでしたら、 Godot Engine の本体機能を使って実現できますよ。
元々 3.5 で使えたものが 4.0 では不具合があったようです。 4.1 では動作しています。
GDCubism では demo/addons/gd_cubism/example/demo_transparent.tscn というサンプルを用意していますので、そちらを実行してみてください。
具体的には以下の様なスクリプトを使って実現できます。
var enable_transparent: bool = true
get_tree().root.transparent_bg = enable_transparent
# 描画していない場所を透過
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_TRANSPARENT, enable_transparent, 0)
# 常に最前面に表示
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_ALWAYS_ON_TOP, true, 0)
Window が透明になっても見た目が透けているだけで Window 枠は依然として存在しています。
ですので、Window 枠をキャラクターぎりぎりのサイズにするといった工夫をしないとデスクトップマスコットとしてはちょっと使いづらいかも?
DisplayServer を参照していただくと、他にもクリップボード制御やアイテムのドロップ等に関する情報を見つける事が出来ます。
以下はXに貼り付けている動画(内容は上のスクリーンショットと同じもの)です。
https://twitter.com/MizunagiKB/status/1701230124186714597
こちらClickを除外したいという部分を失念していました。
画像データの透過処理は上で述べている方法で解決できますので、余計な部分にマウスを反応させたくない、というのをどうするかとなりますね。
処理としては DisplayServer.window_set_mouse_passthrough
関数に渡す PackedVector2Array をどう生成するかとなり、アプローチとしては以下の様なものが考えられます。
- GDCubismUserModel の
get_meshes
関数を使ってメッシュ情報を取得します。 - そこから代表的なメッシュのみを取り出します。
- 凸包というアルゴリズムを使って、頂点を囲むポリゴンを生成します。
提示されている画像では Live2Dモデルを綺麗にくり抜いていますが、自動生成で行おうとするとやや手間となりますので、ここでは周囲を囲むポリゴンを作る問題に置き換えます。
以下の様なイメージです。
凸包について
もしかしたら考える楽しさを奪ってしまうかもしれませんが、説明するのはちょっと難しいのでここではそういうものがあると捉えてください。
ここでは凸包を求めるのに Graham Scan という手法を用います。
コーディングは以下のサイトのものを Godot Engine 向けに移植しました。
GDScript で記述すると以下の様になります。
func check_cross(ary_check: Array, v: Vector2) -> bool:
var va: Vector2 = ary_check[ary_check.size() - 2]
var vb: Vector2 = ary_check[ary_check.size() - 1]
return (((vb.x - va.x) * (v.y - va.y)) - ((vb.y - va.y) * (v.x - va.x))) > 0
func convex_hull(ary_vertex: Array) -> Array:
ary_vertex.sort()
var ary_result: Array
var n = ary_vertex.size()
for vtx in ary_vertex:
while ary_result.size() > 1 and check_cross(ary_result, vtx):
ary_result.pop_back()
ary_result.push_back(vtx)
var t = ary_result.size()
var i = n - 2
while i >= 0:
var vtx: Vector2 = ary_vertex[i]
while ary_result.size() > t and check_cross(ary_result, vtx):
ary_result.pop_back()
ary_result.push_back(vtx)
i -= 1
ary_result.pop_back()
return ary_result
この関数を _process
関数内で呼び出して、 window_set_mouse_passthrough
関数に渡します。
func _process(delta):
var dict_mesh = $Sprite2D/GDCubismUserModel.get_meshes()
var ary: PackedVector2Array
# ArtMesh121 といった名称はLive2Dモデル毎に異なります。
ary += dict_mesh["ArtMesh121"].surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
ary += dict_mesh["ArtMesh122"].surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
ary += dict_mesh["ArtMesh135"].surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
ary += dict_mesh["ArtMesh146"].surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
ary += dict_mesh["ArtMesh147"].surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
ary += dict_mesh["ArtMesh231"].surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
ary += dict_mesh["ArtMesh278"].surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
var ary_poly = convex_hull(ary)
# ここでは Polygon2D がノードツリーに存在しているとしています。
# 直接 PackedVector2Array を渡せますが、 Polygon2D を経由すると表示が確認できるので。
$Polygon2D.polygon = PackedVector2Array(ary_poly)
DisplayServer.window_set_mouse_passthrough($Polygon2D.polygon)
結構重たい処理になりますので、基本姿勢の状態を一回だけ生成したり HitArea のみを生成するといった方法を併用するのが良さそうです。
参考になりましたらどうぞ。
@creeper-0910
こちらのコードですが、頂点情報の取り扱いサンプルとしてもわかりやすいものだと感じましたので 0.3 に取り込みました。
demo / addons / gd_cubism / example に保存ざれている demo_transparent.tscn を動かして貰えると、手元で実際の挙動が確認出来るかと思います。
遅くなってしまい申し訳ございません。
ありがとうございます!確認してみます
こちらのIssue、だいぶ経過していますどうでしょうか。
洗練されたコードではありませんが特に問題はないと思いますのでクローズ致します。