Wie zeichne und speichere ich eine Zelda-ähnliche Karte in einer benutzerdefinierten Spiel-Engine?

8

Ich habe eine Schulaufgabe, bei der ich eine 2D-Karte aus einem der ersten Zelda-Spiele zusammen mit einigen animierten Sprites zeichnen muss. Jetzt habe ich die Sprites zum Laufen gebracht. Ich habe Probleme beim Zeichnen der Karte. So sollte es aussehen:

Geben Sie hier die Bildbeschreibung ein

Dies ist das TileSet, mit dem wir die Karte zeichnen müssen (jede Kachel hat eine Größe von 32 x 32 Pixel):

Geben Sie hier die Bildbeschreibung ein

Wir arbeiten in einer benutzerdefinierten Spiel-Engine, die unser Lehrer für uns erstellt hat.

Um eine Bitmap zu zeichnen, verwenden wir diesen Befehl: GAME_ENGINE.DrawBitmap ([Name], x.cord (auf der Leinwand), y.cord (auf der Leinwand), x.cord (auf dem Tileset), y.cord (auf der Leinwand) Kachelsatz), Anzahl der Pixel, die Sie aus dem Kachelsatz ausschneiden möchten (Breite), Anzahl der Pixel, die Sie aus dem Kachelsatz ausschneiden möchten (Höhe).

Wenn ich diesen Befehl verwende, würde ich die Treppenkachel (oben links) auf die Leinwand zeichnen: GAME_ENGINE.DrawBitmap ([Name], 200, 200, 0, 0, 32, 32);

Also fand ich heraus, dass man mit einer verschachtelten for-Schleife eine 2D-Karte auf die Leinwand zeichnen kann. Dies ist der Code, den ich dafür verwendet habe:

for (int y = 0; y < 10; y++)
{
    for (int x = 0; x < 16; x++)
    {
        GAME_ENGINE.DrawBitmap(Link_Level, 128 + (x * 32), 0 + (y * 32), 0, 0, 32, 32);
    }
}

Aber offensichtlich zeichnet es dann eine 16 x 10 Karte, die nur aus einem Plättchen besteht.

Meine Frage ist: Wie kann ich dasselbe für die Schleife verwenden, aber irgendwie wissen lassen, welche Kacheln an der richtigen Position gezeichnet werden sollen?

Jede Hilfe wäre dankbar!

S. Neut
quelle
Speichern Sie jede Kachel unter einer Ganzzahl-ID, dann unter x und y. Sie könnten also eine Datei haben wie: 1,2,2 neue Zeile 4,23,44 neue Zeile 6, 55,23 usw.
Krythic
Laden und speichern Sie diese Datei dann in einer Liste / Vektor / ArrayList und fragen Sie die Liste einfach mit dem Array-Indexer ab.
Krythic
Sie können auch versuchen, eine parametrische Funktion zu erstellen, die den Zeitindex angibt. Zugegeben, das wäre unglaublich schwer mit reiner Arithmetik zu schreiben.
user64742

Antworten:

3

Hier kommt Ihre Tilemap zum Einsatz. Sie erstellen beispielsweise ein Array von Kacheln

int level[5][5] = {
    1, 1, 1, 1, 1
    1, 0, 0, 0, 1
    1, 0, 0, 0, 1
    1, 0, 0, 0, 1
    1, 1, 1, 1, 1
}

Wo 0 Gras sein könnte und 1 Wasser sein könnte:

Verwenden Sie dann Ihre Schleife:

for (int y = 0; y < 10; y++)
{
    for (int x = 0; x < 16; x++)
    {
       switch(level[y][x])
       {
       case 0: // Grass
            GAME_ENGINE.DrawBitmap(GRASS, 128 + (x * 32), 0 + (y * 32), 0, 0, 32, 32);
            break;
       case 1: // Water
            GAME_ENGINE.DrawBitmap(WATER, 128 + (x * 32), 0 + (y * 32), 0, 0, 32, 32);
            break;
       }
    }
}

David McGrane
quelle
Ich denke, Ihrem Code fehlen die tatsächlichen Koordinaten der ausgewählten Kachel.
Tyyppi_77
4
@ Tyyppi_77 Das ist richtig, aber ich kann das selbst ausfüllen :)
S. Neut
Ich mag diese Methode am meisten, um ehrlich zu sein, obwohl ich einen Fehler bei der Schalterfunktion bekomme. Ich erhalte folgende Fehlermeldung: Falsche Anzahl von Indizes in []; 2 erwartet
S. Neut
5
Die Redundanz, den Draw Call in jedem zu haben, caseist hässlich. Sie sollten mindestens reduzieren, switchum nur eine einzelne Variable zu beeinflussen, die die Kachel beschreibt (obwohl es noch bessere Alternativen gibt).
CodesInChaos
1
Ich denke, dies wäre eine bessere Antwort, wenn esvar tile = null; ... case 0: tile = GRASS; break; case 1: tile = WATER; break; ... GAME_ENGINE.DrawBitmap(tile, 128 + (x * 32), 0 + (y * 32), 0, 0, 32, 32);
Sirdank
7

Ich würde Ihnen empfehlen, Kacheldaten (in diesem Fall die ID 0für oben links und 1rechts usw.) in einem Array zu speichern . Verwenden Sie die ID, um auch zu bestimmen, welches Sprite Sie verwenden werden. Zum Beispiel:

int[,] map = new int[4, 5]{{ 0,  0,  0,  0, 0 },
                           { 0, 19, 19, 19, 0 },
                           { 0, 19, 19, 19, 0 },
                           { 0,  0,  0,  0, 0 }};

for (int y = 0; y < 4; y++) {
    for (int x = 0; x < 5; x++) {
        GAME_ENGINE.DrawBitmap(Link_Level, 128 + (x * 32), 0 + (y * 32), (map[y, x] % 12) * 32, (map[y, x] / 12) * 32, 32, 32);
    }
}

12kommt davon, wie viele Kacheln Sie horizontal in Ihrem Kachelsatz haben. Der obige Code sollte eine 5x4-Karte der Treppen um das Wasser zeichnen.

EDIT: Allerdings bin ich mir nicht sicher, wie der Name "Link_Level" lautet.

Greffin28
quelle
Link_Level bezieht sich auf die Variable, die die Bitmap aus dem Assets-Ordner des Projekts lädt. Zuerst erstellen Sie die Variable mit diesem Befehl: private Bitmap [Variablenname] = null. Dann laden Sie es mit folgendem Befehl: [Variablenname] = neue Bitmap ("[Name der PNG-Datei"); Danke übrigens für die schnelle Reaktion!
S. Neut
@ S.Neut Ah lol okay, das macht jetzt Sinn. Und du bist willkommen: D
Greffin28
3

Eine kleine Ergänzung zu den Antworten: Wenn Sie sehr große Karten haben und nicht mehr als 256 Arten von Kacheln pro Karte (einschließlich der leeren Kachel), sparen Sie möglicherweise viel RAM, das Sie byteanstelle des intArrays verwenden können Art.

Also statt

int[,] map = new int[6400, 8000]

du würdest verwenden

byte[,] map = new byte[6400, 8000]

Wenn Sie jedoch über so große Karten verfügen, können Sie auch versuchen, Kartenblöcke anstelle der gesamten Karte in den Speicher zu laden. Noch ein Hinweis: Chunked Loading funktioniert möglicherweise nicht für Sie, wenn auf der gesamten Karte ständig Dinge vor sich gehen und Sie die ganze Zeit über alles verarbeiten müssen. Aber das ist ein ganz anderes Thema, das beim Entwerfen eines gekachelten Kartenspiels berücksichtigt werden muss.


Beim Zeichnen großer Karten.

Es kann vorkommen, dass Ihre Framerate sinkt. Dies liegt wahrscheinlich daran, dass Sie eine ziemlich große Karte haben und diese vollständig zeichnen, wobei große Teile davon außerhalb des Bildschirms angezeigt werden, aber dennoch Ihre Hardwareressourcen verbrauchen. Um dies zu vermeiden, berechnen Sie das Sichtbarkeitsrechteck und zeichnen Sie nur die Kacheln, deren Indizes den Innenseiten dieses Rechtecks ​​entsprechen.

user1306322
quelle
2

Dies hängt davon ab, wie flexibel das System sein muss.

Die meisten Spiele speichern die Kartendaten in einer externen Datendatei. Dies kann einen bereits vorhandenen Ebeneneditor und dessen Ebenendateiformat (gekachelt) oder einen benutzerdefinierten Editor (kann nur ein Texteditor sein) mit einem benutzerdefinierten Format verwenden, sei es das Format XML, JSON oder ein benutzerdefiniertes Format.

Zum Laden der Kartendaten lesen Sie die Datei durch und speichern den Inhalt der Datei in einem 2D-Array, auf das Sie dann zugreifen können.

Ein einfacherer Weg für ein kleines Projekt wäre, das 2D-Array der Karte einfach auf den Quellcode des Spiels fest zu codieren.

Sobald Sie Ihre Kartendaten in einem 2D-Array haben, müssen Sie nur noch den Kacheltyp auf der Kachel suchen, die Sie gerade rendern.

for (int y = 0; y < 10; y++)
{
    for (int x = 0; x < 16; x++)
    {
        int xTile = MapData[y][x].x * 32;
        int yTile = MapData[y][x].y * 32;
        GAME_ENGINE.DrawBitmap(Link_Level, 128 + (x * 32), 0 + (y * 32), xTile , yTile , 32, 32);
    }
}

Hier MapData ist ein 2D-Array, das aus einem Objekt mit x- und y-Elementen besteht, die den zweidimensionalen "Index" der Kachel im Kachelsatz definieren. Dies bedeutet, dass Sie eine Kachel als fünfte Kachel in der zweiten Zeile für ein Beispiel definieren würden.

Obwohl dieser Ansatz recht einfach ist, kann er etwas langweilig oder sich wiederholend sein. Vielleicht möchten Sie die Kacheltypen in einer Klasse definieren (oder die Kacheln einfach als Ganzzahlen bezeichnen und anhand dieser die richtige Kachelposition berechnen). Eine einfache Dienstprogrammklasse mit Feldern, static global Vec2 StairCase = new Vec2(0, 0);die das Definieren der Karte erleichtern sollen.

Zusammenfassend sollte für ein kleines Projekt wie dieses ein 2D-Array, das das Aussehen der Karte definiert, in Ordnung sein. Wenn Sie das Spiel später erweitern möchten, würde ich die Verwendung einer externen Datendatei empfehlen.

Tyyppi_77
quelle