Wie entferne ich Jitter von der Bewegungseingabe?

9

Ich schreibe einen Minecraft-Mod, der Eingaben von der Razer Hydra unterstützt . Es handelt sich um ein Paar Bewegungssteuerungen (eine für jede Hand), die unglaublich genaue Positions- und Rotationsinformationen liefern.

Für die Zwecke dieser Frage bewirkt das Drehen des rechten Controllers auf der Y-Achse, dass der Charakter des Spielers nach links oder rechts schaut (Gieren), und das Drehen auf der X-Achse lässt den Spieler nach oben und unten schauen (Tonhöhe).

Die Eingabe vom Controller wird direkt der Überschrift des Charakters zugeordnet. Wenn der Controller um 30 Grad nach links gedreht wird, dreht sich das Zeichen um 30 Grad nach links.

Das Problem ist, dass der Eingang "zittert". Wenn ich versuche, den Controller perfekt ruhig zu halten, bewegt sich die Überschrift des Charakters unregelmäßig innerhalb eines sehr kleinen Kegels (vielleicht 1 Grad).

Dies liegt wahrscheinlich an meinen zitternden Händen, da die Daten des Controllers scheinbar genau sind.

Ich habe versucht, Eingaben zu filtern, indem ich Daten aus den letzten X-Frames gemittelt habe, aber dies lässt Eingaben butterartig erscheinen.

Meine Frage ist: Wie kann ich die Rotationsdaten filtern, um Jitter zu entfernen, ohne an Präzision zu verlieren?

Äpfel
quelle
Haben Sie darüber nachgedacht, sehr kleine Bewegungsänderungen zu ignorieren?
Philipp
1
Dieses Razer Hyrdra ist interessant ... das erste Mal, dass ich davon gehört habe, ist es noch interessanter, einen Minecraft-Mod zu schreiben, um es zu ergänzen ... Meine Annahme ist: Genau wie bei pixeligen Bildern, um "Rauschen zu reduzieren", das Sie brauchen um das Bild zu verwischen ... Grundsätzlich können Sie entweder mit dem pixeligen Bild oder mit dem verschwommenen Bild leben ... Ich denke, dies ist das gleiche Prinzip, Sie müssen auswählen, welches Sie bevorzugen, wackeliges Spiel oder weniger Präzision ... ... Genau das denke ich ...
Luke San Antonio Bialecki
1
Sie könnten das Beste aus beiden machen. Speichern Sie immer die letzten 50 Frames. Aber durchschnittlich nur über einige von ihnen Frames, abhängig von der Menge der Eingabebewegung. Verlassen Sie sich bei großen Bewegungen nur auf die letzten 5 Bilder und bei kleinen (ansonsten zitternden) Bewegungen auf die letzten 30 Bilder.
Danijar
@sharethis Das ist eine interessante Idee. Ich könnte am Ende so etwas implementieren. Jitter ist kein Problem, sobald die Eingabe einen bestimmten Schwellenwert überschreitet. Daher könnte ich nur die kleinen Eingaben mitteln, um Jitter zu entfernen, und bei großen Eingaben überhaupt nichts mitteln.
Äpfel

Antworten:

8

Dieses Problem zwischen Verzögerung und Reaktionsfähigkeit tritt bei praktisch allen Motion Controllern auf, egal ob es sich um die Hydra, die Wii-Fernbedienung, den Kinect oder den PlayStation Move handelt.

Das Problem ist folgendes:

Wenn ein Eingabestream eingeht, entscheiden Sie Frame für Frame, ob Sie den Eingabedaten vertrauen möchten oder nicht. ob sich die Trends, die Sie jetzt sehen, in den Daten fortgesetzt haben, die Sie in einem Dutzend Millisekunden erhalten. Wenn sich dieser Frame plötzlich nach rechts verschiebt, wissen Sie nicht, ob es sich um echte Eingabedaten handelt (und Sie sollten darauf reagieren) oder ob es sich lediglich um Jitter handelt (und Sie sollten ihn daher ignorieren). Unabhängig davon, was Sie auswählen, wenn Sie später feststellen, dass Sie sich geirrt haben, haben Sie entweder dem Eingabe-Jitter erlaubt, es in Ihr Spiel zu schaffen (im ersten Fall), oder Sie haben eine Verzögerung in Ihr Spiel eingeführt (im letzteren Fall).

Dafür gibt es keine gute Lösung. Eine "richtige" Lösung, um festzustellen, ob die Eingabe real oder jitter ist, erfordert das Wissen darüber, was der Eingabestream in Zukunft tun wird und was er in der Vergangenheit getan hat. Wir können das in Spielen aus offensichtlichen Gründen nicht tun. Nein, es gibt keine Möglichkeit, Rotationsdaten zu filtern, um Jitter zu entfernen, ohne an Präzision zu verlieren, im Kontext eines Spiels, das mit Eingabedaten in Echtzeit arbeitet.

Ich habe gesehen, dass ein großer Hersteller den Entwicklern empfohlen hat, dieses Problem zu lösen, indem die Spieler beim Drehen des Steuerelements eine Taste gedrückt halten, damit das Spiel an diesem Punkt seinen Anti-Jitter-Code ausschalten kann, sodass es nicht verzögert wird. (Ich empfehle dies nicht, aber es ist ein Ansatz).

Ich habe einige Middleware-Bibliotheken für Bewegungseingaben gesehen, die dieses Problem lösen, indem sie eine künstliche Verzögerung in der Eingabe einführen. Es gibt einen Viertelsekundenpuffer, in den die Eingabedaten eingehen, und Ihr Spiel hört erst eine Viertelsekunde später von der Eingabe. Damit die Bibliothek den Jitter für Sie ausgleichen kann, indem Sie wissen, was aus Sicht des Spiels sowohl vor als auch nach der "Gegenwart" passiert. Das funktioniert großartig, abgesehen von einer Viertelsekunde Verzögerung für alles. Aber es ist eine Möglichkeit, das Problem zu lösen, und es kann eine großartige Arbeit leisten, eine Bewegung mit entferntem Jitter auf Kosten einer konstanten Verzögerung genau darzustellen.

Aber ohne so extrem zu werden, gibt es noch einige Dinge, die wir tun können, um das Verhalten erheblich zu verbessern, obwohl wir wissen, dass es immer "Worst-Case-Szenarien" geben wird, die sich nicht ideal verhalten.

Die zentrale Erkenntnis ist, dass wir uns nur dann wirklich um Jitter kümmern, wenn der Controller größtenteils stationär ist, und dass wir uns nur wirklich um Verzögerungen kümmern, wenn der Controller bewegt wird. Daher sollte unsere Strategie darin bestehen, zu versuchen, mit den Dingen so umzugehen, dass wir eine Verzögerung haben, wenn der Controller stationär ist, und Jitter haben, wenn der Controller in Bewegung ist.

Hier sind zwei Möglichkeiten, dies zu tun:

Ein gängiger Ansatz ist ein "gesperrtes / entsperrtes" System, bei dem Sie die Ausrichtung des Geräts verfolgen. Wenn sich das Gerät für kurze Zeit (etwa eine halbe Sekunde) nicht ändert, "sperren" Sie diese Ausrichtung, indem Sie "Nein" verwenden Aktion basierend auf der vom Gerät gemeldeten Ausrichtung, bis sie sich so weit unterscheidet, dass sie erneut entsperrt werden kann. Dies kann orientierungsbasierten Jitter vollständig unterdrücken, ohne eine Verzögerung einzuführen, wenn sich die Ausrichtung aktiv ändert. Es kann einen Hinweis auf eine Verzögerung geben, bevor der Code entscheidet, dass er in den "entsperrten" Modus wechseln muss, aber es ist viel besser, als überall eine Verzögerung zu haben.

Ein anderer Ansatz besteht darin, Eingabedaten aus Frames zusammen zu mitteln. Der wichtige Punkt hierbei ist, nur die Eingabedaten aus Frames zu mitteln, in denen die Eingabedaten vage ähnlich waren. Dies bedeutete, dass kleine Jitter zusammen verschwimmen und weicher werden, größere Änderungen jedoch nicht unscharf werden, da ihre Daten nicht verschwimmen ähnlich genug zu den Daten aus vorherigen Frames.

Es gibt auch andere Möglichkeiten, einen ähnlichen Effekt zu erzielen. Die zentrale Erkenntnis ist, dass Sie nicht gleichzeitig Jitter und Lag in Ihrem Echtzeitspiel haben können, da dies Kenntnisse über die Zukunft erfordern würde. Sie müssen also auswählen, wann das Verhalten Ihres Steuerelements auf das Akzeptieren von Jitter und wann auf das Akzeptieren von Verzögerungen ausgerichtet werden soll, um das Gesamterlebnis so schlecht wie möglich zu gestalten.

Trevor Powell
quelle
Ich habe gerade eine Antwort gepostet. Können Sie bitte Ihre Meinung zu meiner Lösung abgeben?
Äpfel
2

Ich entwickle Software, die Bewegungseingaben in reaktionsschnelle und präzise Mauseingaben umwandelt, und pflege eine Website, die Entwicklern hilft, gleich gute Lösungen selbst zu implementieren. Ich rate generell von Bewegungsschwellen ab, obwohl es davon abhängt, wie viel Reaktionsfähigkeit und Präzision die Spieler wollen, bin ich froh, dass das in Ihrer Situation für Sie funktioniert. Aber hier biete ich eine andere Lösung an:

Ich benutze etwas namens Soft Tiered Smoothing . Die Idee ist, dass wir die Eingabe abhängig von der aktuellen Größe der Kreiselgeschwindigkeit durch verschiedene Glättungsalgorithmen umleiten (in der Praxis ist einer dieser Glättungsalgorithmen nur "keine Glättung"). Das ist der "abgestufte" Teil. Der "weiche" Teil ist, dass wir die Eingabe tatsächlich reibungslos zwischen verschiedenen Glättungsalgorithmen aufteilen können, je nachdem, wie sie mit 2 Schwellenwerten verglichen wird.

Es bewahrt die Verschiebung korrekt und fügt schnellen Bewegungen keinerlei Verzögerung hinzu.

In der Praxis haben Sie zwei Schwellenwerte. Wenn die Größe der Eingabegeschwindigkeit unter dem unteren Schwellenwert liegt, verwenden wir einen einfachen Glättungsalgorithmus (Durchschnitt über mehrere Frames). Wenn es größer als der andere Schwellenwert ist, verwenden wir überhaupt keinen Glättungsalgorithmus. In diesem Fall übergeben wir jedoch immer noch Nullen an den Glättungsalgorithmus mit niedrigerem Schwellenwert.

Wenn die Eingabegeschwindigkeit zwischen den beiden Schwellenwerten liegt, teilen wir die Eingabe entsprechend zwischen den beiden Algorithmen auf.

Hier ist ein Ausschnitt aus dem obigen Artikel:

GetSoftTieredSmoothedInput(Vec2 input, float threshold1, float threshold2) {
    // this will be length(input) for vectors
    float inputMagnitude = Abs(input);

    float directWeight = (inputMagnitude - threshold1) / (threshold2 - threshold1);
    directWeight = clamp(directWeight, 0, 1);

    return GetDirectInput(input * directWeight) +
        GetSmoothedInput(input * (1.0 - directWeight));
}

GetDirectInput gibt nur das zurück, was ihm gegeben wurde, aber es soll zeigen, dass hier ein anderer Glättungsalgorithmus verwendet werden könnte. GetSmoothedInput nimmt eine Geschwindigkeit an und gibt eine geglättete Geschwindigkeit zurück.

Mit Soft Tiered Smoothing wird keine Glättung auf offensichtlich absichtliche Bewegungen angewendet (oberhalb der größeren Schwelle). Es wird eine Glättung angewendet, um kleine Mengen an Jitter zu überdecken, die sich auch auf sehr kleine Bewegungen auswirken. Wenn Sie jedoch Ihre Schwellenwerte richtig einstellen, ist dies nicht sehr bemerkbar. Und es gibt einen sehr reibungslosen Übergang zwischen den beiden (ohne den Jitter tatsächlich verstärkt werden kann).

Während die anderen Antworten zu Recht sagen, dass es schwierig ist, Jitter zu erkennen, sobald eine Eingabe empfangen wird, ist es auch wahr, dass Jitter fast immer eine sehr niedrige Geschwindigkeit aufweist und die mit der Glättung einhergehende Eingangsverzögerung bei Eingaben mit niedriger Geschwindigkeit weitaus weniger spürbar ist .

Wie im Artikel erwähnt, wird dies an mehreren Stellen in meinem Open-Source-Tool JoyShockMapper verwendet , einem Eingabe-Mapper, der Kreiseleingaben in Mauseingaben umwandelt . Selbst für Benutzer anderer Remapping-Tools wie Steam oder reWASD verwenden einige JoyShockMapper gleichzeitig nur für die Kreiselsteuerung.

Bei dieser Antwort wird davon ausgegangen, dass die Eingabe in Winkelgeschwindigkeit (wie bei Steuerungen mit Bewegungssteuerung üblich) erfolgt, nicht in absoluter Ausrichtung (wie es sich anhört, als würde Ihnen die Razer Hydra geben). Bei absoluter Ausrichtung hoffe ich, dass Sie den Unterschied zwischen der aktuellen Ausrichtung und der zuvor gemeldeten Ausrichtung verwenden können, um eine Geschwindigkeit zu erhalten, aber ich weiß nicht genau, ob dies funktioniert, sowie bei Controllern, die die Winkelgeschwindigkeit selbst melden .

Eine gängige Glättungslösung, wenn Sie sich eher mit einer absoluten Position / Orientierung als mit Geschwindigkeiten befassen, besteht darin, über die Zeit in Richtung Ihrer Zielorientierung zu interpolieren - dies wird in diesem Gamasutra-Artikel ausführlich beschrieben. Dies kann auch mit Soft Tiered Smoothing funktionieren. Sie berechnen die Geschwindigkeitsgröße anhand der Differenz zwischen dieser Eingabe und der zuvor gemeldeten Eingabe. Sie wenden den Orientierungsunterschied zwischen diesem Frame und dem letzten Frame multipliziert mit Ihrem "directWeight" -Wert an, wie im obigen Snippet berechnet. Der letzte Schritt ist das Hinzufügen der geglätteten Eingabe. Aufgrund der Funktionsweise der Glättung der interpolierten Ausrichtung wenden Sie jedoch einfach die Änderung der interpolierten Ausrichtung wie gewohnt an - "directWeight" muss überhaupt nicht berücksichtigt werden. Stellen Sie einfach Ihre Zielausrichtung (dies ist das, worauf Sie mit der in diesem Gamasutra-Artikel beschriebenen Glättung interpolieren) auf die Ausrichtung ein, die Sie vom Gerät erhalten, und interpolieren Sie Ihre Ausrichtung darauf, wie in diesem Artikel beschrieben.

Jibb Smart
quelle
1

Es fühlt sich komisch an, meine eigene Frage zu beantworten, aber ich denke, ich habe meine Lösung gefunden.

//Pseudo-Java

update()
{
    //deltaYaw is the change in yaw of the controller since last update
    //yawBuffer is initialized to zero, and only modified here
    //coneAngle is the stabilizing cone

    deltaYaw = getData().yaw;

    yawBuffer += deltaYaw;
    if (abs(yawBuffer) >= coneAngle)
    {
        player.yaw += (abs(yawBuffer)-coneAngle) * sign(yawBuffer);
        yawBuffer = coneAngle * sign(yawBuffer);
    }
}

Anstatt die Überschrift des Spielers direkt zu ändern, "drücke" ich einfach einen Kegel mit einem bestimmten Winkel (in meinem Fall 2,5 Grad). Ich habe eine kleine HTML5-Demo dieser Technik erstellt.

Sobald Sie anfangen, den Kegel zu drücken, gibt es keine Verzögerung und volle Präzision. Wenn Sie jedoch den Kegel nach links drücken und dann nach rechts zielen möchten, müssen Sie sich über den gesamten Winkel des Kegels bewegen, um einen Effekt zu sehen.

Damit werden die Probleme der zeitlichen Verzögerung und der schrecklichen Glättung gelöst, aber das neue Problem einer Bewegungsschwelle eingeführt. Wenn der Stabilisierungskegel jedoch richtig eingestellt ist, ist die Schwelle nicht wahrnehmbar.

Äpfel
quelle
Dies scheint ein weiterer vernünftiger Weg zu sein. Früher gab es (teure) Camcorder, die mit diesem Ansatz ihr Image stabilisierten. Es gibt Ihnen Präzision, wenn Sie eine Bewegung in eine Richtung fortsetzen, hat jedoch eine Verzögerung, wenn Sie die Richtung ändern. Wenn das für dein Spiel gut funktioniert, dann mach es unbedingt. Sie werden keine einzige Lösung finden, die für jedes Spiel am besten funktioniert. Es ist immer ein Kompromiss, bei dem Sie die Nachteile jedes Ansatzes gegen die spezifischen Anforderungen des jeweiligen Spiels abwägen müssen. :)
Trevor Powell