Spieler mittig halten

Hi,
ich programmiere gerade ein kleines 2D Spiel, bei dem der Spieler immer im Mittelpunkt gehalten werden soll. Die „Welt“ soll im Prinzip unendlich groß sein. Ich habe zwar eine Idee wie ich das machen könnte, allerdings bin ich mir nicht sicher ob die auch schön ist.
Zuerst habe ich einfach den Spieler nicht versetzt, sondern allen anderen Objekten die Spielergeschwindigkeit hinzugefügt, sodass diese einfach nur schneller wurden. Funktioniert auch gut, allerdings finde ich das nicht wirklich schön. Ich habe zwar im moment an anderen Objekten sowieso nur die Gegner, aber wenn ich noch ein paar hinzufügen würde wäre das denke ich etwas umständlich.
Ich könnte natürlich auch eine große Welt machen und diese dann auf dem JPanel verschieben, allerdings wäre die Welt dann ja nicht unendlich groß, was bei einem so simplen Spiel aber sicher möglich ist.

Wäre schön wenn ihr mir ein paar Tipps geben könntet. :wink:

Also z.B. bei WomenInvasion mache ich es einfach so:
Ich nehme mir die Position des Spielers (also x und y Wert) und zentriere die Kamera um ihn. Das bedeutet genau, dass ich nur alle Sachen zeichne, die im Rechteck [mit x = x-Wert Spieler - Hälfte der Bildschirmbreite; y = y-Wert Spieler - Hälfte der Bildschirmhöhe; Breite: Bildschirmbreite; Höhe: Bildschirmhöhe] sind.

Bei deinem Spiel ist die Karte aber auch begrenzt oder? Wenn es unbegrenzt sein soll habe ich ja keine wirklichen Koordinaten außer die Position auf dem sichtbaren Bereich/Screen, die bei dem Spieler aber ja immer gleich bleiben soll.

Hallo Bene,

Wie hast du dir den vorgestellt, deine unendlich große Welt umzusetzten - ohne Koordinaten?
Du mußt ja schließlich ein Bild zeichnen lassen und ohne x/y-Koordinaten für die Zeichenmethoden wirst du dir da etwas schwer tun.

MfG
hansmueller

Also für “unendliche” Welten kann man die Hintergrundgrafik in Tiles aufteilen um dann einzelne ausgewählte bzw. errechnete daraus in einer Art Kugel-Mapping in jene Map zu zeichnen, die letztendlich auf dem Bildschirm landet. Am schönsten wird das Ganze dann, wenn die Pixel des linken Randes der Gesamt-HG-Grafik gleich den Pixeln des rechten und jene des oberen gleich jenen des unteren sind. So entsteht eine nahtlose Welt. Will man den Spieler mittig halten, kommt man um Apos Formel aber nicht drum rum.

Ok, ich guck mal ob ich das hinkrieg. Danke für die Tipps :wink:

So wie Apo hab ich es bisher auch immer gemacht. Zum Thema „unendliche Koordinaten“: Du hast einfach einen Startpunkt (Nullpunkt), alles nach „rechts“/oben ist positiv, alles nach „link“/unten ist negativ. So wie in einem echten Koordinatensystem. Müssen ja nicht nur positive Koordinaten sein. „Unendlich“ wirds ja schon aufgrund der Speicherkapazitäten von Variablen nicht werden, aber ich bin mir sicher, dass du schon mit dem primitiven Datentyp int so große Welten erschaffst, dass das für den Anfang genügen sollte. :slight_smile: Und dann gibts ja noch long und mehr.

So viel braucht man gar nicht, ausserdem sind die Anzahl der Pixel pro Bild eh auf Integer.MAX_VALUE begrenzt. Was man aber machen kann, ist, wie gesagt, seine Weltkarte(n) in Tiles aufteilen, welche man nun bereichsweise in den sichtbaren Bereich (Playfield) kopiert. Überschreitet das Playfield dabei den rechten/unteren Kartenrand, werden ganz einfach Tiles des linken/oberen Kartenrandes in das Playfield gezeichnet. Wenn die gegenüberliegenden Kanten dabei relativ übereinstimmen, entsteht sogar ein nahtloser Übergang (Mustertapete). Das Playfield sollte dabei natürlich um einiges kleiner sein, als die Gesamtgröße aller Weltkarte(n). Mal eine Skizze zur Veranschaulichung (achtet auf die Nummerierung der Felder ;)):

@Gonzo
Das Problem ist nur dass ich mit meiner Lösung, indem ich einfach den Gegnern die Spielergeschwindigkeit übergeben habe, ja funktioniert hat, und die Welt im Prinzip unendlich war. (Bzw. machte es den Eindruck sie sei unendlich, auch wenn sie eigentlich nur so groß war wie das sichtbare Bild und der Spieler eigentlich still stand)

Ich versuche erstmal Spacerats Lösung umzusetzen, scheint mir auch keine schlechte Übung zu sein. Danke für die Skizze. :wink:

Aber wenn man das mit der “Kamera” macht:
Heisst das ich speichere die Welt immer in einem Bild und zeichne nur einen bestimmten bereich oder wie? O.O

Nein, du hälst die Welt in deinem eigenem Datenmodell und zeichnest nur den Bereich den der Spieler sieht.
Jeweils die komplette Welt als Bild zu rendern wird irgendwann einfach zu teuer.

Gruß

Und wie könnte so ein Datenmodell aussehen?

Im allgemeinen eine Zuordnung von Objekten und Koordinaten zu Grafiken.
Da gibt es ziemlich viele verschiedene Ansätze, relativ einfach wäre z.b. eine Sprite-Klasse, die 2 Koordinaten (x-y) und ein Bild kennt.
Falls die Koordinaten beim rendern im Sichtbaren bereich der Kamera liegen (Rechteck-Kollisionscheck), wird das Bild an der relativen Position gezeichnet (Umrechnungsbeispiel war oben im Thread).

Das ganze geht aber beliebig komplex.
Meistens versucht man sowenig Ressourcen wie möglich zu laden (z.B. Bilder für eine Animation nur einmal pro Programm und nicht einmal pro Sprite laden).

Gruß

Zunächst erstmal eine Formel:
index = y * breite + x
Diese Formel braucht man öfters, je nach Größe und Komplexität der Welt. Mit dieser kann man nämlich die einzelnen Pixel eines Tiles, die einzelnen Tiles eines BufferedImage sowie die in CellArrays (sinngemäß auch Collections) definierten einzelnen BIs (bzw deren Lage im File-/Archivsystem) selektieren. Bei Pixeln und BI-Tiles geschieht das schon im BI selbst, wieviel Aufwand man nach oben hin über das CellArray hinaus betreibt, bleibt jedem selbst überlassen, man kann es unendlich weit schachteln. Wichtig ist, dass man immer 9 Cells gleichzeitig im Speicher haben sollte, von denen stets die mittlere gerendert wird. Deswegen sollte man die Größe der BIs auch nicht zu groß wählen (theoretisch sind da 65535 * 65535 Pixel pro BI möglich, 1024 * 1024 Pixel Gang und Gebe). Letztendlich definiert man nur Breite und Höhe der einzelnen Bereiche in Pixeln und übergibt eine Position (z.B. oben links) sowie Höhe und Breite des sichtbaren Playfields an diese Datenstruktur. Die Datenstruktur kleistert dann im laufenden Thread die geladenen Cells zusammen auf das Playfield und lädt bzw verwirft bald bzw nicht mehr sichtbare Cells in einem anderen. Letzteres ist der gravierende Unterschied zu einem Sprite-Datenmodell, denn ein Sprite wird meistens für fliessende, dauerhaft sichtbare Bewegungen verwendet und muss deswegen immer komplett im Speicher gehalten werden, solange es sichtbar ist.

Ok… diese Exkursion ist hoffentlich einigermaßen verständlich. Ein KSKB dazu möchte ich mir erstmal sparen, dass ist nämlich um einiges Aufwendiger als die entsprechende Erklärung dazu. Evtl. tut es aber auch eine Skizze der Cell-Ebenen

    |_Cells
        |_Images
            |_Tiles
                |_Pixel```wobei alles unterhalb der Images bereits in BufferedImage implementiert ist und man sich darüber schon mal keine Gedanken mehr machen muss.