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,
* abort. */
desc->updated = false;
desc->updated = 0;
return false;
}
all_buttons_pressed = true;
desc->updated = true;
desc->updated = 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_ABXY_AREA:
return desc->updated;
return (desc->updated != 0);
case OVERLAY_TYPE_KEYBOARD:
if (ol_state ?
OVERLAY_GET_KEY(ol_state, desc->retro_key_idx) :
input_state_internal(port, RETRO_DEVICE_KEYBOARD, 0, desc->retro_key_idx))
{
desc->updated = true;
desc->updated = 1;
return true;
}
break;
@ -1394,16 +1394,16 @@ static bool inside_hitbox(const struct overlay_desc *desc, float x, float y)
{
case OVERLAY_HITBOX_RADIAL:
{
/* Ellipsis. */
float x_dist = (x - desc->x_shift) / desc->range_x_mod;
float y_dist = (y - desc->y_shift) / desc->range_y_mod;
/* Ellipse. */
float x_dist = (x - desc->x_hitbox) / desc->range_x_mod;
float y_dist = (y - desc->y_hitbox) / desc->range_y_mod;
float sq_dist = x_dist * x_dist + y_dist * y_dist;
return (sq_dist <= 1.0f);
}
case OVERLAY_HITBOX_RECT:
return
(fabs(x - desc->x_shift) <= desc->range_x_mod) &&
(fabs(y - desc->y_shift) <= desc->range_y_mod);
(fabs(x - desc->x_hitbox) <= desc->range_x_mod) &&
(fabs(y - desc->y_hitbox) <= desc->range_y_mod);
}
return false;
}
@ -1411,6 +1411,7 @@ static bool inside_hitbox(const struct overlay_desc *desc, float x, float y)
/**
* input_overlay_poll:
* @out : Polled output data.
* @ptr_idx : Pointer index
* @norm_x : Normalized X 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(
input_overlay_t *ol,
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,
* like RETRO_DEVICE_POINTER. */
@ -1443,14 +1446,34 @@ void input_overlay_poll(
{
float x_dist, y_dist;
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))
continue;
desc->updated = true;
x_dist = x - desc->x_shift;
y_dist = y - desc->y_shift;
/* 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;
y_dist = y - desc->y_shift;
switch (desc->type)
{
@ -1538,10 +1561,10 @@ void input_overlay_post_poll(
{
struct overlay_desc *desc = &ol->active->descs[i];
desc->range_x_mod = desc->range_x;
desc->range_y_mod = desc->range_y;
desc->range_x_mod = desc->range_x_hitbox;
desc->range_y_mod = desc->range_y_hitbox;
if (desc->updated)
if (desc->updated != 0)
{
/* If pressed this frame, change the hitbox. */
desc->range_x_mod *= desc->range_mod;
@ -1556,10 +1579,32 @@ void input_overlay_post_poll(
}
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:
* @ol : Overlay handle.
@ -1644,6 +1689,8 @@ void input_overlay_scale(struct overlay *ol,
desc->mod_h = 2.0f * scale_h;
desc->mod_x = adj_center_x - scale_w;
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];
desc->range_x_mod = desc->range_x;
desc->range_y_mod = desc->range_y;
desc->updated = false;
desc->range_x_mod = desc->range_x_hitbox;
desc->range_y_mod = desc->range_y_hitbox;
desc->updated = 0;
desc->delta_x = 0.0f;
desc->delta_y = 0.0f;
@ -2078,7 +2125,7 @@ void input_poll_overlay(
memset(&polled_data, 0, sizeof(struct input_overlay_state));
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
ol->blocked = false;

View File

@ -144,8 +144,7 @@ struct overlay_desc
enum overlay_hitbox hitbox;
enum overlay_type type;
bool updated;
bool movable;
uint16_t updated; /* one bit per pointer */
unsigned next_index;
unsigned image_index;
@ -167,6 +166,21 @@ struct overlay_desc
float x_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 */
unsigned retro_key_idx;
@ -332,7 +346,7 @@ void input_overlay_auto_rotate_(
void input_overlay_poll(
input_overlay_t *ol,
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:

View File

@ -232,11 +232,12 @@ static bool task_overlay_load_desc(
bool normalized, float alpha_mod, float range_mod)
{
float width_mod, height_mod;
char conf_key[32];
char conf_key[64];
char overlay_desc_key[32];
char overlay_desc_normalized_key[32];
char overlay[256];
float tmp_float = 0.0f;
int tmp_int = 0;
bool tmp_bool = false;
bool ret = true;
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_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_w = 2.0f * desc->range_x;
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))
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),
"overlay%u_desc%u_movable", ol_idx, desc_idx);
desc->movable = false;
@ -429,9 +480,6 @@ static bool task_overlay_load_desc(
if (config_get_bool(conf, conf_key, &tmp_bool))
desc->movable = tmp_bool;
desc->range_x_mod = desc->range_x;
desc->range_y_mod = desc->range_y;
input_overlay->pos ++;
end: