mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2024-11-13 00:24:44 +00:00
"Flick Stick" controller layout implementation
With southpaw version added to "sticks layout" menu Cvar for length of stick to be considered a flick or rotation Lacks rotation smoothing
This commit is contained in:
parent
0417bc1023
commit
748909fd96
3 changed files with 135 additions and 8 deletions
|
@ -476,6 +476,9 @@ Set `0` by default.
|
||||||
- `2`: *Legacy*, left moves forward/backward and turns, right strafes
|
- `2`: *Legacy*, left moves forward/backward and turns, right strafes
|
||||||
and looks up/down
|
and looks up/down
|
||||||
- `3`: *Legacy Southpaw*, inverted sticks version of previous one
|
- `3`: *Legacy Southpaw*, inverted sticks version of previous one
|
||||||
|
- `4`: *Flick Stick*, left stick moves, right checks your surroundings
|
||||||
|
in 360º, gyro required for looking up/down
|
||||||
|
- `5`: *Flick Stick Southpaw*, swapped sticks version of last one
|
||||||
|
|
||||||
* **joy_left_deadzone** / **joy_right_deadzone**: Inner, circular
|
* **joy_left_deadzone** / **joy_right_deadzone**: Inner, circular
|
||||||
deadzone for each stick, where inputs below this radius will be
|
deadzone for each stick, where inputs below this radius will be
|
||||||
|
@ -486,13 +489,18 @@ Set `0` by default.
|
||||||
deadzone with the shape of a "bowtie", which will help you to do
|
deadzone with the shape of a "bowtie", which will help you to do
|
||||||
perfectly horizontal or vertical movements the more you mark a
|
perfectly horizontal or vertical movements the more you mark a
|
||||||
direction with the stick. Increasing this too much will reduce speed
|
direction with the stick. Increasing this too much will reduce speed
|
||||||
for the diagonals. Default `0.15`.
|
for the diagonals, but will help you to mark 90º/180º turns with Flick
|
||||||
|
Stick. Default `0.15`.
|
||||||
|
|
||||||
* **joy_left_expo** / **joy_right_expo**: Exponents on the response
|
* **joy_left_expo** / **joy_right_expo**: Exponents on the response
|
||||||
curve on each stick. Increasing this will make small movements to
|
curve on each stick. Increasing this will make small movements to
|
||||||
represent much smaller inputs, which helps precision with the sticks.
|
represent much smaller inputs, which helps precision with the sticks.
|
||||||
`1.0` is linear. Default `2.0` (quadratic curve).
|
`1.0` is linear. Default `2.0` (quadratic curve).
|
||||||
|
|
||||||
|
* **joy_flick_threshold**: Used only with Flick Stick, specifies the
|
||||||
|
distance from the center of the stick that will make the player flick
|
||||||
|
or rotate. Default `0.65` (65%).
|
||||||
|
|
||||||
* **gyro_mode**: Operation mode for the gyroscope sensor of the game
|
* **gyro_mode**: Operation mode for the gyroscope sensor of the game
|
||||||
controller. Options are `0` = always off, `1` = off with the
|
controller. Options are `0` = always off, `1` = off with the
|
||||||
`+gyroaction` bind to enable, `2` = on with `+gyroaction` to
|
`+gyroaction` bind to enable, `2` = on with `+gyroaction` to
|
||||||
|
|
|
@ -24,6 +24,9 @@
|
||||||
* - http://quakespasm.sourceforge.net
|
* - http://quakespasm.sourceforge.net
|
||||||
* - https://github.com/Minimuino/thumbstick-deadzones
|
* - https://github.com/Minimuino/thumbstick-deadzones
|
||||||
*
|
*
|
||||||
|
* Flick Stick handling is based on:
|
||||||
|
* http://gyrowiki.jibbsmart.com/blog:good-gyro-controls-part-2:the-flick-stick
|
||||||
|
*
|
||||||
* =======================================================================
|
* =======================================================================
|
||||||
*
|
*
|
||||||
* This is the Quake II input system backend, implemented with SDL.
|
* This is the Quake II input system backend, implemented with SDL.
|
||||||
|
@ -51,7 +54,9 @@ enum {
|
||||||
LAYOUT_DEFAULT = 0,
|
LAYOUT_DEFAULT = 0,
|
||||||
LAYOUT_SOUTHPAW,
|
LAYOUT_SOUTHPAW,
|
||||||
LAYOUT_LEGACY,
|
LAYOUT_LEGACY,
|
||||||
LAYOUT_LEGACY_SOUTHPAW
|
LAYOUT_LEGACY_SOUTHPAW,
|
||||||
|
LAYOUT_FLICK_STICK,
|
||||||
|
LAYOUT_FLICK_STICK_SOUTHPAW
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
@ -142,6 +147,7 @@ static cvar_t *joy_left_deadzone;
|
||||||
static cvar_t *joy_right_expo;
|
static cvar_t *joy_right_expo;
|
||||||
static cvar_t *joy_right_snapaxis;
|
static cvar_t *joy_right_snapaxis;
|
||||||
static cvar_t *joy_right_deadzone;
|
static cvar_t *joy_right_deadzone;
|
||||||
|
static cvar_t *joy_flick_threshold;
|
||||||
|
|
||||||
// Joystick haptic
|
// Joystick haptic
|
||||||
static cvar_t *joy_haptic_magnitude;
|
static cvar_t *joy_haptic_magnitude;
|
||||||
|
@ -180,6 +186,11 @@ static updates_countdown_reasons countdown_reason = REASON_CONTROLLERINIT;
|
||||||
// Factor used to transform from SDL input to Q2 "view angle" change
|
// Factor used to transform from SDL input to Q2 "view angle" change
|
||||||
static float normalize_sdl_gyro = 1.0f / M_PI; // can change depending on hardware
|
static float normalize_sdl_gyro = 1.0f / M_PI; // can change depending on hardware
|
||||||
|
|
||||||
|
// Flick Stick
|
||||||
|
#define FLICK_TIME 6 // number of frames it takes for a flick to execute
|
||||||
|
static float target_angle; // angle to end up facing at the end of a flick
|
||||||
|
static unsigned short int flick_progress = FLICK_TIME;
|
||||||
|
|
||||||
extern void CalibrationFinishedCallback(void);
|
extern void CalibrationFinishedCallback(void);
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
@ -952,6 +963,59 @@ IN_ApplyExpo(thumbstick_t stick, float exponent)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flick Stick handling: detect if the player just started one, or return the
|
||||||
|
* player rotation if stick was already flicked
|
||||||
|
*/
|
||||||
|
static float
|
||||||
|
IN_FlickStick(thumbstick_t stick, float axial_deadzone)
|
||||||
|
{
|
||||||
|
static qboolean is_flicking;
|
||||||
|
static float last_stick_angle;
|
||||||
|
thumbstick_t processed = stick;
|
||||||
|
float angle_change = 0;
|
||||||
|
|
||||||
|
if (IN_StickMagnitude(stick) > min(joy_flick_threshold->value, 1.0f)) // flick!
|
||||||
|
{
|
||||||
|
// Make snap-to-axis only if player wasn't already flicking
|
||||||
|
if (!is_flicking || flick_progress < FLICK_TIME)
|
||||||
|
{
|
||||||
|
processed = IN_SlopedAxialDeadzone(stick, axial_deadzone);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float stick_angle = (180 / M_PI) * atan2f(-processed.x, -processed.y);
|
||||||
|
|
||||||
|
if (!is_flicking)
|
||||||
|
{
|
||||||
|
// Flicking begins now, with a new target
|
||||||
|
is_flicking = true;
|
||||||
|
flick_progress = 0;
|
||||||
|
target_angle = stick_angle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Was already flicking, just turning now
|
||||||
|
angle_change = stick_angle - last_stick_angle;
|
||||||
|
|
||||||
|
// angle wrap: https://stackoverflow.com/a/11498248/1130520
|
||||||
|
angle_change = fmod(angle_change + 180.0f, 360.0f);
|
||||||
|
if (angle_change < 0)
|
||||||
|
{
|
||||||
|
angle_change += 360.0f;
|
||||||
|
}
|
||||||
|
angle_change -= 180.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_stick_angle = stick_angle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
is_flicking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return angle_change;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Move handling
|
* Move handling
|
||||||
*/
|
*/
|
||||||
|
@ -961,6 +1025,12 @@ IN_Move(usercmd_t *cmd)
|
||||||
// Factor used to transform from SDL joystick input ([-32768, 32767]) to [-1, 1] range
|
// Factor used to transform from SDL joystick input ([-32768, 32767]) to [-1, 1] range
|
||||||
static const float normalize_sdl_axis = 1.0f / 32768.0f;
|
static const float normalize_sdl_axis = 1.0f / 32768.0f;
|
||||||
|
|
||||||
|
// Flick Stick's factors to change to the target angle with a feeling of "ease out"
|
||||||
|
static const float rotation_factor[FLICK_TIME] =
|
||||||
|
{
|
||||||
|
0.305555556f, 0.249999999f, 0.194444445f, 0.138888889f, 0.083333333f, 0.027777778f
|
||||||
|
};
|
||||||
|
|
||||||
static float old_mouse_x;
|
static float old_mouse_x;
|
||||||
static float old_mouse_y;
|
static float old_mouse_y;
|
||||||
static float joystick_yaw, joystick_pitch;
|
static float joystick_yaw, joystick_pitch;
|
||||||
|
@ -1049,16 +1119,30 @@ IN_Move(usercmd_t *cmd)
|
||||||
if (left_stick.x || left_stick.y)
|
if (left_stick.x || left_stick.y)
|
||||||
{
|
{
|
||||||
left_stick = IN_RadialDeadzone(left_stick, joy_left_deadzone->value);
|
left_stick = IN_RadialDeadzone(left_stick, joy_left_deadzone->value);
|
||||||
|
if ((int)joy_layout->value == LAYOUT_FLICK_STICK_SOUTHPAW)
|
||||||
|
{
|
||||||
|
cl.viewangles[YAW] += IN_FlickStick(left_stick, joy_left_snapaxis->value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
left_stick = IN_SlopedAxialDeadzone(left_stick, joy_left_snapaxis->value);
|
left_stick = IN_SlopedAxialDeadzone(left_stick, joy_left_snapaxis->value);
|
||||||
left_stick = IN_ApplyExpo(left_stick, joy_left_expo->value);
|
left_stick = IN_ApplyExpo(left_stick, joy_left_expo->value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (right_stick.x || right_stick.y)
|
if (right_stick.x || right_stick.y)
|
||||||
{
|
{
|
||||||
right_stick = IN_RadialDeadzone(right_stick, joy_right_deadzone->value);
|
right_stick = IN_RadialDeadzone(right_stick, joy_right_deadzone->value);
|
||||||
|
if ((int)joy_layout->value == LAYOUT_FLICK_STICK)
|
||||||
|
{
|
||||||
|
cl.viewangles[YAW] += IN_FlickStick(right_stick, joy_right_snapaxis->value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
right_stick = IN_SlopedAxialDeadzone(right_stick, joy_right_snapaxis->value);
|
right_stick = IN_SlopedAxialDeadzone(right_stick, joy_right_snapaxis->value);
|
||||||
right_stick = IN_ApplyExpo(right_stick, joy_right_expo->value);
|
right_stick = IN_ApplyExpo(right_stick, joy_right_expo->value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch((int)joy_layout->value)
|
switch((int)joy_layout->value)
|
||||||
{
|
{
|
||||||
|
@ -1080,6 +1164,14 @@ IN_Move(usercmd_t *cmd)
|
||||||
joystick_yaw = right_stick.x;
|
joystick_yaw = right_stick.x;
|
||||||
joystick_pitch = left_stick.y;
|
joystick_pitch = left_stick.y;
|
||||||
break;
|
break;
|
||||||
|
case LAYOUT_FLICK_STICK: // yaw already set by now
|
||||||
|
joystick_forwardmove = left_stick.y;
|
||||||
|
joystick_sidemove = left_stick.x;
|
||||||
|
break;
|
||||||
|
case LAYOUT_FLICK_STICK_SOUTHPAW:
|
||||||
|
joystick_forwardmove = right_stick.y;
|
||||||
|
joystick_sidemove = right_stick.x;
|
||||||
|
break;
|
||||||
default: // LAYOUT_DEFAULT
|
default: // LAYOUT_DEFAULT
|
||||||
joystick_forwardmove = left_stick.y;
|
joystick_forwardmove = left_stick.y;
|
||||||
joystick_sidemove = left_stick.x;
|
joystick_sidemove = left_stick.x;
|
||||||
|
@ -1129,6 +1221,13 @@ IN_Move(usercmd_t *cmd)
|
||||||
{
|
{
|
||||||
cl.viewangles[PITCH] -= (m_pitch->value * gyro_pitch) * gyroViewFactor;
|
cl.viewangles[PITCH] -= (m_pitch->value * gyro_pitch) * gyroViewFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flick Stick: flick in progress, changing the yaw angle to the target progressively
|
||||||
|
if (flick_progress < FLICK_TIME)
|
||||||
|
{
|
||||||
|
cl.viewangles[YAW] += target_angle * rotation_factor[flick_progress];
|
||||||
|
flick_progress++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
@ -1664,6 +1763,7 @@ IN_Init(void)
|
||||||
joy_right_expo = Cvar_Get("joy_right_expo", "2.0", CVAR_ARCHIVE);
|
joy_right_expo = Cvar_Get("joy_right_expo", "2.0", CVAR_ARCHIVE);
|
||||||
joy_right_snapaxis = Cvar_Get("joy_right_snapaxis", "0.15", CVAR_ARCHIVE);
|
joy_right_snapaxis = Cvar_Get("joy_right_snapaxis", "0.15", CVAR_ARCHIVE);
|
||||||
joy_right_deadzone = Cvar_Get("joy_right_deadzone", "0.16", CVAR_ARCHIVE);
|
joy_right_deadzone = Cvar_Get("joy_right_deadzone", "0.16", CVAR_ARCHIVE);
|
||||||
|
joy_flick_threshold = Cvar_Get("joy_flick_threshold", "0.65", CVAR_ARCHIVE);
|
||||||
|
|
||||||
gyro_calibration_x = Cvar_Get("gyro_calibration_x", "0.0", CVAR_ARCHIVE);
|
gyro_calibration_x = Cvar_Get("gyro_calibration_x", "0.0", CVAR_ARCHIVE);
|
||||||
gyro_calibration_y = Cvar_Get("gyro_calibration_y", "0.0", CVAR_ARCHIVE);
|
gyro_calibration_y = Cvar_Get("gyro_calibration_y", "0.0", CVAR_ARCHIVE);
|
||||||
|
|
|
@ -1814,6 +1814,17 @@ Joy_MenuInit(void)
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char *stick_layouts_fs[] =
|
||||||
|
{
|
||||||
|
"default",
|
||||||
|
"southpaw",
|
||||||
|
"legacy",
|
||||||
|
"legacy southpaw",
|
||||||
|
"flick stick",
|
||||||
|
"flick stick spaw",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
int y = 0;
|
int y = 0;
|
||||||
|
|
||||||
s_joy_menu.x = (int)(viddef.width * 0.50f);
|
s_joy_menu.x = (int)(viddef.width * 0.50f);
|
||||||
|
@ -1905,8 +1916,16 @@ Joy_MenuInit(void)
|
||||||
y += 10;
|
y += 10;
|
||||||
s_joy_layout_box.generic.name = "stick layout";
|
s_joy_layout_box.generic.name = "stick layout";
|
||||||
s_joy_layout_box.generic.callback = StickLayoutFunc;
|
s_joy_layout_box.generic.callback = StickLayoutFunc;
|
||||||
|
if (gyro_hardware || joy_layout->value > 3)
|
||||||
|
{
|
||||||
|
s_joy_layout_box.itemnames = stick_layouts_fs;
|
||||||
|
s_joy_layout_box.curvalue = ClampCvar(0, 5, joy_layout->value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
s_joy_layout_box.itemnames = stick_layouts;
|
s_joy_layout_box.itemnames = stick_layouts;
|
||||||
s_joy_layout_box.curvalue = ClampCvar(0, 3, joy_layout->value);
|
s_joy_layout_box.curvalue = ClampCvar(0, 3, joy_layout->value);
|
||||||
|
}
|
||||||
Menu_AddItem(&s_joy_menu, (void *)&s_joy_layout_box);
|
Menu_AddItem(&s_joy_menu, (void *)&s_joy_layout_box);
|
||||||
|
|
||||||
if (gyro_hardware)
|
if (gyro_hardware)
|
||||||
|
|
Loading…
Reference in a new issue