Added touchscreen controls!

This commit is contained in:
2026-01-13 22:47:30 +01:00
parent 89a41397d1
commit 0c0998cd05
65 changed files with 1672 additions and 3270 deletions

View File

@@ -0,0 +1,135 @@
@tool
@icon("TouchScreenButtonControl.svg")
class_name TouchScreenButtonControl
extends TextureButton
const DefaultValues := {
"expand": true,
"ignore_texture_size": true,
"stretch_mode": TextureButton.STRETCH_KEEP_ASPECT_CENTERED,
"action_mode": TextureButton.ACTION_MODE_BUTTON_PRESS,
"focus_mode": TextureButton.FOCUS_NONE,
}
@export var use_default_values := true
@export var touchscreen_only := false
# The actual variable that holds the data
var input_action: String = ""
# 2. MATCHED the property name here...
func _get_property_list() -> Array:
var properties: Array = []
if Engine.is_editor_hint():
InputMap.load_from_project_settings()
# Get actions and format them for the dropdown
var actions = InputMap.get_actions()
var action_list = ",".join(actions)
properties.append({
"name": "input_action",
"type": TYPE_STRING,
"hint": PROPERTY_HINT_ENUM_SUGGESTION,
"hint_string": ",".join(actions),
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR
})
return properties
func _set(property: StringName, value: Variant) -> bool:
if property == "input_action":
input_action = value
return true
return false
func _get(property: StringName) -> Variant:
if property == "input_action":
return input_action
return null
# Track multiple simultaneous touches for multitouch support
var active_touches := {} # Dictionary: touch_index -> bool (true if pressed)
var initial_press_touches := {} # Dictionary: touch_index -> bool (true if this button was pressed on initial touch)
func _init():
if use_default_values:
for k in DefaultValues.keys():
self.set(k, DefaultValues.get(k))
# Set mouse_filter to IGNORE so the button doesn't consume touch events
# This allows multiple buttons to detect the same touch simultaneously
mouse_filter = Control.MOUSE_FILTER_IGNORE
if touchscreen_only and not DisplayServer.is_touchscreen_available():
hide()
func press():
var input_event: InputEvent = InputMap.action_get_events(input_action)[0]
input_event.pressed = true
Input.parse_input_event(input_event)
func release():
var input_event: InputEvent = InputMap.action_get_events(input_action)[0]
input_event.pressed = false
Input.parse_input_event(input_event)
func is_in(pos: Vector2) -> bool:
# Get the global rect of this Control node in screen coordinates
var global_rect = get_global_rect()
# Check if the touch position is within the button's global rect
if pos.x >= global_rect.position.x and pos.x <= global_rect.position.x + global_rect.size.x:
if pos.y >= global_rect.position.y and pos.y <= global_rect.position.y + global_rect.size.y:
return true
return false
func _input(event):
# Use _input to receive all touch events
# IMPORTANT: _input is called on ALL nodes in the scene tree, so multiple buttons
# can receive the same event. We don't consume the event so others can too.
if event is InputEventScreenTouch:
if event.pressed:
# Check if this touch is within this button's bounds
if is_in(event.position):
# Start tracking this touch for this button
if event.index not in active_touches:
active_touches[event.index] = true
initial_press_touches[event.index] = true # Mark as initial press
press()
else:
# Touch released - check if we were tracking it
if event.index in active_touches:
# This touch was being tracked by this button, release it
active_touches.erase(event.index)
initial_press_touches.erase(event.index)
# Only release the action if no other touches are active for this button
if active_touches.is_empty():
release()
elif event is InputEventScreenDrag:
# Handle touch drag with smart re-activation logic
if event.index in active_touches:
# We're tracking this touch
if initial_press_touches.has(event.index):
# This was an initial press - keep it active even if touch moves outside
# Don't release until touch ends
pass
else:
# This was a re-activation (touch moved back into bounds)
# Check if touch moved back out of bounds
if not is_in(event.position):
# Touch moved out of bounds, release this re-activated button
active_touches.erase(event.index)
if active_touches.is_empty():
release()
else:
# We're not tracking this touch - check if it moved into our bounds
# This allows "tapping" behavior - touch can re-enter and activate button
if is_in(event.position):
# Touch moved into bounds, start tracking (but not as initial press)
active_touches[event.index] = true
press()
# CRITICAL: Never call get_viewport().set_input_as_handled() here
# This would prevent other buttons from receiving the same touch event
# mouse_filter is set to IGNORE so the TextureButton doesn't consume mouse/touch events

View File

@@ -0,0 +1 @@
uid://bh5a3ydiu51eo

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><defs/><g><path d="M3,1 C2.7249,1 2.4771,1.1087 2.2929,1.2929 C2.1087,1.4771 2,1.7249 2,2 L2,4 C2,4.2751 2.1087,4.5229 2.2929,4.7071 C2.4771,4.8913 2.7249,5 3,5 L5,5 L5,4 L3,4 L3,2 L11,2 L11,4 L9,4 L9,5 L11,5 C11.2751,5 11.5229,4.8913 11.7071,4.7071 C11.8913,4.5229 12,4.2751 12,4 L12,2 C12,1.7249 11.8913,1.4771 11.7071,1.2929 C11.5229,1.1087 11.2751,1 11,1 L3,1 Z M8,8 L8,4 C8,3.4015 7.5531,3 7,3 C6.4469,3 6,3.4015 6,4 L6,11.05 L3.5,9.25 C2.9376,8.8026 2.2507,8.8435 1.8125,9.3125 C1.3692,9.787 1.3561,10.4968 1.875,11 L6,15 L12,15 C12.5502,15 13.0459,14.7826 13.4142,14.4142 C13.7826,14.0459 14,13.5502 14,13 L14,10 C14,9.4498 13.7826,8.9541 13.4142,8.5858 C13.0459,8.2174 12.5502,8 12,8 L8,8 Z" fill="rgb(142,239,151)" opacity="1"/></g></svg>

After

Width:  |  Height:  |  Size: 807 B

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ckvwnehwdvwyq"
path="res://.godot/imported/TouchScreenButtonControl.svg-ec8e7f48e271699010d76650daf1583d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/touchscreenbuttoncontrol/TouchScreenButtonControl.svg"
dest_files=["res://.godot/imported/TouchScreenButtonControl.svg-ec8e7f48e271699010d76650daf1583d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://dsj828xm2e28w"]
[ext_resource type="Script" path="res://addons/touchscreenbuttoncontrol/TouchScreenButtonControl.gd" id="1_5k641"]
[node name="TouchScreenButtonControl" type="TextureButton"]
script = ExtResource("1_5k641")

View File

@@ -0,0 +1,15 @@
@tool
extends EditorPlugin
var MyCustomNode = preload("res://addons/touchscreenbuttoncontrol/TouchScreenButtonControl.gd")
func _enter_tree():
add_custom_type(
"TouchScreenButtonControl",
"Control",
TouchScreenButtonControl,
preload("res://addons/touchscreenbuttoncontrol/TouchScreenButtonControl.svg")
)
func _exit_tree():
remove_custom_type("TouchScreenButtonControl")

View File

@@ -0,0 +1 @@
uid://cq0bml8epsqa

View File

@@ -0,0 +1,13 @@
[gd_resource type="StyleBoxFlat" format=3 uid="uid://cv5o175a2677e"]
[resource]
bg_color = Color(0.3, 0.3, 0.3, 0.7)
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
border_color = Color(0.8, 0.8, 0.8, 0.9)
corner_radius_top_left = 40
corner_radius_top_right = 40
corner_radius_bottom_right = 40
corner_radius_bottom_left = 40

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><defs/><g><path d="M3,1 C2.7249,1 2.4771,1.1087 2.2929,1.2929 C2.1087,1.4771 2,1.7249 2,2 L2,4 C2,4.2751 2.1087,4.5229 2.2929,4.7071 C2.4771,4.8913 2.7249,5 3,5 L5,5 L5,4 L3,4 L3,2 L11,2 L11,4 L9,4 L9,5 L11,5 C11.2751,5 11.5229,4.8913 11.7071,4.7071 C11.8913,4.5229 12,4.2751 12,4 L12,2 C12,1.7249 11.8913,1.4771 11.7071,1.2929 C11.5229,1.1087 11.2751,1 11,1 L3,1 Z M8,8 L8,4 C8,3.4015 7.5531,3 7,3 C6.4469,3 6,3.4015 6,4 L6,11.05 L3.5,9.25 C2.9376,8.8026 2.2507,8.8435 1.8125,9.3125 C1.3692,9.787 1.3561,10.4968 1.875,11 L6,15 L12,15 C12.5502,15 13.0459,14.7826 13.4142,14.4142 C13.7826,14.0459 14,13.5502 14,13 L14,10 C14,9.4498 13.7826,8.9541 13.4142,8.5858 C13.0459,8.2174 12.5502,8 12,8 L8,8 Z" fill="rgb(142,239,151)" opacity="1"/></g></svg>

After

Width:  |  Height:  |  Size: 807 B

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://x5ggo5vlsqwn"
path="res://.godot/imported/icon.svg-e7770945a21c9fca528355bbf6b1708f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/touchscreenbuttoncontrol/icon.svg"
dest_files=["res://.godot/imported/icon.svg-e7770945a21c9fca528355bbf6b1708f.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,7 @@
[plugin]
name="TouchScreenButtonControl"
description="A Control Node version to TouchScreenButton2D"
author="Matata.exe"
version="1.0"
script="TouchScreenButtonControlPlugin.gd"