Skip to content

Commit c8f77b5

Browse files
committed
Adds VirtualController
1 parent 5b4e0cb commit c8f77b5

9 files changed

Lines changed: 799 additions & 0 deletions

File tree

core/config/project_settings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,6 +1852,7 @@ ProjectSettings::ProjectSettings() {
18521852
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1);
18531853
GLOBAL_DEF("input_devices/pointing/android/override_volume_buttons", false);
18541854
GLOBAL_DEF_BASIC("input_devices/pointing/android/disable_scroll_deadzone", false);
1855+
GLOBAL_DEF_BASIC("input_devices/virtual_controller/enable_controller", false);
18551856

18561857
// These properties will not show up in the dialog. If you want to exclude whole groups, use add_hidden_prefix().
18571858
GLOBAL_DEF_INTERNAL("application/config/features", PackedStringArray());

doc/classes/ProjectSettings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,6 +1716,9 @@
17161716
<member name="input_devices/sensors/enable_magnetometer" type="bool" setter="" getter="" default="false">
17171717
If [code]true[/code], the magnetometer sensor is enabled and [method Input.get_magnetometer] returns valid data.
17181718
</member>
1719+
<member name="input_devices/virtual_controller/enable_controller" type="bool" setter="" getter="" default="false">
1720+
If [code]true[/code], the virtual controller is enabled and will be created in the scene.
1721+
</member>
17191722
<member name="internationalization/locale/fallback" type="String" setter="" getter="" default="&quot;en&quot;">
17201723
The locale to fall back to if a translation isn't available in a given language. If left empty, [code]en[/code] (English) will be used.
17211724
[b]Note:[/b] Not to be confused with [TextServerFallback].

doc/classes/VirtualJoystick.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@
6262
Emitted when the tip moved outside the deadzone and the joystick is released. The [param input_vector] contains the last input direction and strength before release. Its length is between [code]0.0[/code] and [code]1.0[/code].
6363
</description>
6464
</signal>
65+
<signal name="input_changed">
66+
<param index="0" name="input_vector" type="Vector2" />
67+
<description>
68+
Emitted when the input vector changes.
69+
</description>
70+
</signal>
6571
<signal name="pressed">
6672
<description>
6773
Emitted when the joystick is pressed.

scene/gui/virtual_controller.cpp

Lines changed: 587 additions & 0 deletions
Large diffs are not rendered by default.

scene/gui/virtual_controller.h

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/**************************************************************************/
2+
/* virtual_controller.h */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#pragma once
32+
33+
#include "scene/gui/control.h"
34+
35+
class Button;
36+
class VirtualJoystick;
37+
38+
class VirtualController : public Control {
39+
GDCLASS(VirtualController, Control);
40+
41+
private:
42+
int device_id = -1;
43+
44+
struct ThemeCache {
45+
Ref<StyleBox> button_normal;
46+
Ref<StyleBox> button_pressed;
47+
Ref<StyleBox> left_normal_joystick;
48+
Ref<StyleBox> left_normal_tip;
49+
Ref<StyleBox> left_pressed_joystick;
50+
Ref<StyleBox> left_pressed_tip;
51+
Ref<StyleBox> right_normal_joystick;
52+
Ref<StyleBox> right_normal_tip;
53+
Ref<StyleBox> right_pressed_joystick;
54+
Ref<StyleBox> right_pressed_tip;
55+
} theme_cache;
56+
57+
VirtualJoystick *left_joystick = nullptr;
58+
VirtualJoystick *right_joystick = nullptr;
59+
Button *left_joystick_button = nullptr;
60+
Button *right_joystick_button = nullptr;
61+
Button *dpad_up = nullptr;
62+
Button *dpad_down = nullptr;
63+
Button *dpad_left = nullptr;
64+
Button *dpad_right = nullptr;
65+
Button *button_a = nullptr;
66+
Button *button_b = nullptr;
67+
Button *button_x = nullptr;
68+
Button *button_y = nullptr;
69+
Button *left_shoulder = nullptr;
70+
Button *right_shoulder = nullptr;
71+
Button *left_trigger = nullptr;
72+
Button *right_trigger = nullptr;
73+
Button *start_button = nullptr;
74+
Button *back_button = nullptr;
75+
Button *guide_button = nullptr;
76+
77+
void _setup_controls();
78+
79+
void _create_button_event(JoyButton p_button, bool p_pressed);
80+
void _create_motion_event(JoyAxis p_axis, float p_value);
81+
void _update_left_joystick_style(VirtualJoystick *p_joystick);
82+
void _update_right_joystick_style(VirtualJoystick *p_joystick);
83+
void _update_button_style(Button *p_button);
84+
85+
void _on_left_joystick_motion(Vector2 p_value);
86+
void _on_right_joystick_motion(Vector2 p_value);
87+
void _on_left_joystick_pressed();
88+
void _on_left_joystick_released();
89+
void _on_right_joystick_pressed();
90+
void _on_right_joystick_released();
91+
void _on_dpad_up_pressed();
92+
void _on_dpad_up_released();
93+
void _on_dpad_down_pressed();
94+
void _on_dpad_down_released();
95+
void _on_dpad_left_pressed();
96+
void _on_dpad_left_released();
97+
void _on_dpad_right_pressed();
98+
void _on_dpad_right_released();
99+
void _on_button_a_pressed();
100+
void _on_button_a_released();
101+
void _on_button_b_pressed();
102+
void _on_button_b_released();
103+
void _on_button_x_pressed();
104+
void _on_button_x_released();
105+
void _on_button_y_pressed();
106+
void _on_button_y_released();
107+
void _on_left_shoulder_pressed();
108+
void _on_left_shoulder_released();
109+
void _on_right_shoulder_pressed();
110+
void _on_right_shoulder_released();
111+
void _on_left_trigger_pressed();
112+
void _on_left_trigger_released();
113+
void _on_right_trigger_pressed();
114+
void _on_right_trigger_released();
115+
void _on_start_button_pressed();
116+
void _on_start_button_released();
117+
void _on_back_button_pressed();
118+
void _on_back_button_released();
119+
void _on_guide_button_pressed();
120+
void _on_guide_button_released();
121+
122+
protected:
123+
static void _bind_methods();
124+
void _notification(int p_what);
125+
126+
public:
127+
VirtualController();
128+
};

scene/gui/virtual_joystick.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ void VirtualJoystick::gui_input(const Ref<InputEvent> &p_event) {
5656
} else if (touch->get_index() == touch_index) {
5757
is_pressed = false;
5858
emit_signal(SNAME("released"), input_vector);
59+
emit_signal(SNAME("input_changed"), Vector2());
5960

6061
if (!is_flick_canceled && !has_moved) {
6162
emit_signal(SNAME("tapped"));
@@ -127,6 +128,8 @@ void VirtualJoystick::_update_joystick(const Vector2 &p_pos) {
127128
input_vector = Vector2();
128129
}
129130

131+
emit_signal(SNAME("input_changed"), input_vector);
132+
130133
if (!is_flick_canceled && was_pressed && !has_input) {
131134
is_flick_canceled = true;
132135
emit_signal(SNAME("flick_canceled"));
@@ -247,6 +250,7 @@ void VirtualJoystick::_bind_methods() {
247250
ADD_SIGNAL(MethodInfo("released", PropertyInfo(Variant::VECTOR2, "input_vector")));
248251
ADD_SIGNAL(MethodInfo("flicked", PropertyInfo(Variant::VECTOR2, "input_vector")));
249252
ADD_SIGNAL(MethodInfo("flick_canceled"));
253+
ADD_SIGNAL(MethodInfo("input_changed", PropertyInfo(Variant::VECTOR2, "input_vector")));
250254

251255
BIND_ENUM_CONSTANT(JOYSTICK_FIXED);
252256
BIND_ENUM_CONSTANT(JOYSTICK_DYNAMIC);

scene/main/scene_tree.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ STATIC_ASSERT_INCOMPLETE_TYPE(class, RenderingServer);
4545
#include "scene/animation/tween.h"
4646
#include "scene/debugger/scene_debugger.h"
4747
#include "scene/gui/control.h"
48+
#include "scene/gui/virtual_controller.h"
49+
#include "scene/main/canvas_layer.h"
4850
#include "scene/main/multiplayer_api.h"
4951
#include "scene/main/node.h"
5052
#include "scene/main/viewport.h"
@@ -1887,6 +1889,43 @@ bool SceneTree::is_multiplayer_poll_enabled() const {
18871889
return multiplayer_poll;
18881890
}
18891891

1892+
void SceneTree::set_virtual_controller_visible(bool p_visible) {
1893+
if (p_visible) {
1894+
if (virtual_controller_canvas_layer == nullptr) {
1895+
virtual_controller_canvas_layer = memnew(CanvasLayer);
1896+
// Begin name with an underscore to avoid conflict with project nodes.
1897+
virtual_controller_canvas_layer->set_name("_VirtualControllerCanvasLayer");
1898+
get_root()->add_child(virtual_controller_canvas_layer, false, Node::INTERNAL_MODE_BACK);
1899+
1900+
virtual_controller = memnew(VirtualController);
1901+
virtual_controller->set_name("_VirtualController");
1902+
virtual_controller_canvas_layer->add_child(virtual_controller, false, Node::INTERNAL_MODE_BACK);
1903+
} else {
1904+
ERR_PRINT("Virtual controller already exists.");
1905+
}
1906+
} else {
1907+
if (virtual_controller_canvas_layer != nullptr) {
1908+
// Free when closing to avoid reserving memory during the project's run duration.
1909+
virtual_controller_canvas_layer->queue_free();
1910+
virtual_controller_canvas_layer = nullptr;
1911+
virtual_controller = nullptr;
1912+
}
1913+
}
1914+
}
1915+
1916+
bool SceneTree::is_virtual_controller_visible() const {
1917+
if (virtual_controller_canvas_layer) {
1918+
return virtual_controller_canvas_layer->is_visible();
1919+
}
1920+
1921+
// Licenses dialog isn't created yet. Therefore, it's not visible.
1922+
return false;
1923+
}
1924+
1925+
void SceneTree::_project_settings_changed() {
1926+
set_virtual_controller_visible(ProjectSettings::get_singleton()->get("input_devices/virtual_controller/enable_controller"));
1927+
}
1928+
18901929
void SceneTree::_bind_methods() {
18911930
ClassDB::bind_method(D_METHOD("get_root"), &SceneTree::get_root);
18921931
ClassDB::bind_method(D_METHOD("has_group", "name"), &SceneTree::has_group);
@@ -2178,6 +2217,8 @@ SceneTree::SceneTree() {
21782217
Viewport::SDFScale sdf_scale = Viewport::SDFScale(int(GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/2d/sdf/scale", PROPERTY_HINT_ENUM, "100%,50%,25%"), 1)));
21792218
root->set_sdf_scale(sdf_scale);
21802219

2220+
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &SceneTree::_project_settings_changed));
2221+
21812222
#ifndef _3D_DISABLED
21822223
{ // Load default fallback environment.
21832224
// Get possible extensions.

scene/main/scene_tree.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@
4141
#include <cstdlib>
4242

4343
class ArrayMesh;
44+
class CanvasLayer;
4445
class InputEvent;
4546
class Material;
4647
class MultiplayerAPI;
4748
class Node;
4849
class PackedScene;
4950
class Tween;
5051
class Viewport;
52+
class VirtualController;
5153
class Window;
5254

5355
#ifndef _3D_DISABLED
@@ -204,6 +206,9 @@ class SceneTree : public MainLoop {
204206
ObjectID prev_scene_id;
205207
ObjectID pending_new_scene_id;
206208

209+
CanvasLayer *virtual_controller_canvas_layer = nullptr;
210+
VirtualController *virtual_controller = nullptr;
211+
207212
Color debug_collisions_color;
208213
Color debug_collision_contact_color;
209214
Color debug_paths_color;
@@ -283,6 +288,8 @@ class SceneTree : public MainLoop {
283288
//used by viewport
284289
void _call_input_pause(const StringName &p_group, CallInputType p_call_type, const Ref<InputEvent> &p_input, Viewport *p_viewport);
285290

291+
void _project_settings_changed();
292+
286293
protected:
287294
void _notification(int p_notification);
288295
static void _bind_methods();
@@ -451,6 +458,9 @@ class SceneTree : public MainLoop {
451458
void set_multiplayer_poll_enabled(bool p_enabled);
452459
bool is_multiplayer_poll_enabled() const;
453460

461+
void set_virtual_controller_visible(bool p_visible);
462+
bool is_virtual_controller_visible() const;
463+
454464
static void add_idle_callback(IdleCallback p_callback);
455465

456466
void set_disable_node_threading(bool p_disable);

scene/theme/default_theme.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,25 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
681681
theme->set_stylebox("pressed_joystick", "VirtualJoystick", style_joystick);
682682
theme->set_stylebox("pressed_tip", "VirtualJoystick", style_joystick_tip);
683683

684+
// Virtual Controller
685+
686+
Ref<StyleBoxFlat> style_virtual_controller_button = make_flat_stylebox(style_normal_color, 0, 0, 0, 0, 10000, false, 4 * scale);
687+
style_virtual_controller_button->set_corner_detail(24 * scale);
688+
689+
Ref<StyleBoxFlat> style_virtual_controller_button_pressed = make_flat_stylebox(style_pressed_color, 0, 0, 0, 0, 10000, true, 4 * scale);
690+
style_virtual_controller_button_pressed->set_corner_detail(24 * scale);
691+
692+
theme->set_stylebox("button_normal", "VirtualController", style_virtual_controller_button);
693+
theme->set_stylebox("button_pressed", "VirtualController", style_virtual_controller_button_pressed);
694+
theme->set_stylebox("left_normal_joystick", "VirtualController", style_joystick);
695+
theme->set_stylebox("left_normal_tip", "VirtualController", style_joystick_tip);
696+
theme->set_stylebox("left_pressed_joystick", "VirtualController", style_joystick);
697+
theme->set_stylebox("left_pressed_tip", "VirtualController", style_joystick_tip);
698+
theme->set_stylebox("right_normal_joystick", "VirtualController", style_joystick);
699+
theme->set_stylebox("right_normal_tip", "VirtualController", style_joystick_tip);
700+
theme->set_stylebox("right_pressed_joystick", "VirtualController", style_joystick);
701+
theme->set_stylebox("right_pressed_tip", "VirtualController", style_joystick_tip);
702+
684703
// Window
685704

686705
theme->set_stylebox("embedded_border", "Window", sb_expand(make_flat_stylebox(style_popup_color, 10, 28, 10, 8), 8, 32, 8, 6));

0 commit comments

Comments
 (0)