Skip to content

Commit 33f8965

Browse files
committed
Add ios virtual controller support.
1 parent 0b6a717 commit 33f8965

17 files changed

+650
-0
lines changed

core/config/project_settings.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,13 @@ ProjectSettings::ProjectSettings() {
16301630
GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false);
16311631
GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false);
16321632
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1);
1633+
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_controller", false);
1634+
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_left_thumbstick", true);
1635+
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_right_thumbstick", true);
1636+
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_button_a", true);
1637+
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_button_b", true);
1638+
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_button_x", true);
1639+
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_button_y", true);
16331640

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

core/input/input.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ void Input::_bind_methods() {
170170
ClassDB::bind_method(D_METHOD("is_emulating_mouse_from_touch"), &Input::is_emulating_mouse_from_touch);
171171
ClassDB::bind_method(D_METHOD("set_emulate_touch_from_mouse", "enable"), &Input::set_emulate_touch_from_mouse);
172172
ClassDB::bind_method(D_METHOD("is_emulating_touch_from_mouse"), &Input::is_emulating_touch_from_mouse);
173+
ClassDB::bind_method(D_METHOD("get_virtual_controller"), &Input::get_virtual_controller);
173174

174175
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_mode"), "set_mouse_mode", "get_mouse_mode");
175176
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_accumulated_input"), "set_use_accumulated_input", "is_using_accumulated_input");
@@ -1056,6 +1057,10 @@ bool Input::is_emulating_touch_from_mouse() const {
10561057
return emulate_touch_from_mouse;
10571058
}
10581059

1060+
VirtualController *Input::get_virtual_controller() {
1061+
return OS::get_singleton()->get_virtual_controller();
1062+
}
1063+
10591064
// Calling this whenever the game window is focused helps unsticking the "touch mouse"
10601065
// if the OS or its abstraction class hasn't properly reported that touch pointers raised
10611066
void Input::ensure_touch_mouse_raised() {

core/input/input.h

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#define INPUT_H
3333

3434
#include "core/input/input_event.h"
35+
#include "core/input/virtual_controller.h"
3536
#include "core/object/object.h"
3637
#include "core/os/keyboard.h"
3738
#include "core/os/thread_safe.h"
@@ -390,6 +391,8 @@ class Input : public Object {
390391
void set_use_accumulated_input(bool p_enable);
391392
bool is_using_accumulated_input();
392393

394+
VirtualController *get_virtual_controller();
395+
393396
void release_pressed_events();
394397

395398
void set_event_dispatch_function(EventDispatchFunc p_function);

core/input/virtual_controller.cpp

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**************************************************************************/
2+
/* virtual_controller.cpp */
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+
#include "virtual_controller.h"
32+
33+
void VirtualController::_bind_methods() {
34+
ClassDB::bind_method(D_METHOD("enable"), &VirtualController::enable);
35+
ClassDB::bind_method(D_METHOD("disable"), &VirtualController::disable);
36+
ClassDB::bind_method(D_METHOD("is_enabled"), &VirtualController::is_enabled);
37+
ClassDB::bind_method(D_METHOD("set_enabled_left_thumbstick", "enable"), &VirtualController::set_enabled_left_thumbstick);
38+
ClassDB::bind_method(D_METHOD("is_enabled_left_thumbstick"), &VirtualController::is_enabled_left_thumbstick);
39+
ClassDB::bind_method(D_METHOD("set_enabled_right_thumbstick", "enable"), &VirtualController::set_enabled_right_thumbstick);
40+
ClassDB::bind_method(D_METHOD("is_enabled_right_thumbstick"), &VirtualController::is_enabled_right_thumbstick);
41+
ClassDB::bind_method(D_METHOD("set_enabled_button_a", "enable"), &VirtualController::set_enabled_button_a);
42+
ClassDB::bind_method(D_METHOD("is_enabled_button_a"), &VirtualController::is_enabled_button_a);
43+
ClassDB::bind_method(D_METHOD("set_enabled_button_b", "enable"), &VirtualController::set_enabled_button_b);
44+
ClassDB::bind_method(D_METHOD("is_enabled_button_b"), &VirtualController::is_enabled_button_b);
45+
ClassDB::bind_method(D_METHOD("set_enabled_button_x", "enable"), &VirtualController::set_enabled_button_x);
46+
ClassDB::bind_method(D_METHOD("is_enabled_button_x"), &VirtualController::is_enabled_button_x);
47+
ClassDB::bind_method(D_METHOD("set_enabled_button_y", "enable"), &VirtualController::set_enabled_button_y);
48+
ClassDB::bind_method(D_METHOD("is_enabled_button_y"), &VirtualController::is_enabled_button_y);
49+
}

core/input/virtual_controller.h

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
#ifndef VIRTUAL_CONTROLLER_H
32+
#define VIRTUAL_CONTROLLER_H
33+
34+
#include "core/object/class_db.h"
35+
36+
class VirtualController : public Object {
37+
GDCLASS(VirtualController, Object);
38+
39+
protected:
40+
static void _bind_methods();
41+
42+
public:
43+
virtual void enable() = 0;
44+
virtual void disable() = 0;
45+
virtual bool is_enabled() = 0;
46+
virtual void set_enabled_left_thumbstick(bool p_enabled) = 0;
47+
virtual bool is_enabled_left_thumbstick() = 0;
48+
virtual void set_enabled_right_thumbstick(bool p_enabled) = 0;
49+
virtual bool is_enabled_right_thumbstick() = 0;
50+
virtual void set_enabled_button_a(bool p_enabled) = 0;
51+
virtual bool is_enabled_button_a() = 0;
52+
virtual void set_enabled_button_b(bool p_enabled) = 0;
53+
virtual bool is_enabled_button_b() = 0;
54+
virtual void set_enabled_button_x(bool p_enabled) = 0;
55+
virtual bool is_enabled_button_x() = 0;
56+
virtual void set_enabled_button_y(bool p_enabled) = 0;
57+
virtual bool is_enabled_button_y() = 0;
58+
};
59+
60+
#endif // VIRTUAL_CONTROLLER_H

core/os/os.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,16 @@ void OS::benchmark_dump() {
754754
#endif
755755
}
756756

757+
VirtualController *OS::get_virtual_controller() const {
758+
return nullptr;
759+
}
760+
761+
void OS::controller_connected() const {
762+
}
763+
764+
void OS::controller_disconnected() const {
765+
}
766+
757767
OS::OS() {
758768
singleton = this;
759769

core/os/os.h

+7
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#define OS_H
3333

3434
#include "core/config/engine.h"
35+
#include "core/input/virtual_controller.h"
3536
#include "core/io/logger.h"
3637
#include "core/io/remote_filesystem_client.h"
3738
#include "core/os/time_enums.h"
@@ -356,6 +357,12 @@ class OS {
356357
// This is invoked by the GDExtensionManager after loading GDExtensions specified by the project.
357358
virtual void load_platform_gdextensions() const {}
358359

360+
virtual VirtualController *get_virtual_controller() const;
361+
362+
virtual void controller_connected() const;
363+
364+
virtual void controller_disconnected() const;
365+
359366
OS();
360367
virtual ~OS();
361368
};

core/register_core_types.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "core/input/input.h"
4343
#include "core/input/input_map.h"
4444
#include "core/input/shortcut.h"
45+
#include "core/input/virtual_controller.h"
4546
#include "core/io/config_file.h"
4647
#include "core/io/dir_access.h"
4748
#include "core/io/dtls_server.h"
@@ -296,6 +297,8 @@ void register_core_types() {
296297
GDREGISTER_NATIVE_STRUCT(AudioFrame, "float left;float right");
297298
GDREGISTER_NATIVE_STRUCT(ScriptLanguageExtensionProfilingInfo, "StringName signature;uint64_t call_count;uint64_t total_time;uint64_t self_time");
298299

300+
GDREGISTER_ABSTRACT_CLASS(VirtualController);
301+
299302
worker_thread_pool = memnew(WorkerThreadPool);
300303

301304
OS::get_singleton()->benchmark_end_measure("Core", "Register Types");

doc/classes/Input.xml

+7
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,13 @@
198198
By default, the deadzone is automatically calculated from the average of the action deadzones. However, you can override the deadzone to be whatever you want (on the range of 0 to 1).
199199
</description>
200200
</method>
201+
<method name="get_virtual_controller">
202+
<return type="VirtualController" />
203+
<description>
204+
Return the platform-specific virtual controller or [code]null[/code] if it is not available. See also [VirtualController].
205+
[b]Note:[/b] Currently implemented only on iOS 15.0+.
206+
</description>
207+
</method>
201208
<method name="is_action_just_pressed" qualifiers="const">
202209
<return type="bool" />
203210
<param index="0" name="action" type="StringName" />

doc/classes/ProjectSettings.xml

+22
Original file line numberDiff line numberDiff line change
@@ -1458,6 +1458,28 @@
14581458
<member name="input_devices/sensors/enable_magnetometer" type="bool" setter="" getter="" default="false">
14591459
If [code]true[/code], the magnetometer sensor is enabled and [method Input.get_magnetometer] returns valid data.
14601460
</member>
1461+
<member name="input_devices/virtual_controller/ios/enable_button_a" type="bool" setter="" getter="" default="true">
1462+
If [code]true[/code], the button "A" shows in the virtual controller.
1463+
</member>
1464+
<member name="input_devices/virtual_controller/ios/enable_button_b" type="bool" setter="" getter="" default="true">
1465+
If [code]true[/code], the button "B" shows in the virtual controller.
1466+
</member>
1467+
<member name="input_devices/virtual_controller/ios/enable_button_x" type="bool" setter="" getter="" default="true">
1468+
If [code]true[/code], the button "X" shows in the virtual controller.
1469+
</member>
1470+
<member name="input_devices/virtual_controller/ios/enable_button_y" type="bool" setter="" getter="" default="true">
1471+
If [code]true[/code], the button "Y" shows in the virtual controller.
1472+
</member>
1473+
<member name="input_devices/virtual_controller/ios/enable_controller" type="bool" setter="" getter="" default="false">
1474+
If [code]true[/code], if there are no physical controllers connected, the game shows the virtual controller.
1475+
[b]Note:[/b] Currently implemented only on iOS 15.0+.
1476+
</member>
1477+
<member name="input_devices/virtual_controller/ios/enable_left_thumbstick" type="bool" setter="" getter="" default="true">
1478+
If [code]true[/code], the left thumbstick shows in the virtual controller.
1479+
</member>
1480+
<member name="input_devices/virtual_controller/ios/enable_right_thumbstick" type="bool" setter="" getter="" default="true">
1481+
If [code]true[/code], the right thumbstick shows in the virtual controller.
1482+
</member>
14611483
<member name="internationalization/locale/fallback" type="String" setter="" getter="" default="&quot;en&quot;">
14621484
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.
14631485
</member>

doc/classes/VirtualController.xml

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="VirtualController" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
3+
<brief_description>
4+
A software emulation of a real controller that you configure specifically for your game.
5+
</brief_description>
6+
<description>
7+
Use a virtual controller to display software controls that you can customize over your game. You create a virtual controller from a configuration where you choose the input elements to display. When you connect the controller to the device, users interact with it similarly to a real controller.
8+
[b]Note:[/b] Currently implemented only on iOS 15.0+.
9+
</description>
10+
<tutorials>
11+
</tutorials>
12+
<methods>
13+
<method name="disable">
14+
<return type="void" />
15+
<description>
16+
Disconnects the virtual controller from the device and removes it from the screen.
17+
</description>
18+
</method>
19+
<method name="enable">
20+
<return type="void" />
21+
<description>
22+
If there are no physical controllers connected, connects the virtual controller to the device and displays it on the screen.
23+
</description>
24+
</method>
25+
<method name="is_enabled">
26+
<return type="bool" />
27+
<description>
28+
Returns [code]true[/code] if the virtual controller is enabled.
29+
</description>
30+
</method>
31+
<method name="is_enabled_button_a">
32+
<return type="bool" />
33+
<description>
34+
Returns [code]true[/code] if the button "A" is enabled.
35+
</description>
36+
</method>
37+
<method name="is_enabled_button_b">
38+
<return type="bool" />
39+
<description>
40+
Returns [code]true[/code] if the button "B" is enabled.
41+
</description>
42+
</method>
43+
<method name="is_enabled_button_x">
44+
<return type="bool" />
45+
<description>
46+
Returns [code]true[/code] if the button "X" is enabled.
47+
</description>
48+
</method>
49+
<method name="is_enabled_button_y">
50+
<return type="bool" />
51+
<description>
52+
Returns [code]true[/code] if the button "Y" is enabled.
53+
</description>
54+
</method>
55+
<method name="is_enabled_left_thumbstick">
56+
<return type="bool" />
57+
<description>
58+
Returns [code]true[/code] if the left thumbstick is enabled.
59+
</description>
60+
</method>
61+
<method name="is_enabled_right_thumbstick">
62+
<return type="bool" />
63+
<description>
64+
Returns [code]true[/code] if the right thumbstick is enabled.
65+
</description>
66+
</method>
67+
<method name="set_enabled_button_a">
68+
<return type="void" />
69+
<param index="0" name="enable" type="bool" />
70+
<description>
71+
Changes the visibility of a button "A" element. Default is [code]true[/code].
72+
</description>
73+
</method>
74+
<method name="set_enabled_button_b">
75+
<return type="void" />
76+
<param index="0" name="enable" type="bool" />
77+
<description>
78+
Changes the visibility of a button "B" element. Default is [code]true[/code].
79+
</description>
80+
</method>
81+
<method name="set_enabled_button_x">
82+
<return type="void" />
83+
<param index="0" name="enable" type="bool" />
84+
<description>
85+
Changes the visibility of a button "A" element. Default is [code]true[/code].
86+
</description>
87+
</method>
88+
<method name="set_enabled_button_y">
89+
<return type="void" />
90+
<param index="0" name="enable" type="bool" />
91+
<description>
92+
Changes the visibility of a button "Y" element. Default is [code]true[/code].
93+
</description>
94+
</method>
95+
<method name="set_enabled_left_thumbstick">
96+
<return type="void" />
97+
<param index="0" name="enable" type="bool" />
98+
<description>
99+
Changes the visibility of a left thumbstick element. Default is [code]true[/code].
100+
</description>
101+
</method>
102+
<method name="set_enabled_right_thumbstick">
103+
<return type="void" />
104+
<param index="0" name="enable" type="bool" />
105+
<description>
106+
Changes the visibility of a right thumbstick element. Default is [code]true[/code].
107+
</description>
108+
</method>
109+
</methods>
110+
</class>

drivers/apple/joypad_apple.mm

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#import <os/log.h>
3535

3636
#include "core/config/project_settings.h"
37+
#include "core/os/os.h"
3738
#include "main/main.h"
3839

3940
class API_AVAILABLE(macos(11), ios(14.0), tvos(14.0)) RumbleMotor {
@@ -243,6 +244,7 @@ void play_strong_pattern(CHHapticPattern *p_pattern) {
243244
return;
244245
}
245246
add_joypad(controller);
247+
OS::get_singleton()->controller_connected();
246248
}];
247249

248250
disconnect_observer = [NSNotificationCenter.defaultCenter
@@ -255,6 +257,7 @@ void play_strong_pattern(CHHapticPattern *p_pattern) {
255257
return;
256258
}
257259
remove_joypad(controller);
260+
OS::get_singleton()->controller_disconnected();
258261
}];
259262

260263
if (@available(macOS 11.3, iOS 14.5, tvOS 14.5, *)) {

0 commit comments

Comments
 (0)