Wie kann ich die Click'n'Drag-Bewegung auf einen Bereich beschränken?

11

Ich entschuldige mich für den etwas generischen Titel. Ich habe wirklich keine Ahnung, wie ich das erreichen soll, was ich versuche, was es sogar schwieriger macht, nach einer möglichen Lösung zu suchen.

Ich versuche, eine Art Pfadmarkierung zu implementieren (vielleicht gibt es einen am besten geeigneten Namen dafür, aber dies ist der beste, den ich mir vorstellen kann).

Vor dem Spieler befindet sich eine Pfadmarkierung, die bestimmt, wie sich der Spieler bewegt, wenn er mit der Planung seines Zuges fertig ist. Der Spieler kann auf den Marker klicken und ihn an die von ihm gewählte Position ziehen, aber der Marker kann nur innerhalb eines definierten Arbeitsbereichs (dem grauen Bit) verschoben werden.

Pfadmarkierungsdiagramm

Ich habe jetzt zwei Probleme:

Wie genau sollte ich diesen Arbeitsbereich definieren? Ich kann mir vielleicht zwei Vektoren vorstellen, die den Spieler als Ausgangspunkt für die Bildung des bearbeitbaren Winkels haben, und vielleicht könnten diese beiden Bögen aus Kreisen stammen, deren Zentrum dort liegt, wo sich der Spieler befindet, aber ich weiß definitiv nicht, wie ich das alles ausdrücken soll zusammen.

Und zweitens, nachdem ich den Bereich definiert habe, in dem der Marker platziert werden kann, wie kann ich erzwingen, dass der Marker nur in diesem Bereich bleiben soll? Wenn der Spieler beispielsweise auf den Marker klickt und ihn herumzieht, kann er sich innerhalb des Arbeitsbereichs frei bewegen, darf jedoch die Grenzen des Bereichs nicht verlassen. Wenn der Spieler beispielsweise beginnt, den Marker nach oben zu ziehen, bewegt er sich nach oben, bis er das Ende des Arbeitsbereichs erreicht (erstes Diagramm unten). Wenn der Spieler danach jedoch beginnt, seitwärts zu ziehen, muss der Marker dem Ziehen folgen, während er sich noch befindet innerhalb des Bereichs (zweites Diagramm unten).

Erstes Diagramm: Aufwärts bewegen Zweites Diagramm: Nach dem Ziehen

Ich hoffe das war nicht allzu verwirrend. Danke Leute.

Bearbeiten: Falls dies einen Unterschied macht, verwende ich C ++ mit Marmalade SDK.

Vexille
quelle
Ich denke, der Begriff, den Sie suchen, ist "Wegpunkt", und können Sie die Grauzone mathematisch definieren? Die Lösung Ihres Problems wird wahrscheinlich viel einfacher sein, wenn dies der Fall ist.
John McDonald
Sind keine Wegpunkte im Zusammenhang mit der Wegfindung und dergleichen? Auch die mathematische Definition der Grauzone ist eines meiner Probleme: PI glaubt, ich könnte so etwas wie ein Rechteck oder sogar einen Kreis herausfinden, aber diese Art von Form mit zwei Bögen für Seiten und zwei anderen Seiten in einem Winkel ... Es ist ein bisschen über meinen Kopf.
Vexille
Sie müssen auch angeben, welche Sprache und / oder welches Toolkit Sie verwenden, da die Lösungen in erster Linie plattformabhängig sind.
Raceimaztion
"Ja wirklich?" Ich dachte, eine algorhithmische Lösung oder sogar ein Pseudocode würde helfen. Ich werde die Frage trotzdem bearbeiten, nur für den Fall.
Vexille
Definieren Sie die Form in Polarkoordinaten (maximaler Winkel vom Zeichenwinkel und minimaler / maximaler Abstand vom Zeichen). Aktualisieren Sie dann den Wegpunkt nur dort, wo sich die Maus befindet, wenn sich die Maus innerhalb dieser Grenzen befindet. Wenn eines dieser Extreems überschritten wird, wird es auf den höchstmöglichen Extreem-Wert eingestellt. Starten Sie die Aktualisierung, wenn Sie in den Bereich klicken, und stoppen Sie, wenn die Maus losgelassen wird.
ClassicThunder

Antworten:

8

Sie können einen Arbeitsbereich wie den in Ihrer Frage mit drei Werten definieren:

float innerRadius;
float outerRadius;
float maxAngle;

Diese Werte basieren auf einem Mittelpunkt, der möglicherweise nicht vorhanden ist , der die Position des Spielers sein . Die Form des Arbeitsbereichs hängt davon ab, wo Sie diesen Punkt platzieren.

Geben Sie hier die Bildbeschreibung ein

Im obigen Beispiel liegt die Mittelposition einen bestimmten Abstand (sagen wir 50 Einheiten) hinter dem Spieler. Dies könnte leicht berechnet werden als:

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

Um die Position des Markers auf diesen Arbeitsbereich zu beschränken, bewegen Sie den Marker zunächst wie gewohnt. Dann bestätigen Sie den Abstand zwischen dem Mittelpunkt und dem Marker:

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

Schließlich bestätigt den Winkel des Markers an den angegebenen Bereich. Ich werde Pseudocode für diesen verwenden:

- 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

Schauen Sie sich um, wie Sie einen Punkt um einen anderen drehen. Dies kann entweder mit Trigonometrie oder einer Transformationsmatrix erfolgen.

Möglicherweise möchten Sie auch die Größe des Markers berücksichtigen und den Radius und den Winkel zum Ausgleich etwas verkleinern.

Bearbeiten: Beim zweiten Gedanken könnte es natürlicher aussehen, wenn Sie zuerst den Winkel und dann den Abstand überprüfen. Probieren Sie also beide Alternativen aus!

David Gouveia
quelle
Tolle Lösung! Ich bin auf etwas gestoßen, das zwei Kreise und ein Dreieck umfasste, aber Ihre Lösung vereinfacht dies elegant. In Bezug auf die Winkelvalidierung hatte ich mir etwas in der Art ausgedacht, einen normalisierten Vektor zu haben, der bei maxAngle (und einen anderen bei -maxAngle) von playerForward stand und mit der Länge von C-> M multipliziert werden konnte, falls er begrenzt war winkelmäßig. Ich gehe davon aus, dass Ihre Lösung, M um C zu drehen, weniger kostspielig wäre, stimmt das?
Vexille
@Vexille Nun, das Drehen beinhaltet eine cosund eine sinOperation, also bin ich mir nicht sicher. Um diese beiden Vektoren zu berechnen, müssen Sie sie auch drehen, obwohl Sie dies nur tun müssen, wenn sich der Vorwärtsvektor ändert. Sollte es sowieso nicht viel ausmachen, wählen Sie die aus, die Sie implementieren möchten.
David Gouveia
10

Ich dachte darüber nach, wie das Problem gelöst werden könnte, wenn die Form unregelmäßig wäre, und man könnte es nicht mathematisch definieren. Warnung: Dies ist eine schmutzige Lösung, nichts für schwache Nerven.

1. Nehmen Sie Ihre Region:

Geben Sie hier die Bildbeschreibung ein

2. Und konvertieren Sie es in eine monochromatische Bitmap:

Geben Sie hier die Bildbeschreibung ein und nenne es scale_0

3. Klonen Sie die Bitmap und verkleinern Sie sie auf 50%:

Geben Sie hier die Bildbeschreibung ein und nenne es scale_1

4. Und so weiter, bis eine Bitmap mit einer Breite von weniger als 4 Pixel vorhanden ist:

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein Maßstab: 2, 3, 4, 5, 6

5. Jetzt haben wir unseren Bereich als monochromatische Bitmaps mit unterschiedlichen Auflösungen: Geben Sie hier die Bildbeschreibung ein

6. Nehmen Sie das letzte Bild (hier "scale_6") und durchlaufen Sie alle Pixel.

  • Übersetzen Sie die Koordinaten jedes Pixels in Bildschirmkoordinaten: x = Math.pow ( 2, scale_level );wobei scale_level die Zahl ist, die wir nach "scale_" hinzugefügt haben. Wir könnten es auch als Quad-Tree-Level bezeichnen, obwohl wir nicht wirklich mit einem Quad-Tree arbeiten. Mach dasselbe mit y.
  • Überprüfen Sie, ob das Pixel bei übersetztem x & y schwarz ist. Wenn nicht, dann ist es nicht Teil der Form, und Sie sollten nur continuezum nächsten Schritt der Schleife gehen
  • Überprüfen Sie, ob sich das Pixel näher am Mauszeiger befindet als das zuvor überprüfte Pixel. Wenn ja, speichern Sie die Koordinaten des Pixels. Verwenden Sie die Koordinaten vor der Übersetzung, dh die Koordinaten innerhalb der Bitmap.
  • Multiplizieren Sie diese Koordinaten am Ende der Schleife mit 2: x *= 2; y*=2;um sie in Koordinaten im nächsten Bild zu übersetzen (vorherige Skala)

7. Nehmen Sie das vorherige Bild auf (hier "scale_5"), aber durchlaufen Sie nicht alle Pixel. Beginnen Sie bei x = saved_x und enden Sie mit x = saved_x + 2, genau wie bei y. Das heißt, da Sie jetzt nur noch 4 Pixel für jedes Level durchlaufen! Der Rest ist wie in p. 6.

8. Nehmen Sie das erste Bild auf (das größte = das mit der größten Auflösung), durchlaufen Sie erneut 4 Pixel, und Sie haben endlich das Pixel, das dem Mauszeiger am nächsten liegt:

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

9. Ich behandle hier jedoch "M" als Punkt. Wenn Sie möchten, dass der Kreis vollständig passt, müssen Sie die Form zuerst um circle.radiusPixel verkleinern (verkleinern) .

Ich dachte, ich würde hinzufügen, dass dieser Algorithmus nur funktioniert, wenn Sie keine monochromatischen, sondern Graustufenbilder verwenden und ein Pixel als "voll" behandeln, wenn es nicht weiß ist, und als "leer", wenn es genau weiß ist ... ODER wenn Sie die Größe ändern Der Algorithmus ändert jede Gruppe von 4 Pixeln jedes Mal in 1 schwarzes Pixel, wenn mindestens eines dieser 4 Pixel nicht weiß war.

Markus von Broady
quelle
2
+1 für eine Antwort für Formen, die sich nur schwer (wenn nicht unmöglich) mathematisch ausdrücken lassen.
Cypher
Wow, sehr interessant. +1 auch: D
Vexille
Ich habe es in einem realen Projekt implementiert und ich muss sagen, dass einige Probleme aufgetreten sind. Grundsätzlich müssen Sie eine Liste der Gitterzellen erstellen, in der Sie die nächstgelegene Gitterzelle mit dem Namen verwenden closest, und den Abstand zum am weitesten entfernten Punkt in der closest- lassen Sie uns den Abstand benennen furthest_dist. Jetzt müssen Sie alle Zellen aus der Liste entfernen, deren nächster Punkt weiter als der liegt, furthest_distund tiefer gehen. Also statt so etwas: i.imgur.com/4UuFo.png Es ist so ähnlich: i.imgur.com/dyTT3.png
Markus von Broady