mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2024-11-10 07:12:07 +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
|
||||
and looks up/down
|
||||
- `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
|
||||
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
|
||||
perfectly horizontal or vertical movements the more you mark a
|
||||
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
|
||||
curve on each stick. Increasing this will make small movements to
|
||||
represent much smaller inputs, which helps precision with the sticks.
|
||||
`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
|
||||
controller. Options are `0` = always off, `1` = off with the
|
||||
`+gyroaction` bind to enable, `2` = on with `+gyroaction` to
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
* - http://quakespasm.sourceforge.net
|
||||
* - 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.
|
||||
|
@ -51,7 +54,9 @@ enum {
|
|||
LAYOUT_DEFAULT = 0,
|
||||
LAYOUT_SOUTHPAW,
|
||||
LAYOUT_LEGACY,
|
||||
LAYOUT_LEGACY_SOUTHPAW
|
||||
LAYOUT_LEGACY_SOUTHPAW,
|
||||
LAYOUT_FLICK_STICK,
|
||||
LAYOUT_FLICK_STICK_SOUTHPAW
|
||||
};
|
||||
|
||||
typedef struct
|
||||
|
@ -142,6 +147,7 @@ static cvar_t *joy_left_deadzone;
|
|||
static cvar_t *joy_right_expo;
|
||||
static cvar_t *joy_right_snapaxis;
|
||||
static cvar_t *joy_right_deadzone;
|
||||
static cvar_t *joy_flick_threshold;
|
||||
|
||||
// Joystick haptic
|
||||
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
|
||||
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);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
@ -952,6 +963,59 @@ IN_ApplyExpo(thumbstick_t stick, float exponent)
|
|||
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
|
||||
*/
|
||||
|
@ -961,6 +1025,12 @@ IN_Move(usercmd_t *cmd)
|
|||
// Factor used to transform from SDL joystick input ([-32768, 32767]) to [-1, 1] range
|
||||
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_y;
|
||||
static float joystick_yaw, joystick_pitch;
|
||||
|
@ -1049,16 +1119,30 @@ IN_Move(usercmd_t *cmd)
|
|||
if (left_stick.x || left_stick.y)
|
||||
{
|
||||
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_ApplyExpo(left_stick, joy_left_expo->value);
|
||||
}
|
||||
}
|
||||
|
||||
if (right_stick.x || right_stick.y)
|
||||
{
|
||||
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_ApplyExpo(right_stick, joy_right_expo->value);
|
||||
}
|
||||
}
|
||||
|
||||
switch((int)joy_layout->value)
|
||||
{
|
||||
|
@ -1080,6 +1164,14 @@ IN_Move(usercmd_t *cmd)
|
|||
joystick_yaw = right_stick.x;
|
||||
joystick_pitch = left_stick.y;
|
||||
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
|
||||
joystick_forwardmove = left_stick.y;
|
||||
joystick_sidemove = left_stick.x;
|
||||
|
@ -1129,6 +1221,13 @@ IN_Move(usercmd_t *cmd)
|
|||
{
|
||||
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_snapaxis = Cvar_Get("joy_right_snapaxis", "0.15", 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_y = Cvar_Get("gyro_calibration_y", "0.0", CVAR_ARCHIVE);
|
||||
|
|
|
@ -1814,6 +1814,17 @@ Joy_MenuInit(void)
|
|||
0
|
||||
};
|
||||
|
||||
static const char *stick_layouts_fs[] =
|
||||
{
|
||||
"default",
|
||||
"southpaw",
|
||||
"legacy",
|
||||
"legacy southpaw",
|
||||
"flick stick",
|
||||
"flick stick spaw",
|
||||
0
|
||||
};
|
||||
|
||||
int y = 0;
|
||||
|
||||
s_joy_menu.x = (int)(viddef.width * 0.50f);
|
||||
|
@ -1905,8 +1916,16 @@ Joy_MenuInit(void)
|
|||
y += 10;
|
||||
s_joy_layout_box.generic.name = "stick layout";
|
||||
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.curvalue = ClampCvar(0, 3, joy_layout->value);
|
||||
}
|
||||
Menu_AddItem(&s_joy_menu, (void *)&s_joy_layout_box);
|
||||
|
||||
if (gyro_hardware)
|
||||
|
|
Loading…
Reference in a new issue