Dasselbe Schiff auf jedem Bildschirm
Eine tägliche Bestenliste ist nur fair, wenn das Fliegen des gleichen Höhlenabschnitts auf einem alten kleinen Handy, einem 144-Hz-Tablet und einem Desktop-Browser gleich schwer ist. Hier ist, was das erforderte, und was ich nicht gleich machen konnte.
Der Beitrag zur täglichen Höhle handelt davon, die Welt für jeden Spieler identisch zu machen. Dieser handelt von der anderen Hälfte der Fairness: das Schiff identisch verhalten zu lassen. Es gibt zwei getrennte Arten, wie ein Gerät verändern kann, wie sich das Spiel spielt, und sie brauchen zwei getrennte Lösungen. Die eine ist, wie oft der Bildschirm aktualisiert wird. Die andere ist, wie groß der Bildschirm ist.
Problem eins: die Bildwiederholrate
Das Schiff hat eine Position und eine Geschwindigkeit. In jedem Frame ändert Schwerkraft oder Schub die Geschwindigkeit, und die Geschwindigkeit ändert die Position. Der naheliegende Weg, das zu schreiben, ist:
vy += accel * dt; // update velocity
py += vy * dt; // then move by the NEW velocity
Es sieht harmlos aus, ist aber subtil falsch. Die zweite Zeile tut so, als hätte das Schiff das ganze Frame bereits mit seiner Endgeschwindigkeit des Frames zurückgelegt. Wenn das Schiff beschleunigt, überschießt das um die Hälfte von accel × dt² bei jedem einzelnen Frame. Der Haken ist, dass dt, die Framelänge, auf jedem Gerät unterschiedlich ist, also ist auch das Überschießen unterschiedlich, und ein Schiff flog auf jeder Bildwiederholrate eine leicht andere Bahn.
Ich habe es mit einem gehaltenen Schub über eine halbe Sekunde gemessen. Die exakte Antwort ist 225 Pixel. Der alte Integrator ergab 228 bei 144 Hz, 232 bei 60 Hz und 240 bei 30 Hz. Drei Geräte, drei verschiedene Distanzen, bei derselben Eingabe.
Die Lösung ist die Trapezregel, die den Durchschnitt der Geschwindigkeit vor und nach dem Frame verwendet:
const vyPrev = vy;
vy += accel * dt;
py += (vyPrev + vy) * 0.5 * dt; // average speed over the frame
Bei konstanter Beschleunigung ist das exakt, unabhängig von der Framelänge. Das Wiederholen eines identischen Eingabeplans ergibt jetzt eine Streuung von 0,000 Pixeln über 12 bis 144 Hz. Vorher waren es 4,1 Pixel. Ein Test prüft beide Hälften: dass der neue Integrator über alle Bildwiederholraten übereinstimmt und dass der alte tatsächlich divergierte, damit der Test fehlschlägt, falls jemand das zurücksetzt, statt dass die Bestenliste unbemerkt abdriftet.
Zwei ehrliche Vorbehalte
Es hat die Punktelücke zwischen Bildwiederholraten nicht geschlossen. Ich hatte das erwartet. Ein simulierter Pilot erzielte danach bei 144 Hz immer noch etwa ein Drittel mehr Punkte als bei 60 Hz. Diese Lücke ist keine Physik. Es ist Eingabeauflösung: Ein Daumen kann nur an einer Framegrenze zwischen Halten und Nicht-Halten wechseln, also gibt ein schnellerer Bildschirm tatsächlich feinere Kontrolle. Kein Integrator kann das beheben, und ein Bot, der seine Eingabe jedes Frame umschaltet, übertreibt das im Vergleich zu einem Menschen. Die Physik-Hälfte ist jetzt exakt, und der Rest ist eine Eigenschaft der Hardware.
Es ist bei 60 Hz ein ganz leichter Nerf. Ein halbsekündiger Anstieg legt jetzt 225 Pixel statt 232 zurück. Ich habe das unkompensiert gelassen. Sollte ein echter Gerätetest das Schiff je träge finden, ist der Hebel die Schub-Konstante, etwa 3% mehr würden das alte Halbsekunden-Manöver wiederherstellen. Ein guter Moment, um zu erwähnen, dass der Schub schon zweimal wegen Spieler-Feedback geändert wurde, und diese Geschichte ist der Grund, warum ich vorsichtig bin, ihn anzufassen.
Problem zwei: die Bildschirmhöhe
Schwerkraft in Pixel pro Sekunde zum Quadrat bedeutet auf einem 375-Punkte hohen Handy und einem 520-Punkte hohen Tablet ganz Verschiedenes. Wären die Konstanten gleich, würde derselbe Anstieg auf dem Tablet einen viel kleineren Anteil des Bildschirms durchqueren, und das Spiel würde sich schwerer anfühlen.
Also werden die Konstanten gegen eine Referenzhöhe von 440 Punkten definiert, die Querformat-Höhe des größten iPhones, wo das Spielgefühl abgestimmt und getestet wurde. Auf jedem anderen Bildschirm werden Schwerkraft, Schub und Höchstgeschwindigkeit alle mit H / 440 multipliziert. Da der Korridor-Halbabstand auch mit der Höhe skaliert, ist jede Flugbahn geometrisch ähnlich zur Referenz: derselbe Anteil des Korridors pro Sekunde durchquert, dieselbe Zeit von einer Wand zur anderen, dasselbe gefühlte Ansprechen. Ein größerer Bildschirm bekommt eine steilere Beschleunigung in Pixeln, ein kleinerer eine sanftere, mit Absicht.
Das bringt eine Regel für allen zukünftigen Code mit sich: alles, was über vertikale Bewegung urteilt, muss verhältnisbasiert sein. Ein Bruchteil der Höchstgeschwindigkeit, ein Bruchteil der Höhe. Niemals eine rohe Pixel-pro-Sekunde-Konstante gegen die Geschwindigkeit des Schiffs verglichen, weil diese Zahl nur auf einem Gerät stimmt. Wenn ein Literal unvermeidbar ist, wird es mit demselben Faktor multipliziert.
Die Web-Version folgt derselben Regel. Der Browser-Build klemmt seine Canvas auf 956 mal 440, dieselbe Fläche wie das Referenz-iPhone, sodass das Spiel im Browser eine Pixel- und Physik-Kopie davon ist. Ein früherer Web-Build hatte seinen eigenen Gefühls-Multiplikator, der nur existierte, um die Schwammigkeit einer anderen Begrenzung zu bekämpfen, und er wurde gelöscht, sobald die Begrenzung übereinstimmte.
Problem drei: die Bildschirmbreite
Höhe ist die vertikale Achse. Breite ist die horizontale, und sie ist subtiler. Hindernisse werden in festen Abständen in der Welt platziert. Die Höhle scrollt mit einer Geschwindigkeit, die einen Faktor für die Bildschirmbreite enthält. Auf einem breiteren Bildschirm ziehen dieselben Hindernisse also in Bildschirm-Begriffen schneller vorbei, und die Zeit, die du hast, um auf jedes zu reagieren, sinkt proportional zur Breite.
Das war ein echtes Leck. Ein großes Android-Tablet mit etwa 1280 Punkten Breite sah die gemeinsame Höhle rund 1,75-mal schneller als ein kleines Handy. Also ist die Breite auf jeder Plattform auf 956 Punkte begrenzt, die Breite des breitesten iPhones im Querformat. Ein breiteres Gerät bekommt links und rechts schwarze Balken. Es ist auf iPhones ein No-Op und begrenzt nur große Android-Geräte und Desktop-Browser.
Die Begrenzung hat einen Rest, den ich nicht behoben habe. Der Kollisionsradius des Spielers skaliert mit der Breite, während der Korridor mit der Höhe skaliert, sodass das Schiff auf einem quadratischeren Tablet relativ zur Lücke etwas kleiner ist als auf einem Handy, etwa 20% nachsichtiger. Das zu beheben bedeutet, die Hitbox zu ändern, was eine Gefühlsänderung ist, die einen eigenen Playtest braucht, und es irrt in die nachsichtige Richtung, kann also warten.
Fairness testen statt sie zu behaupten
Nichts davon ist beim Spielen sichtbar. Man kann ein paar Pixel Abweichung nicht spüren, und niemand spielt dieselbe Höhle auf sechs Geräten gleichzeitig. Also hat jede Behauptung einen Test, der sie nachspielt: Der Physik-Test integriert eine feste Eingabe bei Bildwiederholraten von 12 bis 144 Hz, und ein Höhlen-Test spielt die echten Spawner auf sechs Gerätegrößen nach und vergleicht das Ergebnis Byte für Byte. Eine Fairness-Eigenschaft, die man nicht testet, ist meiner Erfahrung nach schon leise kaputtgegangen.
Kurz gesagt ist TUNL auf drei getrennte Arten fair über Bildschirme hinweg (Bildwiederholrate, Höhe und Breite), jede mit einer anderen Ursache, einer anderen Lösung und einem anderen Rest. Du kannst das Ergebnis selbst sehen, auf jedem Gerät, das du hast, in der heutigen Höhle.