diff --git a/joystick/textures/joystick_base_outline.png b/joystick/textures/joystick_base_outline.png new file mode 100644 index 0000000..7cae462 Binary files /dev/null and b/joystick/textures/joystick_base_outline.png differ diff --git a/joystick/textures/joystick_base_outline.png.import b/joystick/textures/joystick_base_outline.png.import new file mode 100644 index 0000000..1bf2644 --- /dev/null +++ b/joystick/textures/joystick_base_outline.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c13dhmpklvcb4" +path="res://.godot/imported/joystick_base_outline.png-483e8f1ac02a5b9a594f81d7e341a76f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://joystick/textures/joystick_base_outline.png" +dest_files=["res://.godot/imported/joystick_base_outline.png-483e8f1ac02a5b9a594f81d7e341a76f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/joystick/textures/joystick_tip.png b/joystick/textures/joystick_tip.png new file mode 100644 index 0000000..28c57ac Binary files /dev/null and b/joystick/textures/joystick_tip.png differ diff --git a/joystick/textures/joystick_tip.png.import b/joystick/textures/joystick_tip.png.import new file mode 100644 index 0000000..9733b02 --- /dev/null +++ b/joystick/textures/joystick_tip.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://clcwu3hgw3euc" +path="res://.godot/imported/joystick_tip.png-3a18c9ea76fb1ca4d2905a9a7401093a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://joystick/textures/joystick_tip.png" +dest_files=["res://.godot/imported/joystick_tip.png-3a18c9ea76fb1ca4d2905a9a7401093a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/joystick/textures/joystick_tip_arrows.png b/joystick/textures/joystick_tip_arrows.png new file mode 100644 index 0000000..b4caa99 Binary files /dev/null and b/joystick/textures/joystick_tip_arrows.png differ diff --git a/joystick/textures/joystick_tip_arrows.png.import b/joystick/textures/joystick_tip_arrows.png.import new file mode 100644 index 0000000..d99b4f8 --- /dev/null +++ b/joystick/textures/joystick_tip_arrows.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bprmb464pp766" +path="res://.godot/imported/joystick_tip_arrows.png-c9482441a78cf839baf32238aae88b91.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://joystick/textures/joystick_tip_arrows.png" +dest_files=["res://.godot/imported/joystick_tip_arrows.png-c9482441a78cf839baf32238aae88b91.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/joystick/virtual_joystick.gd b/joystick/virtual_joystick.gd new file mode 100644 index 0000000..ac67ede --- /dev/null +++ b/joystick/virtual_joystick.gd @@ -0,0 +1,178 @@ +class_name VirtualJoystick + +extends Control + +# https://github.com/MarcoFazioRandom/Virtual-Joystick-Godot + +#### EXPORTED VARIABLE #### + +## The color modulation of the button when the joystick is in use. See [member CanvasItem.modulate]. +@export var pressed_color: 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 JoystickMode { + FIXED, ## The joystick doesn't move. Default. + DYNAMIC ## Every time the joystick area is pressed, the joystick position is set on the touched position. +} + +## Whether the joystick should move on pressed. +@export var joystick_mode: JoystickMode = JoystickMode.FIXED + +enum VisibilityMode { + ALWAYS, ## Always visible. Default. + TOUCHSCREEN_ONLY ## Visible on touch screens only. +} + +## Whether or not the joystick is visible if there is no touchscreen +@export var visibility_mode: VisibilityMode = VisibilityMode.ALWAYS + +## If [code]true[/code], the value returned from [method Input.get_axis] using +## [member action_left], [member action_right], [member action_up], and [member action_down] +## are overridden with [member output] for each axis such that +## [code]Input.get_axis(action_left, action_right)[/code] should always equal [member output.x] and +## [code]Input.get_axis(action_up, action_down)[/code] should always equal [member output.y][br] +## See [b]Project → Project Settings → Input Map[/b][br] +## If you want to change the hotkeys for the default actions make sure [b]Show Built-in Action[/b] is checked. +@export var use_input_actions := true + +## Input action for left direction +@export var action_left := "ui_left" +## Input action for right direction +@export var action_right := "ui_right" +## Input action for up direction +@export var action_up := "ui_up" +## Input action for down direction +@export var action_down := "ui_down" + +#### PUBLIC VARIABLES #### + +## If [code]true[/code], the joystick's state is pressed. Means the joystick will receive inputs. +var pressed := false : get = is_pressed + +func is_pressed() -> bool: + return pressed + +## Current position of the joystick. The value's length ranges from [code]0[/code] to [code]1.0[/code]. +var output := Vector2.ZERO : get = get_output + +func get_output() -> Vector2: + return output + +#### PRIVATE VARIABLES #### + +var _touch_index : int = -1 + +@onready var _base := $Base +@onready var _tip := $Base/Tip + +@onready var _base_radius = _base.size * _base.get_global_transform_with_canvas().get_scale() / 2 + +@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 not DisplayServer.is_touchscreen_available() and visibility_mode == VisibilityMode.TOUCHSCREEN_ONLY: + 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 == JoystickMode.DYNAMIC or (joystick_mode == JoystickMode.FIXED and _is_point_inside_base(event.position)): + if joystick_mode == JoystickMode.DYNAMIC: + _move_base(event.position) + _touch_index = event.index + _tip.modulate = pressed_color + _update_joystick(event.position) + get_viewport().set_input_as_handled() + elif event.index == _touch_index: + _reset() + 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 _is_point_inside_base(point: Vector2) -> bool: + 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 center : Vector2 = _base.global_position + _base_radius + var vector : Vector2 = touch_position - center + vector = vector.limit_length(clampzone_size) + + _move_tip(center + vector) + + if vector.length_squared() > deadzone_size * deadzone_size: + pressed = true + output = (vector - (vector.normalized() * deadzone_size)) / (clampzone_size - deadzone_size) + else: + pressed = false + output = Vector2.ZERO + + if use_input_actions: + _update_input_actions() + +func _update_input_actions(): + if output.x < 0: + Input.action_press(action_left, -output.x) + elif Input.is_action_pressed(action_left): + Input.action_release(action_left) + if output.x > 0: + Input.action_press(action_right, output.x) + elif Input.is_action_pressed(action_right): + Input.action_release(action_right) + if output.y < 0: + Input.action_press(action_up, -output.y) + elif Input.is_action_pressed(action_up): + Input.action_release(action_up) + if output.y > 0: + Input.action_press(action_down, output.y) + elif Input.is_action_pressed(action_down): + Input.action_release(action_down) + +func _reset(): + 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: + if Input.is_action_pressed(action_left): + Input.action_release(action_left) + if Input.is_action_pressed(action_right): + Input.action_release(action_right) + if Input.is_action_pressed(action_down): + Input.action_release(action_down) + if Input.is_action_pressed(action_up): + Input.action_release(action_up) + + +func _on_button_pressed(): + Input.action_press("jump") diff --git a/joystick/virtual_joystick.tscn b/joystick/virtual_joystick.tscn new file mode 100644 index 0000000..6c9cb68 --- /dev/null +++ b/joystick/virtual_joystick.tscn @@ -0,0 +1,68 @@ +[gd_scene load_steps=4 format=3 uid="uid://buyvavyuwmccw"] + +[ext_resource type="Texture2D" uid="uid://c13dhmpklvcb4" path="res://joystick/textures/joystick_base_outline.png" id="1"] +[ext_resource type="Texture2D" uid="uid://bprmb464pp766" path="res://joystick/textures/joystick_tip_arrows.png" id="2"] +[ext_resource type="Script" path="res://joystick/virtual_joystick.gd" id="3"] + +[node name="Virtual joystick" type="Control"] +modulate = Color(1, 1, 1, 0.509804) +layout_mode = 3 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 50.0 +offset_top = -250.0 +offset_right = 250.0 +offset_bottom = -50.0 +grow_vertical = 0 +script = ExtResource("3") +action_left = "player_left" +action_right = "player_right" +action_up = "player_forward" +action_down = "player_backward" + +[node name="Base" type="TextureRect" parent="."] +layout_mode = 0 +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) +size_flags_horizontal = 4 +size_flags_vertical = 4 +texture = ExtResource("1") +stretch_mode = 6 + +[node name="Tip" type="TextureRect" parent="Base"] +layout_mode = 0 +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) +size_flags_horizontal = 4 +size_flags_vertical = 4 +texture = ExtResource("2") +stretch_mode = 6 + +[node name="Button" type="Button" parent="."] +layout_mode = 0 +offset_left = 794.0 +offset_top = 17.0 +offset_right = 1068.0 +offset_bottom = 222.0 +text = "JUMP" + +[connection signal="pressed" from="Button" to="." method="_on_button_pressed"] diff --git a/scenes/tutorial.tscn b/scenes/tutorial.tscn index 49eee1d..f68813c 100644 --- a/scenes/tutorial.tscn +++ b/scenes/tutorial.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=9 format=3 uid="uid://dkokyp5lwhks4"] +[gd_scene load_steps=10 format=3 uid="uid://dkokyp5lwhks4"] [ext_resource type="Script" path="res://scripts/maps/tutorial.gd" id="1_40ws8"] [ext_resource type="PackedScene" uid="uid://dp1q51kvd8uow" path="res://scenes/Teddy.tscn" id="1_081si"] [ext_resource type="PackedScene" uid="uid://ytbacsuahxie" path="res://scenes/HUD.tscn" id="2_s3v8n"] [ext_resource type="PackedScene" uid="uid://y3fffh5cdbks" path="res://scenes/AI.tscn" id="4_fttoe"] [ext_resource type="PackedScene" uid="uid://c1y6p4m0sktfj" path="res://scenes/farmland-map.tscn" id="5_imhi6"] +[ext_resource type="PackedScene" uid="uid://buyvavyuwmccw" path="res://joystick/virtual_joystick.tscn" id="6_p8th3"] [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_i5hsu"] sky_top_color = Color(1, 1, 1, 1) @@ -39,3 +40,5 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8.17483, 1.039, 2.08165e-12) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.21775, 1.22338, -28.4151) [node name="Farmland" parent="." instance=ExtResource("5_imhi6")] + +[node name="Virtual joystick" parent="." instance=ExtResource("6_p8th3")]