Skip to content

Commit

Permalink
Joystick: Bias down the axis-to-button threshold if the "co-axis" is …
Browse files Browse the repository at this point in the history
…active.

This makes it much easier to hit the diagonals in the case where you map
the right stick on your 360 controller to say the DPAD for camera
control in some game.

No setting, let's see if that's required.

Fixes #17792, it should now "just work".
  • Loading branch information
hrydgard committed Dec 31, 2023
1 parent eabcd5d commit 706b98a
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 5 deletions.
52 changes: 47 additions & 5 deletions Core/ControlMapper.cpp
Expand Up @@ -18,20 +18,56 @@ using KeyMap::MultiInputMapping;
const float AXIS_BIND_THRESHOLD = 0.75f;
const float AXIS_BIND_THRESHOLD_MOUSE = 0.01f;

float GetDeviceAxisThreshold(int device, const InputMapping &mapping) {

// We reduce the threshold of some axes when another axis on the same stick is active.
// This makes it easier to hit diagonals if you bind an analog stick to four face buttons or D-Pad.
static InputAxis GetCoAxis(InputAxis axis) {
switch (axis) {
case JOYSTICK_AXIS_X: return JOYSTICK_AXIS_Y;
case JOYSTICK_AXIS_Y: return JOYSTICK_AXIS_X;

// This looks weird, but it's simply how XInput axes are mapped.
case JOYSTICK_AXIS_Z: return JOYSTICK_AXIS_RZ;
case JOYSTICK_AXIS_RZ: return JOYSTICK_AXIS_Z;

// Not sure if these two are used.
case JOYSTICK_AXIS_RX: return JOYSTICK_AXIS_RY;
case JOYSTICK_AXIS_RY: return JOYSTICK_AXIS_RX;

default:
return JOYSTICK_AXIS_MAX; // invalid
}
}

float ControlMapper::GetDeviceAxisThreshold(int device, const InputMapping &mapping) {
if (device == DEVICE_ID_MOUSE) {
return AXIS_BIND_THRESHOLD_MOUSE;
}
if (mapping.IsAxis()) {
switch (KeyMap::GetAxisType((InputAxis)mapping.Axis(nullptr))) {
case KeyMap::AxisType::TRIGGER:
return g_Config.fAnalogTriggerThreshold;
case KeyMap::AxisType::STICK:
{
// Co-axis processing, see GetCoAxes comment.
InputAxis axis = (InputAxis)mapping.Axis(nullptr);
InputAxis coAxis = GetCoAxis(axis);
if (coAxis != JOYSTICK_AXIS_MAX) {
float absCoValue = fabsf(rawAxisValue_[(int)coAxis]);
if (absCoValue > 0.0f) {
// Bias down the threshold if the other axis is active.
float biasedThreshold = AXIS_BIND_THRESHOLD * (1.0f - absCoValue * 0.2f);
// INFO_LOG(SYSTEM, "coValue: %f threshold: %f", absCoValue, biasedThreshold);
return biasedThreshold;
}
}
break;
}
default:
return AXIS_BIND_THRESHOLD;
break;
}
} else {
return AXIS_BIND_THRESHOLD;
}
return AXIS_BIND_THRESHOLD;
}

static int GetOppositeVKey(int vkey) {
Expand Down Expand Up @@ -325,7 +361,7 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping, double no
} else {
curTime = iter->second.timestamp;
}
bool down = iter->second.value > GetDeviceAxisThreshold(iter->first.deviceId, mapping);
bool down = iter->second.value > 0.0f && iter->second.value > GetDeviceAxisThreshold(iter->first.deviceId, mapping);
if (!down)
all = false;
}
Expand Down Expand Up @@ -541,10 +577,16 @@ void ControlMapper::Axis(const AxisInput *axes, size_t count) {
KeyMap::LockMappings();
for (size_t i = 0; i < count; i++) {
const AxisInput &axis = axes[i];

if (axis.deviceId == DEVICE_ID_MOUSE && !g_Config.bMouseControl) {
continue;
}

size_t deviceIndex = (size_t)axis.deviceId; // this wraps -1 up high, so will get rejected on the next line.
if (deviceIndex < (size_t)DEVICE_ID_COUNT) {
deviceTimestamps_[deviceIndex] = now;
}
rawAxisValue_[axis.axisId] = axis.value; // these are only used for co-axis mapping
if (axis.value >= 0.0f) {
InputMapping mapping(axis.deviceId, axis.axisId, 1);
InputMapping opposite(axis.deviceId, axis.axisId, -1);
Expand Down
5 changes: 5 additions & 0 deletions Core/ControlMapper.h
Expand Up @@ -59,11 +59,16 @@ class ControlMapper {
void onVKeyAnalog(int deviceId, int vkey, float value);

void UpdateCurInputAxis(const InputMapping &mapping, float value, double timestamp);
float GetDeviceAxisThreshold(int device, const InputMapping &mapping);

// To track mappable virtual keys. We can have as many as we want.
float virtKeys_[VIRTKEY_COUNT]{};
bool virtKeyOn_[VIRTKEY_COUNT]{}; // Track boolean output separaately since thresholds may differ.

// This is only used for co-axis (analog stick to buttons), so not bothering to track separately
// per device.
float rawAxisValue_[JOYSTICK_AXIS_MAX]{};

double deviceTimestamps_[(size_t)DEVICE_ID_COUNT]{};

int lastNonDeadzoneDeviceID_[2]{};
Expand Down

0 comments on commit 706b98a

Please sign in to comment.