最初に
あまりにもGodotのスクリーンタッチに関する情報が少なくて苦戦したので、これが助けになるかはわかりませんが記事を書こうと思いました。
環境
- Godot 4.2.1
- Windows 11
経緯
GarageBandのピアノや、音ゲーのようなものを作りたいと思いGodotを勉強していました。しかし、マウスがそのオブジェクトを触っている状態かどうかという情報を入手する簡単な関数がないので、「鍵盤が押されている」「レーンを触っている」状態を入手することが非常に困難でした。
その状態を入手する方法を頑張って作ったので記していきます。
実際に作ったもの
動画の載せ方を知らないのでツイッターからになってしまいますが、このようなデモを作ってみました。
GitHubにもデモを載せておきました。初めてGitHub使ったので間違っているかもしれません。
作成
設定変更
コードに移る前にセッティングやノードなどを配置していきます。
まずProject Settings >> Input Devices
に行き、
Emulate Mouse From Touch
を無効にします。
タッチ判定ノードを作成
タッチ判定で色が変わるノードを作成していきます。
ノード構成はこのようになっています。形は好きな形で大丈夫です。
MeshInstance3D
については、色を変えないといけないので以下のようにSurface Material Override
にStandard Material 3D
を追加します。
ここで注意なのですが、Standard Material 3D
のResource
のLocal to Scene
をONにしないとCollideObject
の色が連動して変わってしまいます。
外観はこのようになりました。
白い[]はtouchingIndicesLabel
です。
メインノードを作成
ノード構成はこのようになっています。
上で作ったタッチ判定ノードのCollideObject
をCollideObjects
というNode3D
でひとまとめにします。
CollideObject
が一つ外にあるのは、最終的にCollideObjects
子ノードでないと判定されないかどうかを確認するためです。
スクリプトをアタッチ
CollideObjectに以下のスクリプトをアタッチします。
extends StaticBody3D
@onready var mesh_instance_3d = $MeshInstance3D
@onready var touching_indices_label = $touchingIndicesLabel
@onready var material = mesh_instance_3d.get_surface_override_material(0)
const OFF_COLOR = Color(0, 0, 0)
const ON_COLOR = Color(255, 255, 255)
var touchingIndices = {}
var was_touching = false
func _ready():
changeColor(OFF_COLOR)
func changeColor(color):
material.albedo_color = color
mesh_instance_3d.set_surface_override_material(0, material)
func screenTouchUpdate(event, colliding:bool):
var val = event.index
if colliding:
touchingIndices[val] = event.position
else:
touchingIndices.erase(val)
var is_touching = not touchingIndices.is_empty()
if was_touching != is_touching:
if is_touching:#just pressed / entered
changeColor(ON_COLOR)
else:#just released / left
changeColor(OFF_COLOR)
was_touching = is_touching
touching_indices_label.text = str(touchingIndices.keys())
メインノードのCollideObjects
に以下のスクリプトをアタッチします。
extends Node3D
@onready var main_camera = $"../MainCamera"
func getIntersect(pos):
var worldspace = get_world_3d().direct_space_state
var start = main_camera.project_ray_origin(pos)
var end = main_camera.project_position(pos, 1000)
var parameters = PhysicsRayQueryParameters3D.create(start, end)
var result = worldspace.intersect_ray(parameters)
return result
func _input(event):
#mouse debug
if event is InputEventMouse:
if event.is_pressed():
print(getIntersect(event.position))
elif event is InputEventScreenTouch or event is InputEventScreenDrag:
get_viewport().set_input_as_handled()
var collider = null
if event is InputEventScreenTouch and event.is_released():#touch released
collider = null
else:#touch pressed / entered
var intersect = getIntersect(event.position)
if not intersect.is_empty():
collider = intersect["collider"]
for collideObject in get_children():
var colliding = collider == collideObject
collideObject.screenTouchUpdate(event, colliding)
これで完成です。
参考にした情報
上記のgetIntersect
関数に関してはこの動画を参考にしました。
複数指のトラッキングについてはこのサイトを参考にしました。
その他