Versuchen wir ein Projekt, um JSL zu verwalten
Vor langer Zeit habe ich vorgeschlagen, einen Side-Scroller zu schreiben https://community.jmp.com/t5/Uncharted/Wild-Road-Map/bc-p/20996/highlight/true#M5 . Wenn Sie in den 80ern keine Spiele gespielt haben, https://en.wikipedia.org/wiki/Parallax_scrolling hat einige Beispiele. Side-Scroller sind 2D-Spiele, bei denen sich ein Charakter von links nach rechts bewegt und der Hintergrund mehrere Ebenen haben kann, die mit unterschiedlicher Geschwindigkeit gleiten, um ein Gefühl von 3D-Tiefe zu vermitteln. Andere Variationen könnten auch Plattform-Jumper mit einer vertikalen Scroll-Dimension sein.
Es gibt Probleme, die gelöst werden müssen:
- Spieleentwicklung (kann JMP das Schreiben vereinfachen?)
- Spielgrafiken (wie sieht es aus?)
- Spiel-Audio (hast du das gehört?)
- Spieleingabe (wie bewege ich mich?)
- Spielverlauf (passiert etwas?)
1. Spieleentwicklung
Ich verwende ein JMP-Projekt (und bin erst kürzlich auf ein eigenständiges JMP 16-Projekt umgestiegen). Das eigenständige Projekt gefällt mir langsam im Vergleich zu einem Projekt mit fünf bis zehn unabhängigen Dateien. Es verfügt über Registerkarten für die geöffneten Dateien und Andockpunkte für das Protokollfenster. Das in sich geschlossene Projekt speichert alles in einer Datei (zip, unter der Decke) und entpackt und packt sich beim Öffnen und Schließen automatisch wieder.
Es war einfacher, dieses Spiel zum Laufen zu bringen, da die Grafikdatei, die Audiodatei und die Spieldatei separate, relativ kleine Dateien sind. Dadurch ist es einfacher, Dinge zu finden, wie zum Beispiel die JSL, die das Gameplay zwischen den Spielcharakteren verwaltet. Das Projekt zeigt die Dateien in Registerkarten an, etwa so:
A JMP project with several files.A JMP project with several files.A JMP project with several files.A JMP project with several files.A JMP project with several files.Ein JMP-Projekt mit mehreren Dateien.
Sobald dieses Projekt geöffnet wird, sind neun Dateien geöffnet. Die Datei, die die Klassendefinition für Vampire im Spiel enthält, wird derzeit angezeigt. Ich bin noch nicht ganz zufrieden damit, wann die Dateien wieder im Projekt gespeichert werden und wann das Projekt gespeichert wird. Deshalb verwende ich die hervorgehobenen Symbole oben links, bevor ich das Projekt schließe. Ich habe auch das Protokoll unten links angedockt. Projekte scheinen vom Rest von JMP und voneinander isoliert zu sein, sodass sie über ein eigenes Protokoll verfügen.
Die MakeAddin-Datei ist nicht Teil des Spiels, sondern Teil der Entwicklung des Spiels. Wenn diese Registerkarte geöffnet ist und ich sie ausführe, wird das Add-In neu generiert. Was passiert, wenn ich einen anderen Tab starte? Dass JSL ausgeführt wird, was möglicherweise nicht der Beginn der Spiellogik ist. Aber ich habe das ständig aus Versehen gemacht, also habe ich es so behoben:
- Es gibt eine Hauptdatei, game.jsl, die alle anderen Dateien in einer angemessenen Reihenfolge enthält.
- Die meisten Dateien sind unabhängig und erstellen lediglich eine Klasse oder einen Namespace.
- Für die Datei „sidescroller.jsl“ müssen alle anderen Dateien geladen werden (es handelt sich um die Hauptspielschleife).
Die unabhängigen Dateien alle Ende mit
try(include("game.jsl"),if(!contains(char(exception_msg),"includes itself through a closed path"),show(exception_msg)));Dadurch wird die Datei game.jsl nach der aktuellen Datei ausgeführt. Die Datei game.jsl versucht, die aktuelle Datei erneut einzubinden und löst einen rekursiven Include-Fehler aus, der ignoriert wird, oder einen anderen Fehler, der gemeldet wird. Die Side-Scroller-Datei, die die letzte sein muss, enthält diese try-Anweisung als Erste Linie. Auf diese Weise enthält die Datei „game.jsl“ alles andere, bevor der Rest der Side-Scroller-Datei ausgeführt wird.
Die Datei game.jsl sieht folgendermaßen aus
files = {"AUDIO.jsl", "RGBA.jsl", "ASSETS.jsl", "LAYERS.jsl", "VAMPIRE.jsl", "BEAR.jsl",
// currently SIDESCROLLER contains the game logic. All of the files include this file,
// at the end, except for SIDESCROLLER which include it at the start. SIDESCROLLER is
// dependent on all of the other files being loaded. The other files are not order-dependent.
"SIDESCROLLER.jsl"};
For( iFile = 1, iFile <= N Items( files ), iFile += 1,
Try(
Include( files[iFile] ), // this exception is EXPECTED if you start one of the other files; it is OK!
If( !Contains( Char( exception_msg ), "includes itself through a closed path" ),
Show( files[iFile], exception_msg )
)
)
);Jetzt kann jede bearbeitete Datei ausgeführt werden. Außer ... wenn mehr als eine Datei bearbeitet wird, achten Sie darauf, diejenigen zu speichern, die nicht explizit ausgeführt werden! Das Include ruft die Festplattenkopie ab, nicht die Kopie der Bearbeitungsregisterkarte. Wenn Sie jedoch nur eine Datei ändern, wird aufgrund der oben genannten Einbindungen die aktuelle Kopie der Bearbeitungsregisterkarte verwendet, ohne dass eine Speicherung erforderlich ist.
Wunschzettel:
- Speichern Sie alles, bevor Sie es ausführen (oder verwenden Sie die Registerkarte „Bearbeiten“ und kopieren Sie „Include“).
- Geben Sie beim Ausführen eines Projekts eine Startdatei an (nicht die On-Project-Load-Datei).
- Dokumentieren Sie, wie das Speichern einer Registerkarte und das Speichern des Projekts gleich bzw. unterschiedlich sind
- eine Möglichkeit, den Save-All-Befehl des Projekts von JSL aus auszuführen
2. Spielgrafiken
Dieses Projekt verwendet sowohl JSL-generierte Grafiken als auch heruntergeladene Bitmap-Grafiken. Unten wurden der blaue Himmel, die Berge, die Bäume und der Schotterweg von JSL geschaffen. Die Felswand ist eine gekachelte Bitmap und die Vampire, Fledermäuse und der Bär sind animierte Bitmap-Sequenzen. Credits am Ende.
The little bear is idling; the vampires are releasing bats or disappearing.The little bear is idling; the vampires are releasing bats or disappearing.The little bear is idling; the vampires are releasing bats or disappearing.The little bear is idling; the vampires are releasing bats or disappearing.The little bear is idling; the vampires are releasing bats or disappearing.Der kleine Bär ist untätig; Die Vampire lassen Fledermäuse frei oder verschwinden.
Die Datei RGBA.jsl ist ein wichtiger Bestandteil der JSL-Grafik. JMP kann mit Transparenz in einem Diagramm zeichnen und Bitmaps mit Transparenz in einem Diagramm verwenden, aber es gibt keine einfache Möglichkeit, beispielsweise ein Liniendiagramm auf einem transparenten Hintergrund zu erstellen und diese Bitmap mit dem transparenten Hintergrund zu erfassen. (Es gibt die Option „Transparenter Hintergrund für Berichts-PNG-Bilder“, die alles außer dem Diagramm mit Transparenz speichert. Sie können ein gespeichertes SVG bearbeiten, um den Hintergrund zu entfernen, aber Sie benötigen ein anderes Programm, um es wieder in ein PNG auf einem transparenten Hintergrund umzuwandeln.)
Wunschzettel
Die RGBA-Datei erstellt eine Klasse zum Erstellen von Bitmaps mit transparenten Pixeln, in denen nichts gezeichnet wird. Möglicherweise muss es für Ihre bevorzugten Grundelemente erweitert werden. Schauen Sie sich die Bäume mit nicht ganz undurchsichtigen Blättern und Lücken zwischen den Blättern an. Bezier-Baum mit Path() beschreibt das Zeichnen der Bäume, jedoch nicht mit dem Alphakanal. Die Berge sind mehrere Schichten eines Hügelzeichnungsalgorithmus mit unterschiedlichen Parametern, und der Himmel ist ein nicht transparentes, blau bis schwarz schattiertes Rechteck. Der Schotterweg lässt sich zwar recht langsam aufbauen, ist aber nach dem Laufen schnell genug; Jedes Stück Kies wird in einer Bitmap gezeichnet.
Die Datei ASSETS.jsl kennt die vom Spiel verwendeten Bitmap-Assets und weiß, wie sie geöffnet, normalisiert und benannt werden. Da die Animationen von verschiedenen Personen gezeichnet werden, müssen sie normalisiert werden, um sie auf die gleiche Weise zu zentrieren. Diese Informationen werden in der Struktur wie folgt gespeichert
Assets:vampire_images["hit"] = {{"attack/hit_1",.,.,0.80,0.5},Das Bild, das zeigt, wie der Vampir Fledermäuse loslässt, ist in der Bitmap verschoben (0,80 gegenüber 0,5 in anderen), um die besonders breite Animation zu bewältigen. Die Vampire und der Bär erhalten im Side-Scroller ihre eigenen Ebenen und ihre Transparenz lässt weiter entfernte Ebenen durchscheinen.
Da sich das Spiel tatsächlich in einer Grafik befindet, können Sie mit dem Zoom-Tool einen Blick darauf werfen, um die Ebenen zu verstehen. Hier ist es verkleinert und Sie können sehen, dass der Himmel von links nach rechts nicht größer ist als das Fenster. Es scrollt nicht. Aber die näheren und näheren Ebenen werden immer breiter, sodass sie mehr scrollen können.
Normally the sky fills the display, left-to-right. Zoomed out, see the vampire about to enter the stage.Normally the sky fills the display, left-to-right. Zoomed out, see the vampire about to enter the stage.Normally the sky fills the display, left-to-right. Zoomed out, see the vampire about to enter the stage.Normally the sky fills the display, left-to-right. Zoomed out, see the vampire about to enter the stage.Normally the sky fills the display, left-to-right. Zoomed out, see the vampire about to enter the stage.Normalerweise füllt der Himmel die Anzeige von links nach rechts aus. Herausgezoomt sieht man den Vampir, der gerade die Bühne betritt.
Während der kleine Bär nach rechts geht, werden die Positionen der Bitmaps angepasst ...
Not afraid.Not afraid.Not afraid.Not afraid.Not afraid.Nicht ängstlich.
Kurz nach der Mitte...
The NPCs animate even when off the display.The NPCs animate even when off the display.The NPCs animate even when off the display.The NPCs animate even when off the display.The NPCs animate even when off the display.Die NPCs sind auch dann animiert, wenn sie nicht im Display sind.
Und am anderen Ende...
The little bear has reached the right-hand end of his world.The little bear has reached the right-hand end of his world.The little bear has reached the right-hand end of his world.The little bear has reached the right-hand end of his world.The little bear has reached the right-hand end of his world.Der kleine Bär ist am rechten Ende seiner Welt angelangt.
Die Vampire gehen langsamer als der Bär. Sie werden aufholen. Und hier ist es mit einigen gelöschten Hintergrund- und Vordergrundbildern ...
Without all the layers, the characters seem to float.Without all the layers, the characters seem to float.Without all the layers, the characters seem to float.Without all the layers, the characters seem to float.Without all the layers, the characters seem to float.Ohne all die Schichten scheinen die Charaktere zu schweben.
3. Spiel-Audio
Klang ist ein wichtiger Teil eines immersiven Erlebnisses. Ich habe LMMS verwendet, um die Sounds in diesem Spiel zu erstellen, und ein richtiger Sounddesigner könnte das viel besser machen. Aber es ist immer noch ziemlich gut, weil sich die Klänge überlappen können, ohne sich gegenseitig zu unterbrechen. Unter Windows. Wahrscheinlich auf dem Mac, wenn jemand mit einem Mac ein Befehlszeilenprogramm findet, das einen Sound abspielen kann. Danke @Jeff_Perkinson für die Hilfe beim Mac-Audiocode. Für Windows verwendet die Datei AUDIO.jsl RunProgram, um PowerShell auszuführen, das Zugriff auf das Windows-Soundsystem hat. Auf dem Mac wird ein Befehlszeilen-Soundplayer verwendet, /usr/bin/afplay. Wenn Sie versuchen, JMP zu beenden, bevor alle Wiedergabetöne beendet sind, kann es zu einem Absturz von JMP kommen. Lassen Sie den letzten Fortschrittsbalken einfach die Wiedergabe aller gestarteten Sounds beenden.
4. Spieleingabe
Dieses Spiel verwendet die Tastatur als Eingabegerät, indem eine MouseBox um die Grafikbox platziert wird, in der sich das Spiel befindet.
New Window( "overlay",
mb = MouseBox(
V List Box(
hlistbox(
leftSpacer=spacerbox(size(5,windowy),color("black")),
gb = Graph Box(
suppressaxes,
framesize( windowx + 1, windowy + 1 ),
X Scale( -1, windowx ),
Y Scale( -1, windowy )
),Die MouseBox leitet Tastenanschläge an eine kleine Funktion weiter, die sich die Taste und die Uhrzeit merkt
lastkey=empty();
lastkeyexpire=tickseconds();
spriteRunner = function({key},{},
lastkey=key;
lastkeyexpire=tickseconds()+1;
);An anderer Stelle wird die Aktion der Taste wiederholt, bis sie eine Sekunde später veraltet ist
While( go,
if( tickseconds() > lastkeyexpire,
lastkey="";
);
…Die Schlüssel steuern den kleinen Bären. Sie sind in der Finite-State-Maschine (FSM) des Bären implementiert. Ein Beispiel, das den Bären vom Leerlauf zum Gehen ändert, ist die Funktion BEAR.jslcycle():
If( begin,
laststate=m_state;
if( keystroke=="LEFT" & ( pFaceLeft=="faceright" | (pFaceLeft=="faceleft" & pName=="idle") ),
m_state = m_stateWalkLeft;Begin ist wahr, wenn der aktuelle Animationszyklus am Anfang steht, was einen guten Ort zum Ändern von Zuständen darstellt. Es sieht seltsam aus, die Mitte einer Sprunganimation einer Leerlaufanimation zu überlassen. Es ist schon eine Weile her, dass ich diese Logik geschrieben habe, die besagt, dass der Bär sich nur dann nach links bewegen sollte, wenn er nach links schaut und sich im Leerlauf befindet, aber wenn er nach rechts schaut ... einfach anfangen, nach links zu gehen? Ich denke, das ermöglicht eine sofortige Umkehrung beim Gehen und verhindert möglicherweise einen Wechsel in denselben Zustand, der auch auf andere Weise behoben werden könnte.
5. Spielablauf
Ein fesselndes Spiel zu machen, ist für mich der schwierige Teil. Eine Geschichte erzählen, ein Ziel schaffen, die Rätsel hinzufügen. Dieses Spiel ist hauptsächlich eine Umgebung, die auf einen Geschichtenerzähler wartet. Aber ich habe ein bisschen Gameplay hinzugefügt.
,/*else if*/ keystroke=="DOWN" & m_NumRocks<6 & (m_state == m_stateWalkLeft | m_state==m_stateIdleLeft),
m_state = m_statePickLeft;
m_NumRocks = min(6,m_NumRocks+1);
// use SPACE to HIT
,/*else if*/ keystroke==" " & (m_state == m_stateWalkRight | m_state==m_stateIdleRight),
if(m_NumRocks,
m_NumRocks = max(0,m_NumRocks-1);
m_state = m_stateHitRight;
m_RockThrownRightFrom = m_x;
);Durch Drücken des Abwärtspfeils werden Steine aufgenommen. Der kleine Bär kann bis zu sechs Personen aufnehmen. Durch Drücken der Leertaste wird der Stein in die Blickrichtung geschleudert. Der Bär notiert den eingeworfenen Stein RockThrownRightFrom oder RockThrown Links Aus Dies wird durch Code in der Hauptschleife überprüft, der Vampire und den Bären sehen kann.
// check for interaction between bear and vampire
if(!ismissing(bearPlayer:m_RockThrownRightFrom),
if(bearPlayer:m_RockThrownRightFrom<GETPOS></GETPOS>
Wenn ein Vampir mit einem Stein getroffen wird, wird er um 50 Pixel zurückgeschleudert. (Eigentlich kann man zwei Vampire mit einer Klappe schlagen, so wie ich es geschrieben habe.) Die Vampire sollten wahrscheinlich auf einer Liste stehen, die mehr als zwei unterstützen könnte. Eigentlich sollten sich alle NPCs (Nicht-Spieler-Charaktere) in einer Liste befinden und von einer gemeinsamen Basisklasse abgeleitet werden, um die Aufrufe von Cycle() nicht in einer Schleife durchzuführen
vampire1:cycle(stagepos);
vampire2:cycle(stagepos);
bearPlayer:cycle(stagepos,lastkey);
aber das würde nur verschleiern, wie dieses einfache Beispiel funktioniert.
Es sollten zwei Anhänge vorhanden sein: eine Projektdatei und eine Add-In-Datei. (Ich kann das Projekt derzeit nicht an die Community anhängen, aber wenn Sie das Add-In installieren, befindet sich eine Kopie des Projekts im Add-In-Verzeichnis.)
Kunstnachweise
Die offene Game-Art-Site bietet viel mehr Inhalte unterschiedlicher Qualität.