Spieleentwicklung für NES in C. Kapitel 17-21. Eigenes Spiel

Ursprünglicher Autor: Nesdoug
  • Übersetzung
  • Tutorial

In diesem Teil werden wir alles zusammenfügen und einen einfachen Scrolling-Shooter zu einem Weltraumthema erstellen: Das Schiff fliegt und der Laser schießt auf Feinde


<<< zurück weiter >>>


Bild
Quelle


Planung


Wir müssen solche Spielmodi implementieren:


  • Bildschirmschoner
  • Spielemodus
  • Pause-Modus
  • Bildschirm abspielen
  • Bosskampf
  • Sieg Bildschirm

Der Code muss folgendermaßen organisiert sein:


  1. Initialisierung
  2. Begrüßungsbildschirm
  3. Animierter Bildschirmschoner:
    • Wartestartknopf
    • Hintergrundmusik
  4. Wiedergabe des Spielbildschirms
  5. Spielzyklus:
    • Joystick-Ereignis abrufen
    • Schiffsbewegung
    • Das Auftreten von Feinden
    • Die Bewegung von Feinden und Muscheln
    • Kollisionsbehandlung
    • Hintergrundmusik
  6. Wenn das Leben endet, dann zeige den Bildschirm mit dem Verlust
    • Wieder Musik
    • Zurück zum Startbildschirm
  7. Wenn das Spiel vorbei ist, gehe mit dem Boss in den Kampf
  8. Nach dem Sieg:
    • Siegbildschirm anzeigen
    • Und schalten Sie die entsprechende Musik wieder ein

Und trotzdem ist es notwendig, alle notwendigen Grafiken zu zeichnen und Musik zu schreiben.


Beginnen wir mit dem Begrüßungsbildschirm und erstellen dann einen Spielebildschirm. Verwenden Sie Sprite 0, um Leben und Punkte anzuzeigen. Der Bildlauf erfolgt vertikal. Hier ist das Layout:


Bild


Einige der Texte werden auch von Sprites implementiert, zum Beispiel "Pause" und "Spielende". Dies wird die Entwicklung vereinfachen.


Einen Code schreiben


Der erste logisch verbundene Teil des Spiels ist ein Bildschirmschoner, ein Spielbildschirm und ein Pausenbildschirm. Der Übergang zwischen ihnen befindet sich auf der Schaltfläche Start.


Der Bildschirmschoner ist in Photoshop am einfachsten zu zeichnen. Arial Black macht im Namen des Spiels eine gute Figur, besonders nach ein wenig Perspektive und ein paar Filtern. Komprimieren Sie bis zu 128 Pixel in 4 Farben und übertragen Sie sie auf YY-CHR.


Bild


Ein Schiff und Sterne für den Hintergrund können sofort in YY-CHR erstellt werden. Wir skizzieren die Sterne nach dem Zufallsprinzip und das NES Screen Tool packt sie perfekt in die RLE .h-Datei. Der Text wird in der Namenstabelle gespeichert.


Bild


Sie können sofort einen Siegesbildschirm erstellen, indem Sie ihn vorübergehend auf Auswählen setzen. Ich importiere auch Musik in dieser Phase, Effekte können mit Pfeilen umgeschaltet werden. Dann lösche ich alles. Im Allgemeinen ist es richtiger, ganz am Ende Musik hinzuzufügen - derselbe Track beginnt beim Debuggen sehr schnell zu nerven.


Jetzt können Sie das Schiff und die Logik seiner Bewegung mit Pfeilen umsetzen. Nach dem Loslassen des Knopfes bewegt er sich einige Frames lang mit leichter Trägheit weiter - ein leichter Hinweis auf die Bewegungsgesetze im Raum. Im Prinzip kann dies gemäß den Testergebnissen deaktiviert werden.


Als nächstes folgt der Punktezähler, der bei jedem Frame aktualisiert wird. Es wird in einer anderen Namenstabelle vorgenommen, deren Ersetzung in der Mitte des Frames durch ein Null-Sprite implementiert wird. Diese Technik wird in einem der Artikel ausführlich beschrieben .


Hintergrund mit Sternen
Vert_scroll2 = ((Vert_scroll & 0xF8) << 2);
Sprite_Zero(); // ждем коллизии нулевого спрайта
PPU_ADDRESS = 0;
SCROLL = Vert_scroll;
SCROLL = 0;
PPU_ADDRESS = Vert_scroll2;

Hier traten Schwierigkeiten auf: Nach wenigen Sekunden löste sich alles auf. Im FCEUX-Debugger können Sie Haltepunkte für das Schreiben in Register festlegen und diese in Bildlaufsteuerungsregistern ablegen - $ 2000, $ 2005, $ 2006. Die Werte wurden korrekt aufgezeichnet, aber das Einstellen der oberen Koordinate des Bildschirms sollte in V-Blank erfolgen, aber in Wirklichkeit stellte sich heraus, dass dies in der 40. Zeile der Fall war. Dies geschah aufgrund von Musik, deren Prozeduren nicht in V-Blank passten. Sie wurden ganz am Ende der Warteschlange neu angeordnet, und alles begann gut zu funktionieren.


Jetzt können Sie verschiedene Spielmodi ausführen.


main ()
void main (void){
  while (1) { // бесконечный цикл
    while (GameMode == TITLE_MODE){ 
    // Заставка
    }
    while (GameMode == RUN_GAME_MODE){ 
    // Игра
    }
    while (GameMode == PAUSE_MODE){ 
    // Пауза
    }
    while (GameMode == GAME_OVER_MODE){ 
    // Конец игры
    }
    while (GameMode == VICTORY_MODE){ 
    // Победа
    }
  }
}

Sprite-Objekte lassen sich am besten durch Strukturen implementieren:


struct ENEMY
struct ENEMY {
unsigned char anime; // номер спрайта
unsigned char dir; // направление - если 0, то отзеркаливаем влево
unsigned char Y; // верх
unsigned char X; // левый край
unsigned char delay; // задержка начала движения
unsigned char type; // тип объекта
unsigned char move; // куда его двигать
unsigned char count; // насколько уже переместился
};

Die Feinde rennen in Wellen, daher wird der Typ festgelegt, bevor er beginnt.
Dropbox
Github


Separate Struktur für Muscheln:


struct BULLET
struct BULLET {
unsigned char Y; // y = 0 - объект за экраном, и не отображается
unsigned char Y_sub;
unsigned char tile;
unsigned char attrib;
unsigned char X;
unsigned char X_sub;
unsigned char Y_speed; // в старшем полубайте скорость, в младшем - ускорение
unsigned char X_speed;
};

Sprites müssen auf eine etwas andere Art und Weise implementiert werden - rendern Sie sie dynamisch für jeden Frame. Die Sprites befinden sich im OAM-Puffer, die Adressen im Speicher sind $ 200- $ 2FF, und zuerst befinden sie sich hinter dem Bildschirm - die vertikale Koordinate ist größer als 0xF0. Dann ziehe ich ein Null-Sprite und füge danach jedes aktive Sprite in den Puffer ein. Die Reihenfolge, in der Sprites im Puffer abgelegt werden, ändert sich bei jedem Frame, sodass die Sprites flackern.


Größere Metasprites müssen im NES Screen Tool vorbereitet werden. Der Code aus dem Shiru-Beispiel gefiel mir nicht, ich musste ihn umschreiben. Insbesondere wenn das Metasprit über den Bildschirmrand hinausgeht, wird es nicht von der gegenüberliegenden Seite angezeigt, sondern geht einfach aus dem Blickfeld verloren. Dies passt nicht zur Logik des Spiels, obwohl es schneller funktioniert. Darüber hinaus gab es Schwierigkeiten bei der Reflexion von Sprites. Ich musste ein Skript in Python schreiben, das die fertigen Metasprites in ein Format konvertiert, das sich für den Import in Code eignet. Dies beschleunigte den Prozess leicht.


Zu diesem Zeitpunkt fügt die Schaltfläche Nach unten Muscheln hinzu, Auswählen - fügt Feinde hinzu. Für einige von ihnen funktioniert das Spiegeln. Die Kollisionsbehandlung wurde in Assembler umgeschrieben und ermöglicht es Ihnen, die Anzahl der Objekte zu erhöhen.


Bild


Dropbox
Github


Und jetzt müssen wir alles neu schreiben. Im NMI-Handler werden keine Sprite-Verarbeitung und keine Bildlaufsteuerung ausgeführt. Jeder Frame führt folgende Aktionen aus:


  1. Joystick-Ereignisse abrufen
  2. Sprites aus dem Puffer werden aus dem Bildschirm entfernt, ein Null-Sprite wird platziert
  3. Wenn Master_Delay in diesem Frame auf Null gesetzt ist, wird eine Welle von Feinden gestartet. Memcpy wird zum Kopieren von ROM nach RAM verwendet.
  4. Sind alle Feinde getötet? Bei Tod wird die Y-Koordinate zurückgesetzt
  5. Gibt es irgendwelche Treffer auf unserem Schiff? Wenn ja, wird der Lebenspunktestand verringert und gemäß dem entsprechenden Timer wird für mehrere Frames eine Explosion um das Schiff gezogen.
  6. Wenn Pfeile gedrückt wurden, bewegen Sie das Schiff
  7. Gibt es Treffer auf Feinde? Wenn ja, zeichnen Sie eine Explosion und setzen Sie sie auf Y zurück
  8. Rendern Sie alle Sprites - schreiben Sie ihre Daten in OAM
  9. Wir machen Musik. Dieser Schritt kann auf NMI migriert werden.
  10. Punktezähler aktualisieren
  11. Wechseln Sie ggf. über die Schaltfläche Start in den Pausenmodus
  12. Wenn der Lebenspunktestand negativ ist, beende das Spiel

Wir müssen einen Weg finden, um feindliche Schiffe am Anfang jeder Welle zu platzieren. Sie erscheinen auch nicht gleichzeitig. Jeder Feind ist aktiv und bewegt sich, bis er getroffen wird oder bis er den Bildschirm verlässt. Wenn alle Gegner von der Welle auf die eine oder andere Weise verschwinden, wird der Master_Delay-Timer aktiviert und zählt die Frames bis zur nächsten Welle. Wenn die Wellen enden, beginnt der Boss-Modus.


Der Boss hat eine Liste möglicher Züge und Trefferpunkte, die beim Treffer entfernt werden. Wenn sie zurückgesetzt werden, werden einfache Soundeffekte und Bildschirmschütteln aktiviert. Danach geht das Spiel in den Victory-Modus. Daraus können Sie mit der Erhaltung von Leben und Punkten von vorne beginnen.


Ich schlage vor, den Rest im Code des Spiels zu sehen. Verwenden Sie es so wie es ist oder als Basis für Ihr Projekt. Vielen Dank für Ihre Aufmerksamkeit!


Bild
Dropbox
Github


Danksagung


Ich möchte mich bei allen bedanken, die mir geholfen haben, das Programmieren für NES zu lernen, insbesondere bei den Teilnehmern des Forums forum.nesdev.com .


Ich habe viel aus den von Shiru geschriebenen cc65-Codebeispielen gelernt. Einige dieser Beispiele werden in diesem Lernprogramm verwendet. Er ist Autor von Famitone2 und NES Screen Tool. Seine Seite mit Spielen und Beispielen: eins und zwei .


Zwei seiner Spiele sind auf GreetingCarts (Retroscribe) erhältlich (die Seite ist tot - ca. übersetzt)


Ich möchte THEFOX für seine Hilfe danken, als ich gerade anfing, CC65 zu lernen. Und für die Beispiele, die zuvor auf seiner Website waren .


Aber Sie können immer noch sein Spiel spielen, Streemerz.


Rainwarrior machte eine Demo von Coltrane und gab ein gutes Beispiel für die Arbeit mit Sound


Er hat auch ein Eidechsen-Spiel .


Danke an alle!


Und jetzt muss ich dasselbe tun, aber für die SNES ...


Jetzt auch beliebt: