Ist es eine schlechte Idee, die Mausposition auf dem Bildschirm zu „mappen“, damit die Kollisionserkennung unabhängig von der Auflösung funktioniert?

15

Stellen Sie sich ein Spiel mit einer Standardauflösung von 800 x 600 vor. Objekte mit Kollisionsmasken werden in eine Spielwelt der Größe 800x600 gelegt. Die Kollisionsmasken können erkennen, wann die Maus mit ihnen kollidiert.

Nehmen wir nun an, wir skalieren das Spiel auf 1024 x 768 (nehmen wir an, wir skalieren die Grafik, indem wir einfach alles auf eine Ebene rendern und dann die gesamte Ebene auf einmal skalieren). Wir haben zwei Möglichkeiten, um die Kollisionen mit der Maus in dieser neuen Auflösung richtig funktionieren zu lassen:

A.) Skalieren Sie die Welt auf 1024x768 und skalieren Sie die Kollisionsmaske jedes Objekts entsprechend.

B.) "Map" die Mausposition auf die Originalwelt (800x600).

Mit "Karte" meine ich, skaliere einfach die Mausposition auf die ursprüngliche 800x600-Welt. Wenn die Mausposition auf dem Bildschirm beispielsweise (1024, 768) ist, ist die Mausposition in der Welt (800, 600).

Offensichtlich erfordert Option B einen Weg weniger Berechnung und ist wahrscheinlich weniger anfällig für geometrische Fehler, aber es fühlt sich auch Art „hackish“ zu mir, wie es unvorhergesehenen Folgen mit dieser Methode geht die Hölle sein wird , zu beheben später.

Mit welcher Methode soll ich gehen: A, B oder etwas anderes?

user3002473
quelle
2
Wie von Classic Thunder hervorgehoben, sollten Anzeigekoordinaten nicht mit Weltkoordinaten oder Objektkoordinaten übereinstimmen. Normalerweise möchten Sie Transformationen haben (im Grunde, wie Sie sagen, Zuordnungen , aber normalerweise mit Matrixmathematik).
Dan

Antworten:

38

In der Regel (auch für 2D-Spiele) gibt es ein separates Koordinatensystem für das Spiel, das von der Auflösung unabhängig ist. Dies wird normalerweise als Weltraum bezeichnet. Auf diese Weise können Sie auf eine beliebige Auflösung skalieren.

Der einfachste Weg, dies zu erreichen, ist die Verwendung einer 2D-Kamera. Hierbei handelt es sich im Wesentlichen um eine Matrix, die den Übergang vom Weltraum (Ihre willkürlichen Einheiten) zum Bildschirmraum (die Pixel auf dem Bildschirm) definiert. Dies macht den Umgang mit der Mathematik trivial.

Werfen Sie einen Blick auf den folgenden Abschnitt in XNA 2d Camera Scrolling - warum sollten Sie die Matrixtransformation verwenden?

Es macht es wirklich einfach, zwischen Koordinatensystemdefinitionen zu konvertieren

Verwenden Sie einfach Vector2.Transform, um vom Bildschirm in den Weltraum zu gelangen. Dies wird häufig verwendet, um die Position der Maus in der Welt für die Objektauswahl zu ermitteln.

Vector2.Transform(mouseLocation, Matrix.Invert(Camera.TransformMatrix));

Um von der Welt in den Bildschirmraum zu gelangen, machen Sie einfach das Gegenteil.

Vector2.Transform(mouseLocation, Camera.TransformMatrix);

Es gibt keine Nachteile bei der Verwendung einer Matrix, außer dass ein wenig Lernen erforderlich ist.

ClassicThunder
quelle
... und in der Tat ist moderne Hardware im Allgemeinen für Matrixoperationen durch Vektorisierungsarchitekturen wie SSE, NEON usw. ausgelegt. Daher ist dies die clevere Wahl - Sie sparen CPU-Zyklen gegenüber nicht vektorisierten Ansätzen.
Ingenieur
1
Milder Haftungsausschluss: XNA wurde von Microsoft eingestellt, aber Monogame verwendet dieselbe API.
Pharap
1
Sicher, es ist klug, Matrizen zu verwenden, um zwischen den beiden Koordinatensystemen zu übersetzen. Aber wie beantwortet das die Frage? Sie müssen sich noch entscheiden, ob Sie von System A auf System B oder von B auf A abbilden möchten und welche Konsequenzen dies hat, falls es welche gibt.
Mastov
"Ist es eine schlechte Idee, die Mausposition auf dem Bildschirm zu" mappen ", damit die Kollisionserkennung unabhängig von der Auflösung funktioniert?" wird beantwortet mit "es ist klug, Matrizen zu verwenden, um zwischen den beiden Koordinatensystemen zu übersetzen" ...
ClassicThunder
Ich antworte auch: "Sie müssen sich noch entscheiden, ob Sie von System A zu System B oder von B zu A abbilden möchten und welche Konsequenzen dies hat, wenn es welche gibt." indem Sie sagen, Sie sollten eine beliebige verwenden, die nicht an die Auflösung und Skalierung gebunden ist. Also C -> A oder C -> B, je nachdem ob A oder B die Auflösung ist. Natürlich kann C einer Basisauflösung entsprechen, für die Sie eine Skalierung vornehmen. Punkt ist alles Mathe passiert im selben Koordinatensystem und Sie skalieren nur für das Rendering.
ClassicThunder
2

Eine weitere Option wäre: Bewegen Sie bei jedem eingegebenen Mausbewegungsereignis den Mauszeiger im Spiel um die Anzahl der Spielpixel, die der Anzahl der Pixel im Mausereignis entspricht. Dies ist natürlich bei 3D-Spielen der Fall, bei denen der echte Mauszeiger in der Mitte fixiert und die Zielrichtung um einen Betrag gedreht wird, der der eingegebenen Mausbewegung entspricht. Sie können dies jedoch auf die gleiche Weise tun, indem Sie ein Sprite bewegen, das den Mauszeiger im Spiel darstellt.

Sie müssen natürlich alle Mausbewegungsereignisse ignorieren, die durch das Verziehen der Maus in die Mitte verursacht werden. Wenn Ihr Spiel überhaupt eine Verzögerung bei der Eingabe aufweist, ist die Unempfindlichkeit des Cursors der störendste und auffälligste Aspekt.

Welche Lösung Sie verwenden, hängt zum Teil davon ab, wie wichtig die Mausposition für das Gameplay ist. Wenn dies ein RTS ist und der Spieler einfach auf Einheiten klickt, können Sie wahrscheinlich einfach entscheiden, welches von Ihren A oder B einfacher ist. Wenn es sich um einen Top-Down-Shooter handelt und die Maus die Bewegungen des Charakters direkt steuert, werden Sie wahrscheinlich eine detailliertere Lösung wünschen, die die Variabilität der Bewegungen des Charakters nicht nur aufgrund der Auflösung, sondern auch aufgrund von Dingen wie der Mausbewegung einschränkt Geschwindigkeit. Wenn die Maus stattdessen die Ausrichtung steuert, möchten Sie eine andere Lösung usw.

Random832
quelle
0

Die Antwort von ClassicThunder ist richtig, aber ich möchte ein Beispiel für ein alternatives / einfacheres Mittel zum Erreichen des gewünschten Effekts geben. Dies ist eine einfachere Lösung für Rapid Prototyping, wenn Sie keinen Zugriff auf eine voll funktionsfähige Bibliothek haben oder wenn Sie keinen Zugriff auf eine GPU haben (z. B. in eingebetteten Systemen).

Um dieses Mapping durchzuführen, können Sie die folgende Funktion verwenden (vorausgesetzt, sie wird in der statischen Klasse definiert Helper):

static float Map(float value, float fromLow, float fromHigh, float toLow, float toHigh)
{
    return ((value - fromLow) / (fromHigh - fromLow) * (toHigh - toLow)) + toLow;
}

(Sie haben keine Sprache angegeben, aber ich sehe, dass Sie etwas über C # wissen. Mein Beispiel ist also in C #.)

Sie können diese Funktion dann wie folgt verwenden:

float mouseXInWorld = Helper.Map(Mouse.X, 0, Screen.Width - 1, camera.Bounds.X, camera.Bounds.X + camera.Bounds.Width - 1);
float mouseYInWorld = Helper.Map(Mouse.Y, 0, Screen.Height - 1, camera.Bounds.Y, camera.Bounds.Y + camera.Bounds.Height - 1);

Wo camera.Boundsist ein Rechteck, das den Bereich der Welt darstellt, den die Kamera sehen kann (dh den Bereich, der auf den Bildschirm projiziert wird).

Wenn Sie eine Vectoroder Point-Klasse haben, können Sie diesen Vorgang weiter vereinfachen, indem Sie ein 2D-Äquivalent der Kartenfunktion erstellen.

static Vector Map(Vector value, Rectangle fromArea, Rectangle toArea)
{
    Vector result = new Vector();
    result.X = Map(value.X, fromArea.X, fromArea.X + fromArea.Width - 1, toArea.X, toArea.X + toArea.Width - 1);
    result.Y = Map(value.Y, fromArea.Y, fromArea.Y + fromArea.Height - 1, toArea.Y, toArea.Y + toArea.Height - 1);
    return result;
}

Was Ihren Mapping-Code zu einem einfachen Einzeiler machen würde:

Vector mousePosInWorld = Map(Mouse.Pos, Screen.Bounds, camera.Bounds);
Pharap
quelle
-1

Option (C): Ändern Sie die Bildschirmauflösung zurück auf 800x600.

Auch wenn Sie dies nicht tun, betrachten Sie es als Gedankenexperiment. In diesem Fall liegt es in der Verantwortung des Bildschirms, die Größe der Grafiken an die physische Größe anzupassen, und dann in der Verantwortung des Betriebssystems, Zeigerereignisse mit einer Auflösung von 800 x 600 zu erhalten.

Ich denke, es hängt alles davon ab, ob es sich bei Ihren Grafiken um Bitmaps oder Vektoren handelt. Wenn es sich um Bitmaps handelt und Sie in einem 800 x 600-Puffer rendern, ist es viel einfacher, die Maus dem Bildschirmbereich neu zuzuordnen und die tatsächliche Bildschirmauflösung zu ignorieren. Der große Nachteil dabei ist jedoch, dass die Hochskalierung hässlich aussehen kann, insbesondere wenn Sie blockige 8-Bit-Grafiken verwenden.

pjc50
quelle