¿Cómo limitar el movimiento click'n'drag a un área?

11

Pido disculpas por el título algo genérico. Realmente no tengo mucha idea sobre cómo lograr lo que estoy tratando de hacer, lo que hace que sea más difícil incluso buscar una posible solución.

Estoy tratando de implementar una especie de marcador de ruta (tal vez hay un nombre más adecuado para ello, pero es lo mejor que se me ocurre).

Delante del jugador habrá un marcador de ruta, que determinará cómo se moverá el jugador una vez que termine de planificar su turno. El jugador puede hacer clic y arrastrar el marcador a la posición que elija, pero el marcador solo se puede mover dentro de un área de trabajo definida (el bit gris).

Diagrama de marcador de ruta

Así que ahora estoy atrapado con dos problemas:

En primer lugar, ¿cómo debo definir exactamente esa área viable? Me imagino quizás dos vectores que tienen al jugador como punto de partida para formar el ángulo viable, y tal vez esos dos arcos podrían provenir de círculos que tienen su centro donde está el jugador, pero definitivamente no sé cómo poner todo esto. juntos.

Y en segundo lugar, después de definir el área donde se puede colocar el marcador, ¿cómo puedo hacer que el marcador solo permanezca dentro de esa área? Por ejemplo, si el jugador hace clic y arrastra el marcador, puede moverse libremente dentro del área de trabajo, pero no debe abandonar los límites del área. Entonces, por ejemplo, si el jugador comienza a arrastrar el marcador hacia arriba, se moverá hacia arriba hasta que llegue al final del área de trabajo (primer diagrama a continuación), pero si después de eso el jugador comienza a arrastrar hacia los lados, el marcador debe seguir el arrastre mientras aún dentro del área (segundo diagrama a continuación).

Primer diagrama: moverse hacia arriba Segundo diagrama: siguiendo el arrastre

Espero que esto no haya sido demasiado confuso. Gracias chicos.

Editar: en caso de que esto haga la diferencia, estoy usando C ++ con Marmalade SDK.

Vexille
fuente
Creo que el término que estás buscando es "waypoint", y ¿puedes definir matemáticamente el área gris? La solución a su problema probablemente será mucho más fácil si lo es.
John McDonald
¿Arent waypoints relacionados con la búsqueda de rutas y tal? Además, definir matemáticamente el área gris es uno de mis problemas: PI creo que podría imaginar algo como un rectángulo o incluso un círculo, pero ese tipo de forma, con dos arcos para los lados y otros dos lados en un ángulo ... Es un poco sobre mi cabeza
Vexille
También deberá especificar qué idioma y / o kit de herramientas está utilizando, ya que las soluciones dependen principalmente de la plataforma.
Raceimaztion
De Verdad? Pensé que una solución algorítmica o incluso un pseudocódigo ayudaría. Editaré la pregunta de todos modos, por si acaso.
Vexille
Defina la forma en coordenadas polares (ángulo máximo desde el ángulo de caracteres y distancia mínima / máxima desde el carácter). Luego, solo actualice el waypoint a donde está el mouse si el mouse está dentro de esos límites. Si excede cualquiera de esos extreems, ajústelo al valor más extreem posible. Comience a actualizar cuando haga clic dentro del área y deténgase cuando suelte el mouse.
ClassicThunder

Respuestas:

8

Puede definir un área viable como la de su pregunta con tres valores:

float innerRadius;
float outerRadius;
float maxAngle;

Estos valores se basarán en un punto central que puede ser o no la posición del jugador. La forma del área viable depende de dónde coloque este punto.

ingrese la descripción de la imagen aquí

En el ejemplo anterior, la posición central se encuentra a cierta distancia (digamos 50 unidades) detrás del jugador. Esto podría calcularse fácilmente como:

float offset = -50;
Vector2 centerPosition = playerPosition + offset * playerForward;

Para limitar la posición del marcador a esa área viable, primero mueva el marcador como lo haría normalmente. Luego, valide la distancia entre el punto central y el marcador:

Vector2 direction = markerPosition - centerPosition;
float distance = direction.Length();
direction.Normalize();
markerPosition = centerPosition + direction * clamp(distance, innerRadius, outerRadius);

Finalmente, valide el ángulo del marcador al rango especificado. Usaré pseudocódigo para este:

- Find angle between vector C->M and vector playerForward
- If abs(angle) <= maxAngle Then do nothing
- Else If angle > 0 Then rotate M around C by maxAngle-angle
- Else If angle < 0 Then rotate M around C by -maxAngle-angle

Mire a su alrededor sobre cómo rotar un punto alrededor de otro. Se puede hacer con trigonometría o con una matriz de transformación.

También es posible que desee tener en cuenta el tamaño del marcador y hacer que el radio y el ángulo sean un poco más pequeños para compensar.

Editar: Pensándolo bien, podría parecer más natural si primero valida el ángulo, luego la distancia, ¡así que pruebe ambas alternativas!

David Gouveia
fuente
Gran solución! Me he encontrado con algo que involucraba dos círculos y un triángulo, pero su solución lo simplifica elegantemente. Sobre la validación del ángulo, había pensado en algo similar a tener un vector normalizado que se encontraba en maxAngle (y otro en -maxAngle) desde playerForward, que podría multiplicarse por la longitud de C-> M en caso de que fuera de límites en ángulo Supongo que su solución de girar M alrededor de C sería menos costosa, ¿no es así?
Vexille
@Vexille Bueno, implica una rotación cosy una sinoperación, por lo que no estoy seguro. Pero para calcular esos dos vectores también necesita rotarlos, aunque solo necesita hacerlo cuando cambia el vector hacia adelante. De todos modos, no debería importar mucho, elija el que prefiera implementar.
David Gouveia
10

Estaba pensando cómo se podría resolver el problema si la forma fuera irregular, y no se podría definir matemáticamente. Advertencia: esta es una solución sucia, no para los débiles de corazón.

1. Tome su área:

ingrese la descripción de la imagen aquí

2. Y conviértalo a un mapa de bits monocromático:

ingrese la descripción de la imagen aquí y nómbralo scale_0

3. Clone el mapa de bits y reduzca su escala al 50%:

ingrese la descripción de la imagen aquí y nómbralo scale_1

4. Y así sucesivamente, hasta que haya un mapa de bits de menos de 4 píxeles de ancho / alto:

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí escala: 2, 3, 4, 5, 6

5. Ahora tenemos nuestra área como mapas de bits monocromáticos de diferentes resoluciones: ingrese la descripción de la imagen aquí

6. Tome la última imagen (aquí "scale_6") e itere a través de todos sus píxeles.

  • traduce las coordenadas de cada píxel a coordenadas de pantalla: x = Math.pow ( 2, scale_level );donde scale_level es el número que agregamos después de "scale_". También podríamos llamarlo un nivel de árbol cuádruple, aunque en realidad no estamos trabajando con un árbol cuádruple. Haz lo mismo con y.
  • compruebe si el píxel en x & y traducido es negro. Si no es así, entonces no es parte de la forma, y ​​solo debe continuepasar al siguiente paso del bucle
  • verifique si el píxel está más cerca del cursor del mouse que el píxel previamente verificado; en caso afirmativo, guarde las coordenadas del píxel; use las coordenadas antes de la traducción, es decir, las coordenadas dentro del mapa de bits.
  • al final del ciclo, multiplique estas coordenadas por 2: x *= 2; y*=2;para traducirlas a coordenadas en la siguiente imagen (escala anterior)

7. Tome la imagen anterior (aquí "scale_5"), pero no recorra todos los píxeles; comienza en x = salvado_x y termina con x = salvado_x + 2, lo mismo con y. Es decir, ¡ahora solo recorrerás 4 píxeles para cada nivel! El resto es como en la p. 6)

8. Tome la primera imagen (la más grande = la que tenga la mayor resolución), vuelva a recorrer 4 píxeles y finalmente tendrá el píxel más cercano al cursor del mouse:

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

9. Sin embargo, estoy tratando "M" como un punto aquí. Si desea que sea un círculo que se ajuste por completo, circle.radiusprimero debe contraer (reducir) la forma por píxeles.

Pensé agregar que este algoritmo solo funcionará si usa imágenes no monocromáticas sino en escala de grises y trata un píxel como "lleno" si no es blanco, y como "vacío" si es exactamente blanco ... O si cambia el tamaño El algoritmo cambia cada grupo de 4 píxeles en 1 píxel negro cada vez que al menos uno de estos 4 píxeles no era blanco.

Markus von Broady
fuente
2
+1 para una respuesta para formas que son difíciles (si no imposibles) de expresar matemáticamente.
Cypher
Guau, muy interesante. +1 también: D
Vexille
Lo implementé en un proyecto real, y debo decir que surgieron algunos problemas. Básicamente, debe hacer una lista de celdas de cuadrícula, donde toma la celda de cuadrícula más cercana nombrada closest, y verifica la distancia al punto más alejado en el closest- nombremos la distancia furthest_dist. Ahora debe eliminar de la lista todas las celdas que tienen su punto más cercano más allá del furthest_distnivel más profundo. Entonces, en lugar de algo como esto: i.imgur.com/4UuFo.png Es algo como esto: i.imgur.com/dyTT3.png
Markus von Broady