Come limitare il movimento click'n'drag a un'area?


11

Mi scuso per il titolo piuttosto generico. Non ho davvero idea di come realizzare ciò che sto cercando di fare, il che rende ancora più difficile la ricerca di una possibile soluzione.

Sto cercando di implementare una sorta di marker di percorso (forse c'è un nome più adatto per questo, ma questo è il migliore che ho potuto trovare).

Davanti al giocatore ci sarà un segnalino percorso, che determinerà come il giocatore si muoverà una volta terminata la pianificazione del suo turno. Il giocatore può fare clic e trascinare il marker nella posizione prescelta, ma il marker può essere spostato solo all'interno di un'area di lavoro definita (il bit grigio).

Diagramma marcatore percorso

Quindi ora sono bloccato con due problemi:

Prima di tutto, come dovrei definire esattamente quell'area lavorabile? Posso immaginare forse due vettori che hanno il giocatore come punto di partenza per formare l'angolo praticabile, e forse quei due archi potrebbero provenire da cerchi che hanno il loro centro dove si trova il giocatore, ma sicuramente non so come metterlo tutto insieme.

In secondo luogo, dopo aver definito l'area in cui è possibile posizionare il marker, come posso imporre che il marker rimanga solo all'interno di quell'area? Ad esempio, se il giocatore fa clic e trascina il segnalino in giro, può muoversi liberamente all'interno dell'area di lavoro, ma non deve lasciare i confini dell'area. Quindi, ad esempio, se il giocatore inizia a trascinare il marcatore verso l'alto, si sposterà verso l'alto fino a quando non colpisce la fine dell'area di lavoro (primo diagramma di seguito), ma se successivamente il giocatore inizia a trascinare lateralmente, il marcatore deve seguire il trascinamento mentre è ancora fermo all'interno dell'area (secondo diagramma in basso).

Primo diagramma: spostarsi verso l'alto Secondo diagramma: dopo il trascinamento

Spero che non sia stato tutto troppo confuso. Grazie ragazzi.

Modifica: se questo fa la differenza, sto usando C ++ con Marmalade SDK.


Penso che il termine che stai cercando sia "waypoint" e puoi definire matematicamente l'area grigia? La soluzione al tuo problema sarà probabilmente molto più semplice se lo è.
John McDonald,

I waypoint non sono correlati al pathfinding e simili? Inoltre, definire matematicamente l'area grigia è uno dei miei problemi: PI penso che potrei capire qualcosa come un rettangolo o persino un cerchio, ma quel tipo di forma, con due archi per i lati e due altri lati su un angolo ... È un po 'sopra la mia testa.
Vexille,

Dovrai anche specificare quale lingua e / o toolkit stai usando, poiché le soluzioni dipendono principalmente dalla piattaforma.
Raceimaztion

Veramente? Ho pensato che una soluzione algoritmica o persino uno pseudocodice avrebbero aiutato. Modificherò comunque la domanda, per ogni evenienza.
Vexille,

Definire la forma in coordinate polari (angolo massimo dall'angolo dei caratteri e distanza minima / massima dal carattere). Quindi aggiorna il waypoint solo dove si trova il mouse se il mouse si trova all'interno di tali confini. Se supera una di queste estensioni, agganciarlo al valore più esteso possibile. Inizia l'aggiornamento quando fai clic all'interno dell'area e interrompi quando viene rilasciato il mouse.
ClassicThunder

Risposte:


8

Puoi definire un'area lavorabile come quella nella tua domanda con tre valori:

float innerRadius;
float outerRadius;
float maxAngle;

Questi valori saranno basati su un punto centrale che può essere o meno la posizione del giocatore. La forma dell'area lavorabile dipende da dove si posiziona questo punto.

inserisci qui la descrizione dell'immagine

Nell'esempio sopra la posizione centrale si trova una certa distanza (diciamo 50 unità) dietro il giocatore. Questo potrebbe essere facilmente calcolato come:

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

Per limitare la posizione del marker a quell'area lavorabile, per prima cosa sposta il marker come faresti normalmente. Quindi convalidare la distanza tra il punto centrale e il marker:

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

Infine, convalida l'angolo del marker nell'intervallo specificato. Userò pseudocodice per questo:

- 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

Guarda come ruotare un punto attorno a un altro. Può essere fatto con la trigonometria o una matrice di trasformazione.

Potresti anche prendere in considerazione le dimensioni del marker e ridurre leggermente il raggio e l'angolo per compensare.

Modifica: Ripensandoci, potrebbe sembrare più naturale se si convalida prima l'angolo, quindi la distanza, quindi provare entrambe le alternative!


Ottima soluzione! Mi sono imbattuto in qualcosa che ha coinvolto due cerchi e un triangolo, ma la tua soluzione lo semplifica elegantemente. Circa la convalida dell'angolo, avevo pensato a qualcosa sulla falsariga di avere un vettore normalizzato che si trovava a maxAngle (e un altro a -maxAngle) da playerForward, che poteva essere moltiplicato per la lunghezza di C-> M nel caso fosse di limiti , dal punto di vista dell'angolo. Suppongo che la tua soluzione di rotazione di M attorno a C sarebbe meno costosa, giusto?
Vexille,

@Vexille Bene, la rotazione implica un'operazione cose sinquindi non ne sono sicuro. Ma per calcolare quei due vettori devi anche ruotarli, anche se devi farlo solo quando il vettore in avanti cambia. Non dovrebbe importare molto, scegli quello che preferisci implementare.
David Gouveia,

10

Stavo pensando a come risolvere il problema se la forma fosse irregolare e non si potesse definirla matematicamente. Attenzione: questa è una soluzione sporca, non per i deboli di cuore.

1. Prendi la tua zona:

inserisci qui la descrizione dell'immagine

2. E convertilo in bitmap monocromatica:

inserisci qui la descrizione dell'immagine e chiamalo scale_0

3. Clonare la bitmap e ridimensionarla al 50%:

inserisci qui la descrizione dell'immagine e chiamalo scale_1

4. E così via, finché non c'è una bitmap inferiore a 4 pixel di larghezza / altezza:

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine scala: 2, 3, 4, 5, 6

5. Ora abbiamo la nostra area come bitmap monocromatiche di diverse risoluzioni: inserisci qui la descrizione dell'immagine

6. Prendi l'ultima immagine (qui "scale_6") e scorre tutti i pixel.

  • traduci le coordinate di ciascun pixel in coordinate dello schermo: x = Math.pow ( 2, scale_level );dove scale_level è il numero che abbiamo aggiunto dopo "scale_". Potremmo anche chiamarlo a livello di albero quad, anche se non stiamo davvero lavorando con un albero quad. Fai lo stesso con y.
  • controlla se il pixel tradotto in x & y è nero. In caso contrario, non fa parte della forma e dovresti semplicemente continuepassare al passaggio successivo del ciclo
  • controlla se il pixel è più vicino al cursore del mouse rispetto al pixel precedentemente controllato - in caso affermativo, salva le coordinate del pixel - usa le coordinate prima della traduzione, ovvero le coordinate all'interno della bitmap.
  • alla fine del ciclo, moltiplica queste coordinate per 2: x *= 2; y*=2;per tradurle in coordinate nell'immagine successiva (scala precedente)

7. Prendi l'immagine precedente (qui "scale_5"), ma non scorrere tutti i pixel; inizia con x = saved_x e termina con x = saved_x + 2, lo stesso con y. Cioè, da ora passerai attraverso 4 pixel per ogni livello! Il resto è come a p. 6.

8. Prendi la prima immagine (la più grande = quella con la più grande risoluzione), continua ad attraversare 4 pixel e finalmente hai il pixel più vicino al cursore del mouse:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

9. Tuttavia, sto trattando "M" come un punto qui. Se vuoi che sia un cerchio che si adatti completamente, devi prima contrarre (ridurre) la forma di circle.radiuspixel.

Ho pensato di aggiungere che questo algoritmo funzionerà solo se non utilizzerai immagini monocromatiche ma in scala di grigi e tratterai un pixel come "pieno" se non è bianco e come "vuoto" se è esattamente bianco ... O se il tuo ridimensionamento l'algoritmo cambia ogni gruppo di 4 pixel in 1 pixel nero ogni volta che almeno uno di questi 4 pixel non era bianco.


2
+1 per una risposta per forme difficili (se non impossibili) da esprimere matematicamente.
Cypher

Caspita, molto interessante. Anche +1: D
Vexille,

L'ho implementato in un progetto reale e devo dire che sono emersi alcuni problemi. Fondamentalmente, devi creare un elenco di celle della griglia, in cui prendi la cella della griglia più vicina denominata closest, e controlla la distanza dal punto più lontano nella closest- chiamiamo la distanza furthest_dist. Ora è necessario rimuovere dall'elenco tutte le celle che hanno il punto più vicino oltre il furthest_distlivello e andare più in profondità. Quindi invece di qualcosa del genere: i.imgur.com/4UuFo.png È qualcosa del genere: i.imgur.com/dyTT3.png
Markus von Broady,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.