Skip to content

Commit ec9009e

Browse files
authored
Merge pull request #2084 from pelya/improved_touch_controls
Improved touch controls
2 parents be6ae20 + 9bda8cb commit ec9009e

File tree

7 files changed

+192
-74
lines changed

7 files changed

+192
-74
lines changed

src/control/controller.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ boost::optional<Control> Control_from_string(const std::string& text)
6666
return boost::none;
6767
}
6868

69-
Controller::Controller()
69+
Controller::Controller():
70+
m_touchscreen(false)
7071
{
7172
reset();
7273
}
@@ -81,6 +82,7 @@ Controller::reset()
8182
m_controls[i] = false;
8283
m_old_controls[i] = false;
8384
}
85+
m_touchscreen = false;
8486
}
8587

8688
void
@@ -89,6 +91,12 @@ Controller::set_control(Control control, bool value)
8991
m_controls[static_cast<int>(control)] = value;
9092
}
9193

94+
void
95+
Controller::set_touchscreen(bool value)
96+
{
97+
m_touchscreen = value;
98+
}
99+
92100
bool
93101
Controller::hold(Control control) const
94102
{
@@ -107,6 +115,12 @@ Controller::released(Control control) const
107115
return m_old_controls[static_cast<int>(control)] && !m_controls[static_cast<int>(control)];
108116
}
109117

118+
bool
119+
Controller::is_touchscreen() const
120+
{
121+
return m_touchscreen;
122+
}
123+
110124
void
111125
Controller::update()
112126
{

src/control/controller.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ class Controller
6363

6464
void set_control(Control control, bool value);
6565

66+
/** Touchscreen flag is set by MobileController, cleared when starting the level */
67+
void set_touchscreen(bool value);
68+
6669
/** returns true if the control is pressed down */
6770
bool hold(Control control) const;
6871

@@ -74,13 +77,19 @@ class Controller
7477

7578
void reset();
7679

80+
/** returns true if the controller event has been generated by touchscreen */
81+
bool is_touchscreen() const;
82+
7783
protected:
7884
/** current control status */
7985
bool m_controls[static_cast<int>(Control::CONTROLCOUNT)];
8086

8187
/** control status at last frame */
8288
bool m_old_controls[static_cast<int>(Control::CONTROLCOUNT)];
8389

90+
/** the event has been generated by touchscreen */
91+
bool m_touchscreen;
92+
8493
private:
8594
Controller(const Controller&) = delete;
8695
Controller& operator=(const Controller&) = delete;

src/control/mobile_controller.cpp

Lines changed: 120 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,6 @@
2828
#include "video/drawing_context.hpp"
2929
#include "video/surface.hpp"
3030

31-
// Util to automatically put rectangles in their corners
32-
static Rectf apply_corner(const Rectf& rect, int screen_width, int screen_height)
33-
{
34-
Rectf r = rect;
35-
36-
if (r.p1().x < 0)
37-
r.move(Vector(static_cast<float>(screen_width), 0));
38-
39-
if (r.p1().y < 0)
40-
r.move(Vector(0, static_cast<float>(screen_height)));
41-
42-
return r;
43-
}
44-
4531
MobileController::MobileController() :
4632
m_up(false),
4733
m_down(false),
@@ -61,12 +47,19 @@ MobileController::MobileController() :
6147
m_old_cheats(false),
6248
m_old_debug(false),
6349
m_old_escape(false),
50+
m_fingers(),
6451
m_rect_directions(16.f, -144.f, 144.f, -16.f),
6552
m_rect_jump(-160.f, -80.f, -96.f, -16.f),
6653
m_rect_action(-80.f, -80.f, -16.f, -16.f),
6754
m_rect_cheats(-160.f, 16.f, -96.f, 80.f),
6855
m_rect_debug(-80.f, 16.f, -16.f, 80.f),
6956
m_rect_escape(16.f, 16.f, 64.f, 64.f),
57+
m_draw_directions(16.f, -144.f, 144.f, -16.f),
58+
m_draw_jump(-160.f, -80.f, -96.f, -16.f),
59+
m_draw_action(-80.f, -80.f, -16.f, -16.f),
60+
m_draw_cheats(-160.f, 16.f, -96.f, 80.f),
61+
m_draw_debug(-80.f, 16.f, -16.f, 80.f),
62+
m_draw_escape(16.f, 16.f, 64.f, 64.f),
7063
m_tex_dirs(Surface::from_file("/images/engine/mobile/direction.png")),
7164
m_tex_btn(Surface::from_file("/images/engine/mobile/button.png")),
7265
m_tex_btn_press(Surface::from_file("/images/engine/mobile/button_press.png")),
@@ -90,36 +83,75 @@ MobileController::draw(DrawingContext& context)
9083
if (!g_config->mobile_controls)
9184
return;
9285

93-
m_screen_width = context.get_width();
94-
m_screen_height = context.get_height();
86+
if (m_screen_width != context.get_width() || m_screen_height != context.get_height())
87+
{
88+
m_screen_width = context.get_width();
89+
m_screen_height = context.get_height();
90+
float width = static_cast<float>(m_screen_width);
91+
float height = static_cast<float>(m_screen_height);
92+
// Buttons on Android are bigger, and direction buttons are extra wide
93+
// Use screen height to calculate button size, because 20:9 screen ratios are common
94+
#ifdef __ANDROID__
95+
constexpr float BUTTON_SCALE = 0.4f;
96+
#else
97+
constexpr float BUTTON_SCALE = 0.2f;
98+
#endif
99+
m_rect_directions.set_size(height * BUTTON_SCALE * 4 / 3, height * BUTTON_SCALE);
100+
m_rect_directions.set_pos(Vector(0, height - height * BUTTON_SCALE));
101+
m_draw_directions = Rectf::from_center(m_rect_directions.get_middle(),
102+
Sizef(m_rect_directions.get_height() / 2, m_rect_directions.get_height() / 2));
103+
104+
m_rect_jump.set_size(height * BUTTON_SCALE, height * BUTTON_SCALE);
105+
m_rect_jump.set_pos(Vector(width - height * BUTTON_SCALE, height - height * BUTTON_SCALE));
106+
m_draw_jump = m_rect_jump.grown(-m_rect_jump.get_height() * 3 / 8);
107+
108+
m_rect_action.set_size(height * BUTTON_SCALE, height * BUTTON_SCALE);
109+
m_rect_action.set_pos(Vector(width - 2 * height * BUTTON_SCALE, height - height * BUTTON_SCALE));
110+
m_draw_action = m_rect_action.grown(-m_rect_action.get_height() * 3 / 8);
111+
112+
m_rect_escape.set_size(height * BUTTON_SCALE / 2, height * BUTTON_SCALE / 2);
113+
m_rect_escape.set_pos(Vector(0, 0));
114+
m_draw_escape = m_rect_escape.grown(-m_rect_escape.get_height() / 4);
115+
116+
m_rect_cheats.set_size(height * BUTTON_SCALE / 2, height * BUTTON_SCALE / 2);
117+
m_rect_cheats.set_pos(Vector(width - 2 * height * BUTTON_SCALE / 2, 0));
118+
m_draw_cheats = m_rect_cheats.grown(-m_rect_cheats.get_height() / 4);
119+
120+
m_rect_debug.set_size(height * BUTTON_SCALE / 2, height * BUTTON_SCALE / 2);
121+
m_rect_debug.set_pos(Vector(width - height * BUTTON_SCALE / 2, 0));
122+
m_draw_debug = m_rect_debug.grown(-m_rect_debug.get_height() / 4);
123+
}
124+
125+
PaintStyle translucent;
126+
translucent.set_alpha(0.5f);
95127

96-
context.color().draw_surface_scaled(m_tex_dirs, apply_corner(m_rect_directions, m_screen_width, m_screen_height), LAYER_GUI + 99);
128+
context.color().draw_surface_scaled(m_tex_dirs, m_draw_directions, LAYER_GUI + 99, translucent);
97129

98130
if (m_up)
99-
context.color().draw_surface_scaled(m_tex_up, apply_corner(m_rect_directions, m_screen_width, m_screen_height), LAYER_GUI + 99);
131+
context.color().draw_surface_scaled(m_tex_up, m_draw_directions, LAYER_GUI + 99, translucent);
100132
if (m_down)
101-
context.color().draw_surface_scaled(m_tex_dwn, apply_corner(m_rect_directions, m_screen_width, m_screen_height), LAYER_GUI + 99);
133+
context.color().draw_surface_scaled(m_tex_dwn, m_draw_directions, LAYER_GUI + 99, translucent);
102134
if (m_left)
103-
context.color().draw_surface_scaled(m_tex_lft, apply_corner(m_rect_directions, m_screen_width, m_screen_height), LAYER_GUI + 99);
135+
context.color().draw_surface_scaled(m_tex_lft, m_draw_directions, LAYER_GUI + 99, translucent);
104136
if (m_right)
105-
context.color().draw_surface_scaled(m_tex_rgt, apply_corner(m_rect_directions, m_screen_width, m_screen_height), LAYER_GUI + 99);
137+
context.color().draw_surface_scaled(m_tex_rgt, m_draw_directions, LAYER_GUI + 99, translucent);
106138

107-
context.color().draw_surface_scaled(m_action ? m_tex_btn_press : m_tex_btn, apply_corner(m_rect_action, m_screen_width, m_screen_height), LAYER_GUI + 99);
108-
context.color().draw_surface_scaled(m_tex_action, apply_corner(m_rect_action, m_screen_width, m_screen_height), LAYER_GUI + 99);
139+
context.color().draw_surface_scaled(m_action ? m_tex_btn_press : m_tex_btn, m_draw_action, LAYER_GUI + 99, translucent);
140+
context.color().draw_surface_scaled(m_tex_action, m_draw_action, LAYER_GUI + 99, translucent);
109141

110-
context.color().draw_surface_scaled(m_jump ? m_tex_btn_press : m_tex_btn, apply_corner(m_rect_jump, m_screen_width, m_screen_height), LAYER_GUI + 99);
111-
context.color().draw_surface_scaled(m_tex_jump, apply_corner(m_rect_jump, m_screen_width, m_screen_height), LAYER_GUI + 99);
142+
context.color().draw_surface_scaled(m_jump ? m_tex_btn_press : m_tex_btn, m_draw_jump, LAYER_GUI + 99, translucent);
143+
context.color().draw_surface_scaled(m_tex_jump, m_draw_jump, LAYER_GUI + 99, translucent);
112144

113-
context.color().draw_surface_scaled(m_escape ? m_tex_btn_press : m_tex_btn, apply_corner(m_rect_escape, m_screen_width, m_screen_height), LAYER_GUI + 99);
114-
context.color().draw_surface_scaled(m_tex_pause, apply_corner(m_rect_escape, m_screen_width, m_screen_height).grown(-8.f), LAYER_GUI + 99);
145+
context.color().draw_surface_scaled(m_escape ? m_tex_btn_press : m_tex_btn, m_draw_escape, LAYER_GUI + 99, translucent);
146+
context.color().draw_surface_scaled(m_tex_pause, m_draw_escape.grown(-m_draw_escape.get_height() / 8), LAYER_GUI + 99, translucent);
115147

116148
if (g_config->developer_mode)
117149
{
118-
context.color().draw_surface_scaled(m_cheats ? m_tex_btn_press : m_tex_btn, apply_corner(m_rect_cheats, m_screen_width, m_screen_height), LAYER_GUI + 99);
119-
context.color().draw_surface_scaled(m_tex_cheats, apply_corner(m_rect_cheats, m_screen_width, m_screen_height), LAYER_GUI + 99);
150+
context.color().draw_surface_scaled(m_cheats ? m_tex_btn_press : m_tex_btn, m_draw_cheats, LAYER_GUI + 99, translucent);
151+
context.color().draw_surface_scaled(m_tex_cheats, m_draw_cheats, LAYER_GUI + 99, translucent);
120152

121-
context.color().draw_surface_scaled(m_debug ? m_tex_btn_press : m_tex_btn, apply_corner(m_rect_debug, m_screen_width, m_screen_height), LAYER_GUI + 99);
122-
context.color().draw_surface_scaled(m_tex_debug, apply_corner(m_rect_debug, m_screen_width, m_screen_height), LAYER_GUI + 99);
153+
context.color().draw_surface_scaled(m_debug ? m_tex_btn_press : m_tex_btn, m_draw_debug, LAYER_GUI + 99, translucent);
154+
context.color().draw_surface_scaled(m_tex_debug, m_draw_debug, LAYER_GUI + 99, translucent);
123155
}
124156
}
125157

@@ -149,26 +181,9 @@ MobileController::update()
149181
activate_widget_at_pos(static_cast<float>(x), static_cast<float>(y));
150182
}
151183

152-
// FIXME: This assumes that 1) there is only one touchscreen and 2) SuperTux
153-
// fills the whole screen
154-
if (SDL_GetNumTouchDevices() < 1)
155-
return;
156-
157-
SDL_TouchID device = SDL_GetTouchDevice(0);
158-
159-
if (device == 0)
160-
throw new std::runtime_error("Error getting touchscreen info: " + std::string(SDL_GetError()));
161-
162-
int num_touches = SDL_GetNumTouchFingers(device);
163-
164-
for (int i = 0; i < num_touches; i++)
184+
for (auto& i : m_fingers)
165185
{
166-
SDL_Finger* finger = SDL_GetTouchFinger(device, i);
167-
168-
if (!finger)
169-
continue;
170-
171-
activate_widget_at_pos(finger->x * float(m_screen_width), finger->y * float(m_screen_height));
186+
activate_widget_at_pos(i.second.x, i.second.y);
172187
}
173188
}
174189

@@ -196,6 +211,50 @@ MobileController::apply(Controller& controller) const
196211
controller.set_control(Control::DEBUG_MENU, m_debug);
197212
if (m_escape != m_old_escape)
198213
controller.set_control(Control::ESCAPE, m_escape);
214+
215+
if (m_up || m_down || m_left || m_right || m_jump || m_action || m_cheats || m_debug || m_escape)
216+
{
217+
controller.set_touchscreen(true);
218+
}
219+
}
220+
221+
bool
222+
MobileController::process_finger_down_event(const SDL_TouchFingerEvent& event)
223+
{
224+
Vector pos(event.x * float(m_screen_width), event.y * float(m_screen_height));
225+
m_fingers[event.fingerId] = pos;
226+
return m_rect_jump.contains(pos) ||
227+
m_rect_action.contains(pos) ||
228+
m_rect_escape.contains(pos) ||
229+
m_rect_directions.contains(pos) ||
230+
(g_config->developer_mode && m_rect_cheats.contains(pos)) ||
231+
(g_config->developer_mode && m_rect_debug.contains(pos));
232+
}
233+
234+
bool
235+
MobileController::process_finger_up_event(const SDL_TouchFingerEvent& event)
236+
{
237+
Vector pos(event.x * float(m_screen_width), event.y * float(m_screen_height));
238+
m_fingers.erase(event.fingerId);
239+
return m_rect_jump.contains(pos) ||
240+
m_rect_action.contains(pos) ||
241+
m_rect_escape.contains(pos) ||
242+
m_rect_directions.contains(pos) ||
243+
(g_config->developer_mode && m_rect_cheats.contains(pos)) ||
244+
(g_config->developer_mode && m_rect_debug.contains(pos));
245+
}
246+
247+
bool
248+
MobileController::process_finger_motion_event(const SDL_TouchFingerEvent& event)
249+
{
250+
Vector pos(event.x * float(m_screen_width), event.y * float(m_screen_height));
251+
m_fingers[event.fingerId] = pos;
252+
return m_rect_jump.contains(pos) ||
253+
m_rect_action.contains(pos) ||
254+
m_rect_escape.contains(pos) ||
255+
m_rect_directions.contains(pos) ||
256+
(g_config->developer_mode && m_rect_cheats.contains(pos)) ||
257+
(g_config->developer_mode && m_rect_debug.contains(pos));
199258
}
200259

201260
void
@@ -206,42 +265,41 @@ MobileController::activate_widget_at_pos(float x, float y)
206265

207266
Vector pos(x, y);
208267

209-
if (apply_corner(m_rect_jump, m_screen_width, m_screen_height).contains(pos))
268+
if (m_rect_jump.contains(pos))
210269
m_jump = true;
211270

212-
if (apply_corner(m_rect_action, m_screen_width, m_screen_height).contains(pos))
271+
if (m_rect_action.contains(pos))
213272
m_action = true;
214273

215274
if (g_config->developer_mode)
216275
{
217-
if (apply_corner(m_rect_cheats, m_screen_width, m_screen_height).contains(pos))
276+
if (m_rect_cheats.contains(pos))
218277
m_cheats = true;
219278

220-
if (apply_corner(m_rect_debug, m_screen_width, m_screen_height).contains(pos))
279+
if (m_rect_debug.contains(pos))
221280
m_debug = true;
222281
}
223282

224-
if (apply_corner(m_rect_escape, m_screen_width, m_screen_height).contains(pos))
283+
if (m_rect_escape.contains(pos))
225284
m_escape = true;
226285

227-
Rectf applied = apply_corner(m_rect_directions, m_screen_width, m_screen_height);
228-
Rectf up = applied;
286+
Rectf up = m_rect_directions;
229287
up.set_bottom(up.get_bottom() - up.get_height() * 2.f / 3.f);
230288
if (up.contains(pos))
231289
m_up = true;
232290

233-
Rectf down = applied;
291+
Rectf down = m_rect_directions;
234292
down.set_top(down.get_top() + down.get_height() * 2.f / 3.f);
235293
if (down.contains(pos))
236294
m_down = true;
237295

238-
Rectf left = applied;
239-
left.set_right(left.get_right() - left.get_width() * 2.f / 3.f);
296+
Rectf left = m_rect_directions;
297+
left.set_right(left.get_right() - left.get_width() * 7.f / 12.f);
240298
if (left.contains(pos))
241299
m_left = true;
242300

243-
Rectf right = applied;
244-
right.set_left(right.get_left() + right.get_width() * 2.f / 3.f);
301+
Rectf right = m_rect_directions;
302+
right.set_left(right.get_left() + right.get_width() * 7.f / 12.f);
245303
if (right.contains(pos))
246304
m_right = true;
247305
}

src/control/mobile_controller.hpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@
1717
#ifndef HEADER_SUPERTUX_CONTROL_MOBILE_CONTROLLER_HPP
1818
#define HEADER_SUPERTUX_CONTROL_MOBILE_CONTROLLER_HPP
1919

20+
#include <SDL.h>
21+
#include <map>
22+
2023
#include "config.h"
2124

2225

2326
#include "math/rectf.hpp"
27+
#include "math/vector.hpp"
2428
#include "video/surface_ptr.hpp"
2529

2630
class Controller;
@@ -34,15 +38,26 @@ class MobileController final
3438
void apply(Controller& controller) const;
3539
void update();
3640

41+
/** returns true if the finger event was inside the screen button area */
42+
bool process_finger_down_event(const SDL_TouchFingerEvent& event);
43+
/** returns true if the finger event was inside the screen button area */
44+
bool process_finger_up_event(const SDL_TouchFingerEvent& event);
45+
/** returns true if the finger event was inside the screen button area */
46+
bool process_finger_motion_event(const SDL_TouchFingerEvent& event);
47+
3748
private:
3849
void activate_widget_at_pos(float x, float y);
3950

4051
private:
4152
bool m_up, m_down, m_left, m_right, m_jump, m_action, m_cheats, m_debug, m_escape;
4253
bool m_old_up, m_old_down, m_old_left, m_old_right, m_old_jump, m_old_action, m_old_cheats, m_old_debug, m_old_escape;
4354

44-
const Rectf m_rect_directions, m_rect_jump, m_rect_action, m_rect_cheats,
45-
m_rect_debug, m_rect_escape;
55+
std::map<SDL_FingerID, Vector> m_fingers;
56+
57+
Rectf m_rect_directions, m_rect_jump, m_rect_action, m_rect_cheats,
58+
m_rect_debug, m_rect_escape;
59+
Rectf m_draw_directions, m_draw_jump, m_draw_action, m_draw_cheats,
60+
m_draw_debug, m_draw_escape;
4661
const SurfacePtr m_tex_dirs, m_tex_btn, m_tex_btn_press, m_tex_pause,
4762
m_tex_up, m_tex_dwn, m_tex_lft, m_tex_rgt,
4863
m_tex_jump, m_tex_action, m_tex_cheats, m_tex_debug;

0 commit comments

Comments
 (0)