Skip to content

Commit 0078ae2

Browse files
committed
Support Input and UI for multiple players
1 parent 1586c56 commit 0078ae2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+759
-244
lines changed

core/config/project_settings.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,9 @@ ProjectSettings::ProjectSettings() {
15461546
GLOBAL_DEF("audio/general/ios/mix_with_others", false);
15471547

15481548
_add_builtin_input_map();
1549+
GLOBAL_DEF(PropertyInfo(Variant::INT, "input/keyboard_player_id_override", PROPERTY_HINT_ENUM, "P1,P2,P3,P4,P5,P6,P7,P8"), (int)PlayerId::P1);
1550+
GLOBAL_DEF(PropertyInfo(Variant::INT, "input/mouse_player_id_override", PROPERTY_HINT_ENUM, "P1,P2,P3,P4,P5,P6,P7,P8"), (int)PlayerId::P1);
1551+
GLOBAL_DEF(PropertyInfo(Variant::INT, "input/touch_player_id_override", PROPERTY_HINT_ENUM, "P1,P2,P3,P4,P5,P6,P7,P8"), (int)PlayerId::P1);
15491552

15501553
// Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum.
15511554
custom_prop_info["display/window/handheld/orientation"] = PropertyInfo(Variant::INT, "display/window/handheld/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor");

core/config/project_settings.h

+4
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ class ProjectSettings : public Object {
9595
bool project_loaded = false;
9696
List<String> input_presets;
9797

98+
PlayerId keyboard_player_id_override = PlayerId::P1;
99+
PlayerId mouse_player_id_override = PlayerId::P1;
100+
PlayerId touch_player_id_override = PlayerId::P1;
101+
98102
HashSet<String> custom_features;
99103
HashMap<StringName, LocalVector<Pair<StringName, StringName>>> feature_overrides;
100104

core/core_constants.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,15 @@ void register_global_constants() {
527527
BIND_CORE_BITFIELD_CLASS_FLAG(MouseButtonMask, MOUSE_BUTTON_MASK, MB_XBUTTON1);
528528
BIND_CORE_BITFIELD_CLASS_FLAG(MouseButtonMask, MOUSE_BUTTON_MASK, MB_XBUTTON2);
529529

530+
BIND_CORE_ENUM_CLASS_CONSTANT(PlayerId, PLAYER_ID, P1);
531+
BIND_CORE_ENUM_CLASS_CONSTANT(PlayerId, PLAYER_ID, P2);
532+
BIND_CORE_ENUM_CLASS_CONSTANT(PlayerId, PLAYER_ID, P3);
533+
BIND_CORE_ENUM_CLASS_CONSTANT(PlayerId, PLAYER_ID, P4);
534+
BIND_CORE_ENUM_CLASS_CONSTANT(PlayerId, PLAYER_ID, P5);
535+
BIND_CORE_ENUM_CLASS_CONSTANT(PlayerId, PLAYER_ID, P6);
536+
BIND_CORE_ENUM_CLASS_CONSTANT(PlayerId, PLAYER_ID, P7);
537+
BIND_CORE_ENUM_CLASS_CONSTANT(PlayerId, PLAYER_ID, P8);
538+
530539
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, INVALID);
531540
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, A);
532541
BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, B);
@@ -649,6 +658,7 @@ void register_global_constants() {
649658
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_PHYSICS);
650659
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_NAVIGATION);
651660
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_AVOIDANCE);
661+
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_PLAYER_MASK);
652662

653663
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_FILE);
654664
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_DIR);

core/input/input.cpp

+158-64
Large diffs are not rendered by default.

core/input/input.h

+33-22
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,19 @@ class Input : public Object {
5757
MOUSE_MODE_MAX,
5858
};
5959

60+
struct Joypad {
61+
StringName name;
62+
StringName uid;
63+
PlayerId player_id = PlayerId::P1;
64+
bool connected = false;
65+
bool last_buttons[(size_t)JoyButton::MAX] = { false };
66+
float last_axis[(size_t)JoyAxis::MAX] = { 0.0f };
67+
HatMask last_hat = HatMask::CENTER;
68+
int mapping = -1;
69+
int hat_current = 0;
70+
Dictionary info;
71+
};
72+
6073
#undef CursorShape
6174
enum CursorShape {
6275
CURSOR_ARROW,
@@ -131,7 +144,9 @@ class Input : public Object {
131144
} cache;
132145
};
133146

134-
HashMap<StringName, ActionState> action_states;
147+
// Key -> player_id.
148+
// Value -> All available actions to that player.
149+
HashMap<int, HashMap<StringName, ActionState>> action_states;
135150

136151
bool emulate_touch_from_mouse = false;
137152
bool emulate_mouse_from_touch = false;
@@ -164,18 +179,6 @@ class Input : public Object {
164179
VelocityTrack();
165180
};
166181

167-
struct Joypad {
168-
StringName name;
169-
StringName uid;
170-
bool connected = false;
171-
bool last_buttons[(size_t)JoyButton::MAX] = { false };
172-
float last_axis[(size_t)JoyAxis::MAX] = { 0.0f };
173-
HatMask last_hat = HatMask::CENTER;
174-
int mapping = -1;
175-
int hat_current = 0;
176-
Dictionary info;
177-
};
178-
179182
VelocityTrack mouse_velocity_track;
180183
HashMap<int, VelocityTrack> touch_velocity_track;
181184
HashMap<int, Joypad> joy_names;
@@ -306,14 +309,20 @@ class Input : public Object {
306309
bool is_key_label_pressed(Key p_keycode) const;
307310
bool is_mouse_button_pressed(MouseButton p_button) const;
308311
bool is_joy_button_pressed(int p_device, JoyButton p_button) const;
309-
bool is_action_pressed(const StringName &p_action, bool p_exact = false) const;
310-
bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const;
311-
bool is_action_just_released(const StringName &p_action, bool p_exact = false) const;
312-
float get_action_strength(const StringName &p_action, bool p_exact = false) const;
313-
float get_action_raw_strength(const StringName &p_action, bool p_exact = false) const;
312+
bool is_action_pressed(const StringName &p_action, bool p_exact = false, PlayerId p_player_id = PlayerId::P1) const;
313+
bool is_action_just_pressed(const StringName &p_action, bool p_exact = false, PlayerId p_player_id = PlayerId::P1) const;
314+
bool is_action_just_released(const StringName &p_action, bool p_exact = false, PlayerId p_player_id = PlayerId::P1) const;
315+
float get_action_strength(const StringName &p_action, bool p_exact = false, PlayerId p_player_id = PlayerId::P1) const;
316+
float get_action_raw_strength(const StringName &p_action, bool p_exact = false, PlayerId p_player_id = PlayerId::P1) const;
317+
318+
float get_axis(const StringName &p_negative_action, const StringName &p_positive_action, PlayerId p_player_id = PlayerId::P1) const;
319+
Vector2 get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone = -1.0f, PlayerId p_player_id = PlayerId::P1) const;
320+
321+
static inline bool is_player_id_in_mask(BitField<PlayerMask> p_player_mask, PlayerId p_player_id) {
322+
return p_player_mask.has_flag(player_id_to_mask(p_player_id));
323+
}
314324

315-
float get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const;
316-
Vector2 get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone = -1.0f) const;
325+
HashMap<int, Joypad> _get_joy_names() const { return joy_names; }
317326

318327
float get_joy_axis(int p_device, JoyAxis p_axis) const;
319328
String get_joy_name(int p_idx);
@@ -350,8 +359,8 @@ class Input : public Object {
350359

351360
void set_mouse_position(const Point2 &p_posf);
352361

353-
void action_press(const StringName &p_action, float p_strength = 1.f);
354-
void action_release(const StringName &p_action);
362+
void action_press(const StringName &p_action, float p_strength = 1.f, PlayerId p_player_id = PlayerId::P1);
363+
void action_release(const StringName &p_action, PlayerId p_player_id = PlayerId::P1);
355364

356365
void set_emulate_touch_from_mouse(bool p_emulate);
357366
bool is_emulating_touch_from_mouse() const;
@@ -377,6 +386,8 @@ class Input : public Object {
377386

378387
bool is_joy_known(int p_device);
379388
String get_joy_guid(int p_device) const;
389+
PlayerId get_joy_player_id(int p_device) const;
390+
void set_joy_player_id(int p_device, PlayerId p_player_id);
380391
bool should_ignore_device(int p_vendor_id, int p_product_id) const;
381392
Dictionary get_joy_info(int p_device) const;
382393
void set_fallback_mapping(const String &p_guid);

core/input/input_enums.h

+32
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,36 @@ inline MouseButtonMask mouse_button_to_mask(MouseButton button) {
138138
return MouseButtonMask(1 << ((int)button - 1));
139139
}
140140

141+
enum class PlayerId : uint8_t {
142+
P1 = 0,
143+
P2 = 1,
144+
P3 = 2,
145+
P4 = 3,
146+
P5 = 4,
147+
P6 = 5,
148+
P7 = 6,
149+
P8 = 7,
150+
};
151+
152+
enum {
153+
PLAYERS_MAX = 8,
154+
};
155+
156+
enum PlayerMask : uint8_t {
157+
PLAYER_NONE = 0U,
158+
PLAYER_1 = 1U << 0,
159+
PLAYER_2 = 1U << 1,
160+
PLAYER_3 = 1U << 2,
161+
PLAYER_4 = 1U << 3,
162+
PLAYER_5 = 1U << 4,
163+
PLAYER_6 = 1U << 5,
164+
PLAYER_7 = 1U << 6,
165+
PLAYER_8 = 1U << 7,
166+
PLAYER_ALL = 0xFFU,
167+
};
168+
169+
inline PlayerMask player_id_to_mask(PlayerId id) {
170+
return PlayerMask(1U << (uint8_t)id);
171+
}
172+
141173
#endif // INPUT_ENUMS_H

core/input/input_event.cpp

+89-7
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
#include "input_event.h"
3232

33+
#include "core/config/project_settings.h"
34+
#include "core/input/input.h"
3335
#include "core/input/input_map.h"
3436
#include "core/input/shortcut.h"
3537
#include "core/os/keyboard.h"
@@ -47,29 +49,98 @@ int InputEvent::get_device() const {
4749
return device;
4850
}
4951

52+
void InputEvent::set_player(PlayerId p_player) {
53+
player = p_player;
54+
emit_changed();
55+
}
56+
57+
PlayerId InputEvent::get_player() const {
58+
return player;
59+
}
60+
61+
void InputEvent::set_player_from_device() {
62+
// ProjectSettings *ps = ProjectSettings::get_singleton();
63+
Ref<InputEvent> event = Ref<InputEvent>(this);
64+
65+
// Keyboard events.
66+
67+
Ref<InputEventKey> k = event;
68+
if (k.is_valid()) {
69+
player = (PlayerId)(GLOBAL_GET("input/keyboard_player_id_override").operator int());
70+
return;
71+
}
72+
73+
// Mouse events.
74+
75+
Ref<InputEventMouseButton> mb = event;
76+
Ref<InputEventMouseMotion> mm = event;
77+
if (mb.is_valid() || mm.is_valid()) {
78+
player = (PlayerId)(GLOBAL_GET("input/mouse_player_id_override").operator int());
79+
return;
80+
}
81+
82+
// Joypad events.
83+
84+
Ref<InputEventJoypadButton> jb = event;
85+
Ref<InputEventJoypadMotion> jm = event;
86+
if (jb.is_valid() || jm.is_valid()) {
87+
Input *input = Input::get_singleton();
88+
HashMap<int, Input::Joypad>::Iterator E = input->_get_joy_names().find(device);
89+
90+
if (!E) {
91+
player = PlayerId::P1;
92+
return;
93+
}
94+
95+
player = E->value.player_id;
96+
}
97+
98+
// Touch events.
99+
100+
Ref<InputEventScreenTouch> st = event;
101+
Ref<InputEventScreenDrag> sd = event;
102+
Ref<InputEventGesture> ge = event;
103+
if (st.is_valid() || sd.is_valid() || ge.is_valid()) {
104+
player = (PlayerId)(GLOBAL_GET("input/touch_player_id_override").operator int());
105+
return;
106+
}
107+
}
108+
50109
bool InputEvent::is_action(const StringName &p_action, bool p_exact_match) const {
51110
return InputMap::get_singleton()->event_is_action(Ref<InputEvent>(const_cast<InputEvent *>(this)), p_action, p_exact_match);
52111
}
53112

54-
bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo, bool p_exact_match) const {
113+
bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo, bool p_exact_match, PlayerId p_player_id) const {
114+
if (player != p_player_id) {
115+
return false;
116+
}
55117
bool pressed_state;
56118
bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>(const_cast<InputEvent *>(this)), p_action, p_exact_match, &pressed_state, nullptr, nullptr);
57119
return valid && pressed_state && (p_allow_echo || !is_echo());
58120
}
59121

60-
bool InputEvent::is_action_released(const StringName &p_action, bool p_exact_match) const {
122+
bool InputEvent::is_action_released(const StringName &p_action, bool p_exact_match, PlayerId p_player_id) const {
123+
if (player != p_player_id) {
124+
return false;
125+
}
61126
bool pressed_state;
62127
bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>(const_cast<InputEvent *>(this)), p_action, p_exact_match, &pressed_state, nullptr, nullptr);
63128
return valid && !pressed_state;
64129
}
65130

66-
float InputEvent::get_action_strength(const StringName &p_action, bool p_exact_match) const {
131+
float InputEvent::get_action_strength(const StringName &p_action, bool p_exact_match, PlayerId p_player_id) const {
132+
if (player != p_player_id) {
133+
return 0.0f;
134+
}
67135
float strength;
68136
bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>(const_cast<InputEvent *>(this)), p_action, p_exact_match, nullptr, &strength, nullptr);
69137
return valid ? strength : 0.0f;
70138
}
71139

72-
float InputEvent::get_action_raw_strength(const StringName &p_action, bool p_exact_match) const {
140+
float InputEvent::get_action_raw_strength(const StringName &p_action, bool p_exact_match, PlayerId p_player_id) const {
141+
if (player != p_player_id) {
142+
return 0.0f;
143+
}
73144
float raw_strength;
74145
bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>(const_cast<InputEvent *>(this)), p_action, p_exact_match, nullptr, nullptr, &raw_strength);
75146
return valid ? raw_strength : 0.0f;
@@ -111,10 +182,14 @@ void InputEvent::_bind_methods() {
111182
ClassDB::bind_method(D_METHOD("set_device", "device"), &InputEvent::set_device);
112183
ClassDB::bind_method(D_METHOD("get_device"), &InputEvent::get_device);
113184

185+
ClassDB::bind_method(D_METHOD("set_player", "player"), &InputEvent::set_player);
186+
ClassDB::bind_method(D_METHOD("get_player"), &InputEvent::get_player);
187+
ClassDB::bind_method(D_METHOD("set_player_from_device"), &InputEvent::set_player_from_device);
188+
114189
ClassDB::bind_method(D_METHOD("is_action", "action", "exact_match"), &InputEvent::is_action, DEFVAL(false));
115-
ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo", "exact_match"), &InputEvent::is_action_pressed, DEFVAL(false), DEFVAL(false));
116-
ClassDB::bind_method(D_METHOD("is_action_released", "action", "exact_match"), &InputEvent::is_action_released, DEFVAL(false));
117-
ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match"), &InputEvent::get_action_strength, DEFVAL(false));
190+
ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo", "exact_match", "player_id"), &InputEvent::is_action_pressed, DEFVAL(false), DEFVAL(false), DEFVAL(PlayerId::P1));
191+
ClassDB::bind_method(D_METHOD("is_action_released", "action", "exact_match", "player_id"), &InputEvent::is_action_released, DEFVAL(false), DEFVAL(PlayerId::P1));
192+
ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match", "player_id"), &InputEvent::get_action_strength, DEFVAL(false), DEFVAL(PlayerId::P1));
118193

119194
ClassDB::bind_method(D_METHOD("is_canceled"), &InputEvent::is_canceled);
120195
ClassDB::bind_method(D_METHOD("is_pressed"), &InputEvent::is_pressed);
@@ -132,6 +207,7 @@ void InputEvent::_bind_methods() {
132207
ClassDB::bind_method(D_METHOD("xformed_by", "xform", "local_ofs"), &InputEvent::xformed_by, DEFVAL(Vector2()));
133208

134209
ADD_PROPERTY(PropertyInfo(Variant::INT, "device"), "set_device", "get_device");
210+
ADD_PROPERTY(PropertyInfo(Variant::INT, "player"), "set_player", "get_player");
135211

136212
BIND_CONSTANT(DEVICE_ID_EMULATION);
137213
}
@@ -741,6 +817,7 @@ Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, co
741817
mb.instantiate();
742818

743819
mb->set_device(get_device());
820+
mb->set_player_from_device();
744821
mb->set_window_id(get_window_id());
745822
mb->set_modifiers_from_event(this);
746823

@@ -960,6 +1037,7 @@ Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, co
9601037
mm.instantiate();
9611038

9621039
mm->set_device(get_device());
1040+
mm->set_player_from_device();
9631041
mm->set_window_id(get_window_id());
9641042

9651043
mm->set_modifiers_from_event(this);
@@ -1363,6 +1441,7 @@ Ref<InputEvent> InputEventScreenTouch::xformed_by(const Transform2D &p_xform, co
13631441
Ref<InputEventScreenTouch> st;
13641442
st.instantiate();
13651443
st->set_device(get_device());
1444+
st->set_player_from_device();
13661445
st->set_window_id(get_window_id());
13671446
st->set_index(index);
13681447
st->set_position(p_xform.xform(pos + p_local_ofs));
@@ -1488,6 +1567,7 @@ Ref<InputEvent> InputEventScreenDrag::xformed_by(const Transform2D &p_xform, con
14881567
sd.instantiate();
14891568

14901569
sd->set_device(get_device());
1570+
sd->set_player_from_device();
14911571
sd->set_window_id(get_window_id());
14921572

14931573
sd->set_index(index);
@@ -1706,6 +1786,7 @@ Ref<InputEvent> InputEventMagnifyGesture::xformed_by(const Transform2D &p_xform,
17061786
ev.instantiate();
17071787

17081788
ev->set_device(get_device());
1789+
ev->set_player_from_device();
17091790
ev->set_window_id(get_window_id());
17101791

17111792
ev->set_modifiers_from_event(this);
@@ -1748,6 +1829,7 @@ Ref<InputEvent> InputEventPanGesture::xformed_by(const Transform2D &p_xform, con
17481829
ev.instantiate();
17491830

17501831
ev->set_device(get_device());
1832+
ev->set_player_from_device();
17511833
ev->set_window_id(get_window_id());
17521834

17531835
ev->set_modifiers_from_event(this);

core/input/input_event.h

+9-4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class InputEvent : public Resource {
5454
GDCLASS(InputEvent, Resource);
5555

5656
int device = 0;
57+
PlayerId player = PlayerId::P1;
5758

5859
protected:
5960
bool canceled = false;
@@ -68,11 +69,15 @@ class InputEvent : public Resource {
6869
void set_device(int p_device);
6970
int get_device() const;
7071

72+
void set_player(PlayerId p_player);
73+
PlayerId get_player() const;
74+
void set_player_from_device();
75+
7176
bool is_action(const StringName &p_action, bool p_exact_match = false) const;
72-
bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false, bool p_exact_match = false) const;
73-
bool is_action_released(const StringName &p_action, bool p_exact_match = false) const;
74-
float get_action_strength(const StringName &p_action, bool p_exact_match = false) const;
75-
float get_action_raw_strength(const StringName &p_action, bool p_exact_match = false) const;
77+
bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false, bool p_exact_match = false, PlayerId p_player_id = PlayerId::P1) const;
78+
bool is_action_released(const StringName &p_action, bool p_exact_match = false, PlayerId p_player_id = PlayerId::P1) const;
79+
float get_action_strength(const StringName &p_action, bool p_exact_match = false, PlayerId p_player_id = PlayerId::P1) const;
80+
float get_action_raw_strength(const StringName &p_action, bool p_exact_match = false, PlayerId p_player_id = PlayerId::P1) const;
7681

7782
bool is_canceled() const;
7883
bool is_pressed() const;

0 commit comments

Comments
 (0)