Skip to content

Commit 934a1e1

Browse files
committed
Support Input and UI for multiple players
1 parent 1ba8565 commit 934a1e1

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

+740
-243
lines changed

core/config/project_settings.cpp

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

15311531
_add_builtin_input_map();
1532+
GLOBAL_DEF(PropertyInfo(Variant::INT, "input/keyboard_player_id_override", PROPERTY_HINT_ENUM, "P1,P2,P3,P4,P5,P6,P7,P8"), (int)PlayerId::P1);
1533+
GLOBAL_DEF(PropertyInfo(Variant::INT, "input/mouse_player_id_override", PROPERTY_HINT_ENUM, "P1,P2,P3,P4,P5,P6,P7,P8"), (int)PlayerId::P1);
1534+
GLOBAL_DEF(PropertyInfo(Variant::INT, "input/touch_player_id_override", PROPERTY_HINT_ENUM, "P1,P2,P3,P4,P5,P6,P7,P8"), (int)PlayerId::P1);
15321535

15331536
// Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum.
15341537
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
@@ -94,6 +94,10 @@ class ProjectSettings : public Object {
9494
bool project_loaded = false;
9595
List<String> input_presets;
9696

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

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
@@ -56,6 +56,19 @@ class Input : public Object {
5656
MOUSE_MODE_MAX,
5757
};
5858

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

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

135150
bool emulate_touch_from_mouse = false;
136151
bool emulate_mouse_from_touch = false;
@@ -163,18 +178,6 @@ class Input : public Object {
163178
VelocityTrack();
164179
};
165180

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

314-
float get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const;
315-
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;
324+
HashMap<int, Joypad> _get_joy_names() const { return joy_names; }
316325

317326
float get_joy_axis(int p_device, JoyAxis p_axis) const;
318327
String get_joy_name(int p_idx);
@@ -349,8 +358,8 @@ class Input : public Object {
349358

350359
void set_mouse_position(const Point2 &p_posf);
351360

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

355364
void set_emulate_touch_from_mouse(bool p_emulate);
356365
bool is_emulating_touch_from_mouse() const;
@@ -376,6 +385,8 @@ class Input : public Object {
376385

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

core/input/input_enums.h

+32
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,35 @@ inline MouseButtonMask mouse_button_to_mask(MouseButton button) {
136136

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

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
@@ -53,6 +53,7 @@ class InputEvent : public Resource {
5353
GDCLASS(InputEvent, Resource);
5454

5555
int device = 0;
56+
PlayerId player = PlayerId::P1;
5657

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

71+
void set_player(PlayerId p_player);
72+
PlayerId get_player() const;
73+
void set_player_from_device();
74+
7075
bool is_action(const StringName &p_action, bool p_exact_match = false) const;
71-
bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false, bool p_exact_match = false) const;
72-
bool is_action_released(const StringName &p_action, bool p_exact_match = false) const;
73-
float get_action_strength(const StringName &p_action, bool p_exact_match = false) const;
74-
float get_action_raw_strength(const StringName &p_action, bool p_exact_match = false) const;
76+
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;
77+
bool is_action_released(const StringName &p_action, bool p_exact_match = false, PlayerId p_player_id = PlayerId::P1) const;
78+
float get_action_strength(const StringName &p_action, bool p_exact_match = false, PlayerId p_player_id = PlayerId::P1) const;
79+
float get_action_raw_strength(const StringName &p_action, bool p_exact_match = false, PlayerId p_player_id = PlayerId::P1) const;
7580

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

0 commit comments

Comments
 (0)