diff options
Diffstat (limited to 'client/player/onscreen_controls/virtual_joystick.gd')
-rw-r--r-- | client/player/onscreen_controls/virtual_joystick.gd | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/client/player/onscreen_controls/virtual_joystick.gd b/client/player/onscreen_controls/virtual_joystick.gd new file mode 100644 index 00000000..406de29b --- /dev/null +++ b/client/player/onscreen_controls/virtual_joystick.gd @@ -0,0 +1,161 @@ +# Undercooked - a game about cooking +# Copyright 2024 Marco F +# Copyright 2024 tpart +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, version 3 of the License only. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +class_name VirtualJoystick + +extends Control + +@export var pressed_color := Color.GRAY +@export_range(0, 200, 1) var deadzone_size : float = 10 +@export_range(0, 500, 1) var clampzone_size : float = 75 + +enum Joystick_mode { + FIXED, + DYNAMIC, + FOLLOWING +} + +@export var joystick_mode := Joystick_mode.FIXED + +enum Visibility_mode { + ALWAYS, + TOUCHSCREEN_ONLY, + SETTING, + WHEN_TOUCHED +} + +@export var visibility_mode := Visibility_mode.SETTING + +@export var use_input_actions := true + +@export var action_left := "left" +@export var action_right := "right" +@export var action_up := "forward" +@export var action_down := "backwards" + +var is_pressed := false + +var output := Vector2.ZERO + +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 + +func _ready() -> void: + if not DisplayServer.is_touchscreen_available() and visibility_mode == Visibility_mode.TOUCHSCREEN_ONLY : + hide() + + if visibility_mode == Visibility_mode.WHEN_TOUCHED: + hide() + + if visibility_mode == Visibility_mode.SETTING and not Global.get_setting("touch_controls"): + 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: + if output.x > 0: + Input.action_release(action_left) + Input.action_press(action_right, output.x) + else: + Input.action_release(action_right) + Input.action_press(action_left, -output.x) + + if output.y > 0: + Input.action_release(action_up) + Input.action_press(action_down, output.y) + else: + Input.action_release(action_down) + Input.action_press(action_up, -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 + if use_input_actions: + for action in [action_left, action_right, action_down, action_up]: + Input.action_release(action) |