Overlays: Add 'reach' and 'exclusive' for hitboxes (#14591)

Allows stretching hitboxes and handling their overlap

reach_up, reach_down, reach_left, reach_right:
- Stretches in one direction:
reach_x, reach_y
- Stretches symmetrically

exclusive:
- If true, blocks input from overlapped hitboxes
range_mod_exclusive:
- Similar, but only applies when this hitbox is extended by range_mod
- After range_mod takes effect, has priority over 'exclusive'

E.g. This creates a D-Pad area and extends its hitbox left & right 50%, up 15%, and down 30%. Then applies range_mod_exclusive:
overlay0_desc0 = "dpad_area,0.15,0.57,rect,0.166228,0.295516"
overlay0_desc0_reach_x = 1.5
overlay0_desc0_reach_up = 1.15
overlay0_desc0_reach_down = 1.3
overlay0_desc0_range_mod = 2.0
overlay0_desc0_range_mod_exclusive = true
This commit is contained in:
neil4 2022-11-05 15:32:22 -05:00 committed by GitHub
parent 7a6c56e947
commit b47fb0b807
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 139 additions and 30 deletions

View File

@ -1166,12 +1166,12 @@ static bool input_overlay_add_inputs_inner(overlay_desc_t *desc,
{ {
/* We need ALL of the inputs to be active, /* We need ALL of the inputs to be active,
* abort. */ * abort. */
desc->updated = false; desc->updated = 0;
return false; return false;
} }
all_buttons_pressed = true; all_buttons_pressed = true;
desc->updated = true; desc->updated = 1;
} }
bank_mask >>= 1; bank_mask >>= 1;
@ -1223,14 +1223,14 @@ static bool input_overlay_add_inputs_inner(overlay_desc_t *desc,
case OVERLAY_TYPE_DPAD_AREA: case OVERLAY_TYPE_DPAD_AREA:
case OVERLAY_TYPE_ABXY_AREA: case OVERLAY_TYPE_ABXY_AREA:
return desc->updated; return (desc->updated != 0);
case OVERLAY_TYPE_KEYBOARD: case OVERLAY_TYPE_KEYBOARD:
if (ol_state ? if (ol_state ?
OVERLAY_GET_KEY(ol_state, desc->retro_key_idx) : OVERLAY_GET_KEY(ol_state, desc->retro_key_idx) :
input_state_internal(port, RETRO_DEVICE_KEYBOARD, 0, desc->retro_key_idx)) input_state_internal(port, RETRO_DEVICE_KEYBOARD, 0, desc->retro_key_idx))
{ {
desc->updated = true; desc->updated = 1;
return true; return true;
} }
break; break;
@ -1394,16 +1394,16 @@ static bool inside_hitbox(const struct overlay_desc *desc, float x, float y)
{ {
case OVERLAY_HITBOX_RADIAL: case OVERLAY_HITBOX_RADIAL:
{ {
/* Ellipsis. */ /* Ellipse. */
float x_dist = (x - desc->x_shift) / desc->range_x_mod; float x_dist = (x - desc->x_hitbox) / desc->range_x_mod;
float y_dist = (y - desc->y_shift) / desc->range_y_mod; float y_dist = (y - desc->y_hitbox) / desc->range_y_mod;
float sq_dist = x_dist * x_dist + y_dist * y_dist; float sq_dist = x_dist * x_dist + y_dist * y_dist;
return (sq_dist <= 1.0f); return (sq_dist <= 1.0f);
} }
case OVERLAY_HITBOX_RECT: case OVERLAY_HITBOX_RECT:
return return
(fabs(x - desc->x_shift) <= desc->range_x_mod) && (fabs(x - desc->x_hitbox) <= desc->range_x_mod) &&
(fabs(y - desc->y_shift) <= desc->range_y_mod); (fabs(y - desc->y_hitbox) <= desc->range_y_mod);
} }
return false; return false;
} }
@ -1411,6 +1411,7 @@ static bool inside_hitbox(const struct overlay_desc *desc, float x, float y)
/** /**
* input_overlay_poll: * input_overlay_poll:
* @out : Polled output data. * @out : Polled output data.
* @ptr_idx : Pointer index
* @norm_x : Normalized X coordinate. * @norm_x : Normalized X coordinate.
* @norm_y : Normalized Y coordinate. * @norm_y : Normalized Y coordinate.
* *
@ -1422,9 +1423,11 @@ static bool inside_hitbox(const struct overlay_desc *desc, float x, float y)
void input_overlay_poll( void input_overlay_poll(
input_overlay_t *ol, input_overlay_t *ol,
input_overlay_state_t *out, input_overlay_state_t *out,
int16_t norm_x, int16_t norm_y, float touch_scale) unsigned ptr_idx, int16_t norm_x, int16_t norm_y, float touch_scale)
{ {
size_t i; size_t i, j;
struct overlay_desc *descs = ol->active->descs;
unsigned int highest_prio = 0;
/* norm_x and norm_y is in [-0x7fff, 0x7fff] range, /* norm_x and norm_y is in [-0x7fff, 0x7fff] range,
* like RETRO_DEVICE_POINTER. */ * like RETRO_DEVICE_POINTER. */
@ -1443,12 +1446,32 @@ void input_overlay_poll(
{ {
float x_dist, y_dist; float x_dist, y_dist;
unsigned int base = 0; unsigned int base = 0;
struct overlay_desc *desc = &ol->active->descs[i]; unsigned int desc_prio = 0;
struct overlay_desc *desc = &descs[i];
if (!desc || !inside_hitbox(desc, x, y)) if (!desc || !inside_hitbox(desc, x, y))
continue; continue;
desc->updated = true; /* Check for exclusive hitbox, which blocks other input.
* range_mod_exclusive has priority over exclusive. */
if (desc->range_mod_exclusive
&& desc->range_x_mod != desc->range_x_hitbox)
desc_prio = 2;
else if (desc->exclusive)
desc_prio = 1;
if (highest_prio > desc_prio)
continue;
if (desc_prio > highest_prio)
{
highest_prio = desc_prio;
memset(out, 0, sizeof(*out));
for (j = 0; j < i; j++)
BIT16_CLEAR(descs[j].updated, ptr_idx);
}
BIT16_SET(desc->updated, ptr_idx);
x_dist = x - desc->x_shift; x_dist = x - desc->x_shift;
y_dist = y - desc->y_shift; y_dist = y - desc->y_shift;
@ -1538,10 +1561,10 @@ void input_overlay_post_poll(
{ {
struct overlay_desc *desc = &ol->active->descs[i]; struct overlay_desc *desc = &ol->active->descs[i];
desc->range_x_mod = desc->range_x; desc->range_x_mod = desc->range_x_hitbox;
desc->range_y_mod = desc->range_y; desc->range_y_mod = desc->range_y_hitbox;
if (desc->updated) if (desc->updated != 0)
{ {
/* If pressed this frame, change the hitbox. */ /* If pressed this frame, change the hitbox. */
desc->range_x_mod *= desc->range_mod; desc->range_x_mod *= desc->range_mod;
@ -1556,10 +1579,32 @@ void input_overlay_post_poll(
} }
input_overlay_update_desc_geom(ol, desc); input_overlay_update_desc_geom(ol, desc);
desc->updated = false; desc->updated = 0;
} }
} }
static void input_overlay_desc_init_hitbox(struct overlay_desc *desc)
{
desc->x_hitbox =
((desc->x_shift + desc->range_x * desc->reach_right) +
(desc->x_shift - desc->range_x * desc->reach_left)) / 2.0f;
desc->y_hitbox =
((desc->y_shift + desc->range_y * desc->reach_down) +
(desc->y_shift - desc->range_y * desc->reach_up)) / 2.0f;
desc->range_x_hitbox =
(desc->range_x * desc->reach_right +
desc->range_x * desc->reach_left) / 2.0f;
desc->range_y_hitbox =
(desc->range_y * desc->reach_down +
desc->range_y * desc->reach_up) / 2.0f;
desc->range_x_mod = desc->range_x_hitbox;
desc->range_y_mod = desc->range_y_hitbox;
}
/** /**
* input_overlay_set_scale_factor: * input_overlay_set_scale_factor:
* @ol : Overlay handle. * @ol : Overlay handle.
@ -1644,6 +1689,8 @@ void input_overlay_scale(struct overlay *ol,
desc->mod_h = 2.0f * scale_h; desc->mod_h = 2.0f * scale_h;
desc->mod_x = adj_center_x - scale_w; desc->mod_x = adj_center_x - scale_w;
desc->mod_y = adj_center_y - scale_h; desc->mod_y = adj_center_y - scale_h;
input_overlay_desc_init_hitbox(desc);
} }
} }
@ -1823,9 +1870,9 @@ void input_overlay_poll_clear(
{ {
struct overlay_desc *desc = &ol->active->descs[i]; struct overlay_desc *desc = &ol->active->descs[i];
desc->range_x_mod = desc->range_x; desc->range_x_mod = desc->range_x_hitbox;
desc->range_y_mod = desc->range_y; desc->range_y_mod = desc->range_y_hitbox;
desc->updated = false; desc->updated = 0;
desc->delta_x = 0.0f; desc->delta_x = 0.0f;
desc->delta_y = 0.0f; desc->delta_y = 0.0f;
@ -2078,7 +2125,7 @@ void input_poll_overlay(
memset(&polled_data, 0, sizeof(struct input_overlay_state)); memset(&polled_data, 0, sizeof(struct input_overlay_state));
if (ol->enable) if (ol->enable)
input_overlay_poll(ol, &polled_data, x, y, touch_scale); input_overlay_poll(ol, &polled_data, i, x, y, touch_scale);
else else
ol->blocked = false; ol->blocked = false;

View File

@ -144,8 +144,7 @@ struct overlay_desc
enum overlay_hitbox hitbox; enum overlay_hitbox hitbox;
enum overlay_type type; enum overlay_type type;
bool updated; uint16_t updated; /* one bit per pointer */
bool movable;
unsigned next_index; unsigned next_index;
unsigned image_index; unsigned image_index;
@ -167,6 +166,21 @@ struct overlay_desc
float x_shift; float x_shift;
float y_shift; float y_shift;
/* These values are used only for hitbox
* detection. A hitbox can be stretched in
* any direction(s) by its 'reach' values */
float x_hitbox;
float y_hitbox;
float range_x_hitbox, range_y_hitbox;
float reach_right, reach_left, reach_up, reach_down;
/* If true, blocks input from overlapped hitboxes */
bool exclusive;
/* Similar, but only applies after range_mod takes affect */
bool range_mod_exclusive;
bool movable;
/* This is a retro_key value for keyboards */ /* This is a retro_key value for keyboards */
unsigned retro_key_idx; unsigned retro_key_idx;
@ -332,7 +346,7 @@ void input_overlay_auto_rotate_(
void input_overlay_poll( void input_overlay_poll(
input_overlay_t *ol, input_overlay_t *ol,
input_overlay_state_t *out, input_overlay_state_t *out,
int16_t norm_x, int16_t norm_y, float touch_scale); unsigned ptr_idx, int16_t norm_x, int16_t norm_y, float touch_scale);
/** /**
* input_overlay_poll_clear: * input_overlay_poll_clear:

View File

@ -232,11 +232,12 @@ static bool task_overlay_load_desc(
bool normalized, float alpha_mod, float range_mod) bool normalized, float alpha_mod, float range_mod)
{ {
float width_mod, height_mod; float width_mod, height_mod;
char conf_key[32]; char conf_key[64];
char overlay_desc_key[32]; char overlay_desc_key[32];
char overlay_desc_normalized_key[32]; char overlay_desc_normalized_key[32];
char overlay[256]; char overlay[256];
float tmp_float = 0.0f; float tmp_float = 0.0f;
int tmp_int = 0;
bool tmp_bool = false; bool tmp_bool = false;
bool ret = true; bool ret = true;
bool by_pixel = false; bool by_pixel = false;
@ -403,6 +404,46 @@ static bool task_overlay_load_desc(
desc->range_x = (float)strtod(list.elems[4].data, NULL) * width_mod; desc->range_x = (float)strtod(list.elems[4].data, NULL) * width_mod;
desc->range_y = (float)strtod(list.elems[5].data, NULL) * height_mod; desc->range_y = (float)strtod(list.elems[5].data, NULL) * height_mod;
snprintf(conf_key, sizeof(conf_key),
"overlay%u_desc%u_reach_right", ol_idx, desc_idx);
desc->reach_right = 1.0f;
if (config_get_float(conf, conf_key, &tmp_float))
desc->reach_right = tmp_float;
snprintf(conf_key, sizeof(conf_key),
"overlay%u_desc%u_reach_left", ol_idx, desc_idx);
desc->reach_left = 1.0f;
if (config_get_float(conf, conf_key, &tmp_float))
desc->reach_left = tmp_float;
snprintf(conf_key, sizeof(conf_key),
"overlay%u_desc%u_reach_up", ol_idx, desc_idx);
desc->reach_up = 1.0f;
if (config_get_float(conf, conf_key, &tmp_float))
desc->reach_up = tmp_float;
snprintf(conf_key, sizeof(conf_key),
"overlay%u_desc%u_reach_down", ol_idx, desc_idx);
desc->reach_down = 1.0f;
if (config_get_float(conf, conf_key, &tmp_float))
desc->reach_down = tmp_float;
snprintf(conf_key, sizeof(conf_key),
"overlay%u_desc%u_reach_x", ol_idx, desc_idx);
if (config_get_float(conf, conf_key, &tmp_float))
{
desc->reach_right = tmp_float;
desc->reach_left = tmp_float;
}
snprintf(conf_key, sizeof(conf_key),
"overlay%u_desc%u_reach_y", ol_idx, desc_idx);
if (config_get_float(conf, conf_key, &tmp_float))
{
desc->reach_up = tmp_float;
desc->reach_down = tmp_float;
}
desc->mod_x = desc->x - desc->range_x; desc->mod_x = desc->x - desc->range_x;
desc->mod_w = 2.0f * desc->range_x; desc->mod_w = 2.0f * desc->range_x;
desc->mod_y = desc->y - desc->range_y; desc->mod_y = desc->y - desc->range_y;
@ -420,6 +461,16 @@ static bool task_overlay_load_desc(
if (config_get_float(conf, conf_key, &tmp_float)) if (config_get_float(conf, conf_key, &tmp_float))
desc->range_mod = tmp_float; desc->range_mod = tmp_float;
snprintf(conf_key, sizeof(conf_key),
"overlay%u_desc%u_exclusive", ol_idx, desc_idx);
desc->exclusive = false;
config_get_bool(conf, conf_key, &desc->exclusive);
snprintf(conf_key, sizeof(conf_key),
"overlay%u_desc%u_range_mod_exclusive", ol_idx, desc_idx);
desc->range_mod_exclusive = false;
config_get_bool(conf, conf_key, &desc->range_mod_exclusive);
snprintf(conf_key, sizeof(conf_key), snprintf(conf_key, sizeof(conf_key),
"overlay%u_desc%u_movable", ol_idx, desc_idx); "overlay%u_desc%u_movable", ol_idx, desc_idx);
desc->movable = false; desc->movable = false;
@ -429,9 +480,6 @@ static bool task_overlay_load_desc(
if (config_get_bool(conf, conf_key, &tmp_bool)) if (config_get_bool(conf, conf_key, &tmp_bool))
desc->movable = tmp_bool; desc->movable = tmp_bool;
desc->range_x_mod = desc->range_x;
desc->range_y_mod = desc->range_y;
input_overlay->pos ++; input_overlay->pos ++;
end: end: