Ich arbeite an einem Spiel, bei dem die Spieler auf dem Bildschirm eines Android-Geräts eine Linie von einem Punkt A (x1, y1) zum anderen Punkt B (x2, y2) ziehen müssen.
Ich möchte herausfinden, wie gut diese Zeichnung zu einer geraden Linie passt. Zum Beispiel würde ein Ergebnis von 90% bedeuten, dass die Zeichnung fast perfekt zur Linie passt. Wenn die Spieler eine gekrümmte Linie von A nach B ziehen, sollte sie eine niedrige Punktzahl erhalten.
Die Endpunkte sind nicht im Voraus bekannt. Wie kann ich das machen?
j=1
so dass Sie vergleichen könnentouchList[j]
mittouchList[j-1]
, aber wenntouch.phase == TouchPhase.Began
odertouch.phase == TouchPhase.Ended
die Positionen nicht hinzugefügt werdentouchList
und anschließend in nicht enthaltensumLength
. Dieser Fehler ist in allen Fällen vorhanden, tritt jedoch deutlicher auf, wenn die Zeile nur wenige Segmente enthält.Antworten:
Eine perfekt gerade Linie wäre auch die kürzest mögliche Linie mit einer Gesamtlänge von
sqrt((x1-x2)² + (y1-y2)²)
. Eine stärker kritzelnde Linie ist eine weniger ideale Verbindung und daher zwangsläufig länger.Wenn Sie alle einzelnen Punkte des Pfades, den der Benutzer gezeichnet hat, nehmen und die Abstände zwischen ihnen aufsummieren, können Sie die Gesamtlänge mit der idealen Länge vergleichen. Je kleiner die Gesamtlänge geteilt durch die ideale Länge ist, desto besser ist die Linie.
Hier ist eine Visualisierung. Wenn die schwarzen Punkte die Endpunkte der Geste sind und die blauen Punkte die Punkte, die Sie während der Geste gemessen haben, berechnen und addieren Sie die Längen der grünen Linien und dividieren sie durch die Länge der roten Linie:
Ein Score oder Sinuosity-Index von 1 wäre perfekt, alles, was höher ist, wäre weniger perfekt, alles, was unter 1 liegt, wäre ein Fehler. Wenn Sie die Punktzahl lieber in Prozent haben möchten, dividieren Sie 100% durch diese Zahl.
quelle
Dies ist möglicherweise auch nicht der beste Weg, dies zu implementieren. Ich schlage jedoch vor, dass eine RMSD (Root Mean Square Deviation) in den von Dancrumb genannten Fällen besser sein könnte als nur die Distanzmethode (siehe die ersten beiden Zeilen unten).
RMSD = sqrt(mean(deviation^2))
Hinweis:
=sum(abs(deviation))
)(Bitte entschuldigen Sie die schlechte Qualität meiner Zeichnung)
Wie Sie sehen, müssen Sie
Wenn Ihre Linie in die Richtung zeigt, in die
(1, 3)
Sie möchten(3, -1)
(jeweils durch den Ursprung)h
von der Ideallinie zum Benutzer parallel zu diesem Vektor.quelle
Bestehende Antworten berücksichtigen nicht, dass die Endpunkte willkürlich sind (anstatt gegeben). Daher ist es bei der Messung der Geradheit der Kurve nicht sinnvoll, die Endpunkte zu verwenden (z. B. um die erwartete Länge, den Winkel und die Position zu berechnen). Ein einfaches Beispiel wäre eine gerade Linie, bei der beide Enden geknickt sind. Wenn wir den Abstand von der Kurve und der geraden Linie zwischen den Endpunkten messen, ist dieser ziemlich groß, da die gerade Linie, die wir gezeichnet haben, von der geraden Linie zwischen den Endpunkten versetzt ist.
Wie erkennen wir, wie gerade die Kurve ist? Unter der Annahme, dass die Kurve glatt genug ist, möchten wir wissen, um wie viel sich die Tangente an die Kurve im Durchschnitt ändert. Für eine Linie wäre dies Null (da die Tangente konstant ist).
Wenn wir die Position zum Zeitpunkt t (x (t), y (t)) sein lassen, dann ist der Tangens (Dx (t), Dy (t)), wobei Dx (t) die Ableitung von x zum Zeitpunkt t ist (Diese Seite scheint keine Unterstützung für TeX zu haben). Wenn die Kurve nicht durch die Bogenlänge parametrisiert ist, normieren wir durch Teilen durch || (Dx (t), Dy (t)) ||. Wir haben also einen Einheitsvektor (oder Winkel) der Tangente an die Kurve zum Zeitpunkt t. Der Winkel ist also a (t) = (Dx (t), Dy (t)) / || (Dx (t), Dy (t)) ||
Wir interessieren uns dann für || Da (t) || ^ 2, das entlang der Kurve integriert ist.
Da wir höchstwahrscheinlich eher diskrete Datenpunkte als eine Kurve haben, müssen wir endliche Differenzen verwenden, um die Ableitungen zu approximieren. Also wird Da (t)
(a(t+h)-a(t))/h
. Und a (t) wird((x(t+h)-x(t))/h,(y(t+h)-y(t))/h)/||((x(t+h)-x(t))/h,(y(t+h)-y(t))/h)||
. Dann erhalten wir S, indem wirh||Da(t)||^2
für alle Datenpunkte summieren und möglicherweise durch die Länge der Kurve normalisieren. Am wahrscheinlichsten verwenden wirh=1
, aber es ist wirklich nur ein willkürlicher Skalierungsfaktor.Um es noch einmal zu wiederholen: S ist für eine Linie Null und größer, je mehr sie von einer Linie abweicht. In der gewünschten Format, Verwendung zu konvertieren
1/(1+S)
. Da die Skalierung etwas willkürlich ist, ist es möglich, S mit einer positiven Zahl zu multiplizieren (oder auf andere Weise zu transformieren, z. B. mit bS ^ c anstelle von S), um die Geradheit bestimmter Kurven anzupassen.quelle
Dies ist ein gitterbasiertes System, oder? Finden Sie Ihre eigenen Punkte für die Linie und berechnen Sie die Neigung der Linie. Bestimmen Sie nun anhand dieser Berechnung gültige Punkte, durch die die Linie verlaufen würde, wenn eine gewisse Fehlerquote gegenüber dem exakten Wert besteht.
Bestimmen Sie anhand einer kurzen Anzahl von Testversuchen, welche Anzahl von guten und welche Anzahl von schlechten Übereinstimmungspunkten vorhanden sind, und richten Sie Ihr Spiel anhand einer Skala aus, die dieselben Ergebnisse wie Ihre Tests liefert.
Das heißt, eine kurze Linie mit fast horizontaler Neigung kann 7 Punkte haben, durch die Sie zeichnen können. Wenn Sie konsequent 6 oder mehr der 7 übereinstimmen können, die als Teil der geraden Linie bestimmt wurden, ist dies die höchste Punktzahl. Die Bewertung der Länge und Genauigkeit sollte Teil der Bewertung sein.
quelle
Ein sehr einfaches und intuitives Maß ist der Bereich zwischen der am besten passenden Geraden und der tatsächlichen Kurve. Dies zu bestimmen ist ziemlich einfach:
quelle
Die Idee besteht darin, alle vom Benutzer berührten Punkte beizubehalten und dann den Abstand zwischen jedem dieser Punkte und der beim Loslassen des Bildschirms gebildeten Linie zu bewerten und zu summieren.
Hier ist etwas, um Ihnen den Einstieg in Pseudo-Code zu erleichtern:
Was ist
cumulativeDistance
könnte Ihnen eine Idee über die Armatur geben. Ein Abstand von 0 würde bedeuten, dass der Benutzer die ganze Zeit direkt in der Leitung war. Jetzt müssten Sie einige Tests durchführen, um zu sehen, wie es sich in Ihrem Kontext verhält. Und Sie können den zurückgegebenen WertdistanceOfPointToLine
durch Quadrieren verstärken, um größere Entfernungen von der Linie zu bestrafen.Ich bin nicht mit Unity vertraut, aber der Code
update
hier kann in eineonDrag
Funktion gehen.Und vielleicht möchten Sie irgendwo einen Code hinzufügen, um zu verhindern, dass ein Punkt registriert wird, wenn er mit dem zuletzt registrierten identisch ist. Sie möchten keine Inhalte registrieren, wenn sich der Benutzer nicht bewegt.
quelle
Eine Methode, die Sie verwenden können, besteht darin, die Linie in Segmente zu unterteilen und zwischen jedem Vektor, der das Segment darstellt, und einem Vektor, der eine gerade Linie zwischen dem ersten und dem letzten Punkt darstellt, ein Vektorpunktprodukt zu erstellen. Dies hat den Vorteil, dass Sie extrem "stachelige" Segmente leicht finden können.
Bearbeiten:
Ich würde auch in Betracht ziehen, die Länge des Segments zusätzlich zum Skalarprodukt zu verwenden. Ein sehr kurzer, aber orthogonaler Vektor sollte weniger zählen als ein langer, der eine geringere Abweichung aufweist.
quelle
Am einfachsten und schnellsten kann es sein, einfach herauszufinden, wie dick die Linie sein muss, um alle Punkte der vom Benutzer gezeichneten Linie abzudecken.
Je dicker die Linie sein muss, desto schlechter hat der Benutzer die Linie gezeichnet.
quelle
In Bezug auf MSalters Answer finden Sie hier einige spezifischere Informationen.
Verwenden Sie die Methode der kleinsten Quadrate, um eine Linie für Ihre Punkte anzupassen. Sie suchen im Grunde nach einer Funktion y = f (x), die am besten passt. Sobald Sie es haben, können Sie die tatsächlichen y-Werte verwenden, um das Quadrat der Differenzen zu summieren:
s = Summe über ((yf (x)) ^ 2)
Je kleiner die Summe, desto gerader die Linie.
Wie Sie die beste Annäherung erhalten, erfahren Sie hier: http://math.mit.edu/~gs/linearalgebra/ila0403.pdf
Lesen Sie einfach "Eine gerade Linie einpassen". Beachten Sie, dass t anstelle von x und b anstelle von y verwendet wird. C und D sind näherungsweise zu bestimmen, dann gilt f (x) = C + Dx
Zusätzlicher Hinweis: Natürlich müssen Sie auch die Länge der Linie berücksichtigen. Jede Linie, die aus 2 Punkten besteht, ist perfekt. Ich kenne den genauen Kontext nicht, aber ich würde die Summe der Quadrate geteilt durch die Anzahl der Punkte als Bewertung verwenden. Auch würde ich die Anforderung einer minimalen Länge, einer minimalen Anzahl von Punkten hinzufügen. (Vielleicht 75% der maximalen Länge)
quelle