Added introductory touchscreen support for mobile devices.

This commit is contained in:
Henry 2026-03-10 12:55:43 +00:00
parent df13ee42d8
commit 4c512379c5
34 changed files with 697 additions and 1 deletions

View file

@ -0,0 +1,7 @@
[plugin]
name="Virtual Joystick"
description="A simple virtual joystick for touchscreens, with useful options."
author="Marco Fazio"
version="1.0"
script="virtual_joystick_plugin.gd"

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 292 KiB

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dyiqtaavx0hc6"
path="res://.godot/imported/CoverPreview.svg-7c17dc7ed6007b5f94a5e30c30227a42.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/virtual_joystick/previews/CoverPreview.svg"
dest_files=["res://.godot/imported/CoverPreview.svg-7c17dc7ed6007b5f94a5e30c30227a42.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 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" zoomAndPan="magnify" viewBox="0 0 150 149.999998" height="200" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><clipPath id="97d15a9768"><path d="M 9.464844 12.230469 L 134.714844 12.230469 L 134.714844 137.480469 L 9.464844 137.480469 Z M 9.464844 12.230469 " clip-rule="nonzero"/></clipPath><clipPath id="c3001683e3"><path d="M 72.230469 12.230469 C 37.566406 12.230469 9.464844 40.332031 9.464844 74.996094 C 9.464844 109.664062 37.566406 137.761719 72.230469 137.761719 C 106.894531 137.761719 134.996094 109.664062 134.996094 74.996094 C 134.996094 40.332031 106.894531 12.230469 72.230469 12.230469 Z M 72.230469 12.230469 " clip-rule="nonzero"/></clipPath><clipPath id="4c84864984"><path d="M 35.820312 38.585938 L 108.644531 38.585938 L 108.644531 111.414062 L 35.820312 111.414062 Z M 35.820312 38.585938 " clip-rule="nonzero"/></clipPath><clipPath id="a8aed88fce"><path d="M 72.230469 38.585938 C 52.121094 38.585938 35.820312 54.890625 35.820312 75 C 35.820312 95.109375 52.121094 111.414062 72.230469 111.414062 C 92.34375 111.414062 108.644531 95.109375 108.644531 75 C 108.644531 54.890625 92.34375 38.585938 72.230469 38.585938 Z M 72.230469 38.585938 " clip-rule="nonzero"/></clipPath><clipPath id="f2c20a4ed7"><path d="M 66.226562 45.125 L 78.257812 45.125 L 78.257812 55.636719 L 66.226562 55.636719 Z M 66.226562 45.125 " clip-rule="nonzero"/></clipPath><clipPath id="5bd6a5723d"><path d="M 72.230469 45.125 L 78.238281 55.636719 L 66.226562 55.636719 Z M 72.230469 45.125 " clip-rule="nonzero"/></clipPath><clipPath id="a7809b5ea4"><path d="M 66.207031 94.351562 L 78.238281 94.351562 L 78.238281 104.863281 L 66.207031 104.863281 Z M 66.207031 94.351562 " clip-rule="nonzero"/></clipPath><clipPath id="12c2b9b344"><path d="M 72.230469 104.863281 L 66.226562 94.351562 L 78.238281 94.351562 Z M 72.230469 104.863281 " clip-rule="nonzero"/></clipPath><clipPath id="718557a13c"><path d="M 90.886719 68.988281 L 101.398438 68.988281 L 101.398438 81 L 90.886719 81 Z M 90.886719 68.988281 " clip-rule="nonzero"/></clipPath><clipPath id="d18f567ffc"><path d="M 101.398438 74.996094 L 90.886719 81 L 90.886719 68.988281 Z M 101.398438 74.996094 " clip-rule="nonzero"/></clipPath><clipPath id="9103f01bde"><path d="M 43.066406 68.96875 L 53.578125 68.96875 L 53.578125 81 L 43.066406 81 Z M 43.066406 68.96875 " clip-rule="nonzero"/></clipPath><clipPath id="7a707734da"><path d="M 43.066406 74.992188 L 53.578125 68.988281 L 53.578125 81 Z M 43.066406 74.992188 " clip-rule="nonzero"/></clipPath></defs><rect x="-15" width="180" fill="#ffffff" y="-15" height="179.999997" fill-opacity="1"/><rect x="-15" width="180" fill="#0194ff" y="-15" height="179.999997" fill-opacity="1"/><g clip-path="url(#97d15a9768)"><g clip-path="url(#c3001683e3)"><path stroke-linecap="butt" transform="matrix(0.75, 0, 0, 0.75, 9.464658, 12.232329)" fill="none" stroke-linejoin="miter" d="M 83.687752 -0.00248061 C 37.469 -0.00248061 0.000247376 37.466272 0.000247376 83.685024 C 0.000247376 129.908985 37.469 167.372529 83.687752 167.372529 C 129.906505 167.372529 167.375257 129.908985 167.375257 83.685024 C 167.375257 37.466272 129.906505 -0.00248061 83.687752 -0.00248061 Z M 83.687752 -0.00248061 " stroke="#ffffff" stroke-width="18" stroke-opacity="1" stroke-miterlimit="4"/></g></g><g clip-path="url(#4c84864984)"><g clip-path="url(#a8aed88fce)"><path fill="#ffffff" d="M 35.820312 38.585938 L 108.644531 38.585938 L 108.644531 111.414062 L 35.820312 111.414062 Z M 35.820312 38.585938 " fill-opacity="1" fill-rule="nonzero"/></g></g><g clip-path="url(#f2c20a4ed7)"><g clip-path="url(#5bd6a5723d)"><path fill="#131313" d="M 66.226562 45.125 L 78.257812 45.125 L 78.257812 55.636719 L 66.226562 55.636719 Z M 66.226562 45.125 " fill-opacity="1" fill-rule="nonzero"/></g></g><g clip-path="url(#a7809b5ea4)"><g clip-path="url(#12c2b9b344)"><path fill="#131313" d="M 78.238281 104.863281 L 66.207031 104.863281 L 66.207031 94.351562 L 78.238281 94.351562 Z M 78.238281 104.863281 " fill-opacity="1" fill-rule="nonzero"/></g></g><g clip-path="url(#718557a13c)"><g clip-path="url(#d18f567ffc)"><path fill="#131313" d="M 101.398438 68.988281 L 101.398438 81.019531 L 90.886719 81.019531 L 90.886719 68.988281 Z M 101.398438 68.988281 " fill-opacity="1" fill-rule="nonzero"/></g></g><g clip-path="url(#9103f01bde)"><g clip-path="url(#7a707734da)"><path fill="#131313" d="M 43.066406 81 L 43.066406 68.96875 L 53.578125 68.96875 L 53.578125 81 Z M 43.066406 81 " fill-opacity="1" fill-rule="nonzero"/></g></g></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dauwds7y6kul0"
path="res://.godot/imported/Logo.svg-ff6e2b43579b0b50d493f1bf1feaff55.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/virtual_joystick/previews/Logo.svg"
dest_files=["res://.godot/imported/Logo.svg-ff6e2b43579b0b50d493f1bf1feaff55.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

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

View file

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bjorj4u4mywl2"
path="res://.godot/imported/ShowcasePreview.png-70f7a50f53e5fc814a0e306c3f1e556a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/virtual_joystick/previews/ShowcasePreview.png"
dest_files=["res://.godot/imported/ShowcasePreview.png-70f7a50f53e5fc814a0e306c3f1e556a.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

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bawnhue5kyj0a"
path="res://.godot/imported/joystick_icon.png-6e8744e631946e765d6836c0ecb27227.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/virtual_joystick/previews/joystick_icon.png"
dest_files=["res://.godot/imported/joystick_icon.png-6e8744e631946e765d6836c0ecb27227.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

View file

@ -0,0 +1,23 @@
extends Sprite2D
@export var speed : float = 100
@export var joystick_left : VirtualJoystick
@export var joystick_right : VirtualJoystick
var move_vector := Vector2.ZERO
func _process(delta: float) -> void:
## Movement using the joystick output:
# if joystick_left and joystick_left.is_pressed:
# position += joystick_left.output * speed * delta
## Movement using Input functions:
move_vector = Vector2.ZERO
move_vector = Input.get_vector("ui_left","ui_right","ui_up","ui_down")
position += move_vector * speed * delta
# Rotation:
if joystick_right and joystick_right.is_pressed:
rotation = joystick_right.output.angle()

View file

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

View file

@ -0,0 +1,34 @@
[gd_scene format=3 uid="uid://bq2sqb1u1l5ve"]
[ext_resource type="PackedScene" uid="uid://dmr0fcamx7t56" path="res://addons/virtual_joystick/virtual_joystick_scene.tscn" id="1_4k4lh"]
[ext_resource type="Texture2D" uid="uid://dy743vehs0t1t" path="res://icon.svg" id="2_44wa8"]
[ext_resource type="Script" uid="uid://dbehmxybmfqcy" path="res://addons/virtual_joystick/test/player.gd" id="3_dsmxw"]
[node name="Test" type="Node2D" unique_id=349797032]
[node name="UI" type="CanvasLayer" parent="." unique_id=1633235312]
[node name="Virtual joystick left" parent="UI" unique_id=1988201088 instance=ExtResource("1_4k4lh")]
offset_left = 159.0
offset_top = -81.0
offset_right = 459.0
offset_bottom = 219.0
[node name="Virtual joystick right" parent="UI" unique_id=1109766981 instance=ExtResource("1_4k4lh")]
anchors_preset = 3
anchor_left = 1.0
anchor_right = 1.0
offset_left = -232.0
offset_top = -81.0
offset_right = 68.0
offset_bottom = 219.0
grow_horizontal = 0
joystick_mode = 1
use_input_actions = false
[node name="Player" type="Sprite2D" parent="." unique_id=1232070811 node_paths=PackedStringArray("joystick_left", "joystick_right")]
position = Vector2(108, 134)
texture = ExtResource("2_44wa8")
script = ExtResource("3_dsmxw")
joystick_left = NodePath("../UI/Virtual joystick left")
joystick_right = NodePath("../UI/Virtual joystick right")

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View file

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bm30au8mjfc2f"
path="res://.godot/imported/joystick_base_outline.png-1529fbc0a23b5af9e961e1a3d047aa0b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/virtual_joystick/textures/joystick_base_outline.png"
dest_files=["res://.godot/imported/joystick_base_outline.png-1529fbc0a23b5af9e961e1a3d047aa0b.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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bkvmydfn5saxg"
path="res://.godot/imported/joystick_tip.png-95989d4495f4fb8387b93ac5f53ed669.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/virtual_joystick/textures/joystick_tip.png"
dest_files=["res://.godot/imported/joystick_tip.png-95989d4495f4fb8387b93ac5f53ed669.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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dt13r06u87fib"
path="res://.godot/imported/joystick_tip_arrows.png-cd5fb367005f4e3e79a192d69a489560.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/virtual_joystick/textures/joystick_tip_arrows.png"
dest_files=["res://.godot/imported/joystick_tip_arrows.png-cd5fb367005f4e3e79a192d69a489560.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

View file

@ -0,0 +1,174 @@
class_name VirtualJoystick
extends Control
## A simple virtual joystick for touchscreens, with useful options.
## Github: https://github.com/MarcoFazioRandom/Virtual-Joystick-Godot
# EXPORTED VARIABLE
## The color of the button when the joystick is pressed.
@export var pressed_color := Color.GRAY
## If the input is inside this range, the output is zero.
@export_range(0, 200, 1) var deadzone_size : float = 10
## The max distance the tip can reach.
@export_range(0, 500, 1) var clampzone_size : float = 75
enum Joystick_mode {
FIXED, ## The joystick doesn't move.
DYNAMIC, ## Every time the joystick area is pressed, the joystick position is set on the touched position.
FOLLOWING ## When the finger moves outside the joystick area, the joystick will follow it.
}
## If the joystick stays in the same position or appears on the touched position when touch is started
@export var joystick_mode := Joystick_mode.FIXED
enum Visibility_mode {
ALWAYS, ## Always visible
TOUCHSCREEN_ONLY, ## Visible on touch screens only
WHEN_TOUCHED ## Visible only when touched
}
## If the joystick is always visible, or is shown only if there is a touchscreen
@export var visibility_mode := Visibility_mode.ALWAYS
## If true, the joystick uses Input Actions (Project -> Project Settings -> Input Map)
@export var use_input_actions := true
@export var action_left := "ui_left"
@export var action_right := "ui_right"
@export var action_up := "ui_up"
@export var action_down := "ui_down"
# PUBLIC VARIABLES
## If the joystick is receiving inputs.
var is_pressed := false
# The joystick output.
var output := Vector2.ZERO
# PRIVATE VARIABLES
var _touch_index : int = -1
@onready var _base := $Base
@onready var _tip := $Base/Tip
@onready var _base_default_position : Vector2 = _base.position
@onready var _tip_default_position : Vector2 = _tip.position
@onready var _default_color : Color = _tip.modulate
# FUNCTIONS
func _ready() -> void:
if ProjectSettings.get_setting("input_devices/pointing/emulate_mouse_from_touch"):
printerr("The Project Setting 'emulate_mouse_from_touch' should be set to False")
if not ProjectSettings.get_setting("input_devices/pointing/emulate_touch_from_mouse"):
printerr("The Project Setting 'emulate_touch_from_mouse' should be set to True")
if not DisplayServer.is_touchscreen_available() and visibility_mode == Visibility_mode.TOUCHSCREEN_ONLY :
hide()
if visibility_mode == Visibility_mode.WHEN_TOUCHED:
hide()
func _input(event: InputEvent) -> void:
if event is InputEventScreenTouch:
if event.pressed:
if _is_point_inside_joystick_area(event.position) and _touch_index == -1:
if joystick_mode == Joystick_mode.DYNAMIC or joystick_mode == Joystick_mode.FOLLOWING or (joystick_mode == Joystick_mode.FIXED and _is_point_inside_base(event.position)):
if joystick_mode == Joystick_mode.DYNAMIC or joystick_mode == Joystick_mode.FOLLOWING:
_move_base(event.position)
if visibility_mode == Visibility_mode.WHEN_TOUCHED:
show()
_touch_index = event.index
_tip.modulate = pressed_color
_update_joystick(event.position)
get_viewport().set_input_as_handled()
elif event.index == _touch_index:
_reset()
if visibility_mode == Visibility_mode.WHEN_TOUCHED:
hide()
get_viewport().set_input_as_handled()
elif event is InputEventScreenDrag:
if event.index == _touch_index:
_update_joystick(event.position)
get_viewport().set_input_as_handled()
func _move_base(new_position: Vector2) -> void:
_base.global_position = new_position - _base.pivot_offset * get_global_transform_with_canvas().get_scale()
func _move_tip(new_position: Vector2) -> void:
_tip.global_position = new_position - _tip.pivot_offset * _base.get_global_transform_with_canvas().get_scale()
func _is_point_inside_joystick_area(point: Vector2) -> bool:
var x: bool = point.x >= global_position.x and point.x <= global_position.x + (size.x * get_global_transform_with_canvas().get_scale().x)
var y: bool = point.y >= global_position.y and point.y <= global_position.y + (size.y * get_global_transform_with_canvas().get_scale().y)
return x and y
func _get_base_radius() -> Vector2:
return _base.size * _base.get_global_transform_with_canvas().get_scale() / 2
func _is_point_inside_base(point: Vector2) -> bool:
var _base_radius = _get_base_radius()
var center : Vector2 = _base.global_position + _base_radius
var vector : Vector2 = point - center
if vector.length_squared() <= _base_radius.x * _base_radius.x:
return true
else:
return false
func _update_joystick(touch_position: Vector2) -> void:
var _base_radius = _get_base_radius()
var center : Vector2 = _base.global_position + _base_radius
var vector : Vector2 = touch_position - center
vector = vector.limit_length(clampzone_size)
if joystick_mode == Joystick_mode.FOLLOWING and touch_position.distance_to(center) > clampzone_size:
_move_base(touch_position - vector)
_move_tip(center + vector)
if vector.length_squared() > deadzone_size * deadzone_size:
is_pressed = true
output = (vector - (vector.normalized() * deadzone_size)) / (clampzone_size - deadzone_size)
else:
is_pressed = false
output = Vector2.ZERO
if use_input_actions:
# Release actions
if output.x >= 0 and Input.is_action_pressed(action_left):
Input.action_release(action_left)
if output.x <= 0 and Input.is_action_pressed(action_right):
Input.action_release(action_right)
if output.y >= 0 and Input.is_action_pressed(action_up):
Input.action_release(action_up)
if output.y <= 0 and Input.is_action_pressed(action_down):
Input.action_release(action_down)
# Press actions
if output.x < 0:
Input.action_press(action_left, -output.x)
if output.x > 0:
Input.action_press(action_right, output.x)
if output.y < 0:
Input.action_press(action_up, -output.y)
if output.y > 0:
Input.action_press(action_down, output.y)
func _reset():
is_pressed = false
output = Vector2.ZERO
_touch_index = -1
_tip.modulate = _default_color
_base.position = _base_default_position
_tip.position = _tip_default_position
# Release actions
if use_input_actions:
for action in [action_left, action_right, action_down, action_up]:
if Input.is_action_pressed(action):
Input.action_release(action)

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dyngdl4xiinc"
path="res://.godot/imported/virtual_joystick_icon.png-de46b24999f9aaea71ffaa7771da41e6.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/virtual_joystick/virtual_joystick_icon.png"
dest_files=["res://.godot/imported/virtual_joystick_icon.png-de46b24999f9aaea71ffaa7771da41e6.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

View file

@ -0,0 +1,17 @@
@tool
extends Control
var scene
func _enter_tree():
scene = preload("res://addons/virtual_joystick/virtual_joystick_scene.tscn").instantiate()
add_child(scene)
if ProjectSettings.get_setting("input_devices/pointing/emulate_mouse_from_touch"):
printerr("The Project Setting 'emulate_mouse_from_touch' should be set to False")
if not ProjectSettings.get_setting("input_devices/pointing/emulate_touch_from_mouse"):
printerr("The Project Setting 'emulate_touch_from_mouse' should be set to True")
func _exit_tree():
scene.free()

View file

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

View file

@ -0,0 +1,10 @@
@tool
extends EditorPlugin
func _enter_tree():
add_custom_type("Virtual Joystick", "Control", preload("virtual_joystick_instantiator.gd"), preload("virtual_joystick_icon.png"))
func _exit_tree():
remove_custom_type("Virtual Joystick")

View file

@ -0,0 +1 @@
uid://8psmrjhl03dp

View file

@ -0,0 +1,53 @@
[gd_scene format=3 uid="uid://dmr0fcamx7t56"]
[ext_resource type="Script" uid="uid://chxcpeg4d1phj" path="res://addons/virtual_joystick/virtual_joystick.gd" id="1_8x4dy"]
[ext_resource type="Texture2D" uid="uid://bm30au8mjfc2f" path="res://addons/virtual_joystick/textures/joystick_base_outline.png" id="2_jhjs2"]
[ext_resource type="Texture2D" uid="uid://dt13r06u87fib" path="res://addons/virtual_joystick/textures/joystick_tip_arrows.png" id="3_3etdg"]
[node name="Virtual Joystick" type="Control" unique_id=2019740770]
layout_mode = 3
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = -0.99999994
offset_top = -104.0
offset_right = 299.0
offset_bottom = 196.0
grow_vertical = 0
scale = Vector2(0.35, 0.35)
script = ExtResource("1_8x4dy")
[node name="Base" type="TextureRect" parent="." unique_id=1338665392]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -100.0
offset_top = -100.0
offset_right = 100.0
offset_bottom = 100.0
grow_horizontal = 2
grow_vertical = 2
pivot_offset = Vector2(100, 100)
mouse_force_pass_scroll_events = false
texture = ExtResource("2_jhjs2")
stretch_mode = 5
[node name="Tip" type="TextureRect" parent="Base" unique_id=1182099131]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -50.0
offset_top = -50.0
offset_right = 50.0
offset_bottom = 50.0
grow_horizontal = 2
grow_vertical = 2
pivot_offset = Vector2(50, 50)
texture = ExtResource("3_3etdg")
stretch_mode = 5

View file

@ -8,7 +8,7 @@ custom_features=""
export_filter="all_resources" export_filter="all_resources"
include_filter="*" include_filter="*"
exclude_filter="" exclude_filter=""
export_path="../../Downloads/2026-03-08 121752 The Third Place/index.html" export_path="../../Downloads/2026-03-10 105009 The Third Place/index.html"
patches=PackedStringArray() patches=PackedStringArray()
patch_delta_encoding=false patch_delta_encoding=false
patch_delta_compression_level_zstd=19 patch_delta_compression_level_zstd=19

View file

@ -61,6 +61,10 @@ shoot={
] ]
} }
[input_devices]
pointing/emulate_mouse_from_touch=false
[rendering] [rendering]
textures/canvas_textures/default_texture_filter=0 textures/canvas_textures/default_texture_filter=0

View file

@ -84,6 +84,7 @@ func _on_shoot_timer_timeout():
else: else:
$ShootTimer.start() $ShootTimer.start()
func _process(delta): func _process(delta):
if stunned == true: if stunned == true:

View file

@ -19,6 +19,7 @@ const ROWS: int = 3
@onready var start_button = $CanvasLayer/CenterContainer/Start @onready var start_button = $CanvasLayer/CenterContainer/Start
@onready var game_over = $CanvasLayer/CenterContainer/GameOver @onready var game_over = $CanvasLayer/CenterContainer/GameOver
@onready var virtual_controls = $CanvasLayer/VirtualControls
func _ready(): func _ready():
game_over.hide() game_over.hide()
@ -29,6 +30,7 @@ func _ready():
EventBus.flash_screen.connect(_on_flash_screen) EventBus.flash_screen.connect(_on_flash_screen)
EventBus.win_game.connect(_on_win_game) EventBus.win_game.connect(_on_win_game)
EventBus.secret_win_game.connect(_on_secret_win_game) EventBus.secret_win_game.connect(_on_secret_win_game)
virtual_controls.hide()
#func spawn_enemies(): #func spawn_enemies():
@ -139,6 +141,12 @@ func new_game():
func _input(EventInput): func _input(EventInput):
if EventInput is InputEventScreenTouch:
if EventInput.pressed:
virtual_controls.show()
if EventInput.is_action_pressed("shoot") and playing == false: if EventInput.is_action_pressed("shoot") and playing == false:
print("Input detected!") print("Input detected!")
start_button.hide() start_button.hide()

View file

@ -2,6 +2,7 @@
[ext_resource type="Script" uid="uid://c51huloycn5as" path="res://scenes/main.gd" id="1_h2yge"] [ext_resource type="Script" uid="uid://c51huloycn5as" path="res://scenes/main.gd" id="1_h2yge"]
[ext_resource type="PackedScene" uid="uid://tlcdxoupi2lb" path="res://scenes/parallax_clouds.tscn" id="2_5vw27"] [ext_resource type="PackedScene" uid="uid://tlcdxoupi2lb" path="res://scenes/parallax_clouds.tscn" id="2_5vw27"]
[ext_resource type="PackedScene" uid="uid://dmr0fcamx7t56" path="res://addons/virtual_joystick/virtual_joystick_scene.tscn" id="3_jbj1t"]
[ext_resource type="Texture2D" uid="uid://b25w6ms7bxlhu" path="res://sprites/third space bgrnd.png" id="3_tipki"] [ext_resource type="Texture2D" uid="uid://b25w6ms7bxlhu" path="res://sprites/third space bgrnd.png" id="3_tipki"]
[ext_resource type="PackedScene" uid="uid://s6wf3egdqtmh" path="res://scenes/ui.tscn" id="4_1bvp3"] [ext_resource type="PackedScene" uid="uid://s6wf3egdqtmh" path="res://scenes/ui.tscn" id="4_1bvp3"]
[ext_resource type="Shader" uid="uid://x02irwg8ynvp" path="res://shaders/pixel_highlight.gdshader" id="4_272bh"] [ext_resource type="Shader" uid="uid://x02irwg8ynvp" path="res://shaders/pixel_highlight.gdshader" id="4_272bh"]
@ -11,6 +12,7 @@
[ext_resource type="Shader" uid="uid://blxo4qgrkjiup" path="res://shaders/flash.gdshader" id="8_eb6dy"] [ext_resource type="Shader" uid="uid://blxo4qgrkjiup" path="res://shaders/flash.gdshader" id="8_eb6dy"]
[ext_resource type="PackedScene" uid="uid://cd1kwb8klcsb5" path="res://scenes/goose.tscn" id="9_85g3d"] [ext_resource type="PackedScene" uid="uid://cd1kwb8klcsb5" path="res://scenes/goose.tscn" id="9_85g3d"]
[ext_resource type="AudioStream" uid="uid://c3e8sdfqcikp6" path="res://sounds/welcome.wav" id="10_choun"] [ext_resource type="AudioStream" uid="uid://c3e8sdfqcikp6" path="res://sounds/welcome.wav" id="10_choun"]
[ext_resource type="Texture2D" uid="uid://bkvmydfn5saxg" path="res://addons/virtual_joystick/textures/joystick_tip.png" id="10_muem4"]
[ext_resource type="PackedScene" uid="uid://drf1k1iulvy6f" path="res://scenes/wave.tscn" id="11_ya4ey"] [ext_resource type="PackedScene" uid="uid://drf1k1iulvy6f" path="res://scenes/wave.tscn" id="11_ya4ey"]
[ext_resource type="AudioStream" uid="uid://i61hg7wispq6" path="res://sounds/Ambient 2.0.wav" id="12_trceg"] [ext_resource type="AudioStream" uid="uid://i61hg7wispq6" path="res://sounds/Ambient 2.0.wav" id="12_trceg"]
[ext_resource type="AudioStream" uid="uid://mjhprrmwn7qk" path="res://sounds/radio_tune.wav" id="14_a8y0u"] [ext_resource type="AudioStream" uid="uid://mjhprrmwn7qk" path="res://sounds/radio_tune.wav" id="14_a8y0u"]
@ -67,6 +69,9 @@ shader_parameter/pixelate_line = 0
shader = ExtResource("8_eb6dy") shader = ExtResource("8_eb6dy")
shader_parameter/intensity = 1.0 shader_parameter/intensity = 1.0
[sub_resource type="CircleShape2D" id="CircleShape2D_dp3eg"]
radius = 46.98
[sub_resource type="ShaderMaterial" id="ShaderMaterial_5vw27"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_5vw27"]
[node name="Main" type="Node2D" unique_id=128010797] [node name="Main" type="Node2D" unique_id=128010797]
@ -119,6 +124,34 @@ offset_bottom = 320.0
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
[node name="VirtualControls" type="Node2D" parent="CanvasLayer" unique_id=728616058]
[node name="Virtual Joystick" parent="CanvasLayer/VirtualControls" unique_id=2019740770 instance=ExtResource("3_jbj1t")]
process_mode = 3
modulate = Color(1, 1, 1, 0.44000003)
z_index = 10
anchors_preset = -1
offset_left = -2.999999
offset_top = 228.0
offset_right = 297.0
offset_bottom = 528.0
pressed_color = Color(0.7477647, 0.718735, 0.89654267, 1)
clampzone_size = 40.0
visibility_mode = 1
action_left = "left"
action_right = "right"
action_up = "up"
action_down = "down"
[node name="TouchScreenButton" type="TouchScreenButton" parent="CanvasLayer/VirtualControls" unique_id=1967993492]
modulate = Color(1, 1, 1, 0.44000003)
position = Vector2(180, 263)
scale = Vector2(0.35, 0.35)
texture_normal = ExtResource("10_muem4")
shape = SubResource("CircleShape2D_dp3eg")
action = "shoot"
visibility_mode = 1
[node name="background" type="Sprite2D" parent="." unique_id=1790434669] [node name="background" type="Sprite2D" parent="." unique_id=1790434669]
visible = false visible = false
z_index = -4 z_index = -4

BIN
sounds/Ambient 1.0.wav.asd Normal file

Binary file not shown.

Binary file not shown.