2D Anwendungen

Top  Previous  Next

Es ist Zeit für eine neue Workshop Serie! Dieses Mal geht es um 2D Anwendungen und 2D Spiele. Es ist vielleicht schwer zu glauben, aber Spiele dieser Art sterben einfach nicht aus und einige machen sogar noch mehr Spaß als so manches 3D Spiel. Ich kann nicht für Sie sprechen, aber ich finde z.B. "Worms" in 2D viel besser als in 3D. Darüber hinaus ist es einfacher zu lernen, 2D Spiele zu erstellen und die meisten laufen auch noch auf älteren Rechnern, sie eignen sich also sehr güt für Gelegenheitsspiele.

 

Wir werden diese Serie mit zwei "ernsten" Anwendungen beginnen. Der Grund dafür ist, dass wir erst lernen müssen, mit welchen Tools wir arbeiten können und diese Anwendungen beinhalten alles, was Sie wissen müssen. Öffnen Sie zunächst randmusic.c.

 

aum80_workshop1

 

Ob Sie es glauben oder nicht, bei diesem Programm handelt es sich um einen Musikgenerator, der zufällig Soundtracks generieren kann, die für Monate laufen! Ich habe ihn auf 60 Minuten Tracks begrenzt, aber das können Sie leicht ändern. Wie er funktioniert? Ich verwende 12 Musikstücke, die gut ineinander übergeblendet werden können; man nennt dies ein "Construction kit". Diese Stücke werden zufällig aneinandergehängt und erzeugen so einen akzeptablen Soundtrack.

 

Wählen Sie die Länge des Songs (die Voreinstellung ist 1 Minute) mit Hilfe der Knöpfe ("Buttons") und drücken Sie auf "Generate!". Während der Song läuft, bewegt sich der Track Indicator nach links und zeigt erst 10, dann 20, ... und schließlich 100. Mit Hilfe der "Mute" Checkbox können Sie das Stück stumm schalten oder dies wieder aufheben. Es gibt auch einen Text, der das aktuelle Stück anzeigt und ein "Digits" Display, welches zeigt, wieviele Sekunden noch gespielt werden. All diese Elemente werden auf einem grauen "Panel" angezeigt, welches als Hintergrund fungiert.

 

Die Worte in Anführungszeichen oben beschreiben 2D Elemente, die in allen 2D Spielen genutzt werden können. Falls Sie meine früheren Workshops verfolgt haben, wissen Sie schon einiges über Text, Panel, Button und Digit Elemente. Es gibt aber auch einige 2D Elemente, die bislang noch nicht in Workshops vorkamen.

 

Ich werde den Code für die Definitionen der Variablen, Bitmaps, Sounds, etc. nicht aufschreiben, das ist trivial. Der erste interessante Teil ist die Definition des Haupt Panels.

 

PANEL* main_pan =

{

       bmap = "main.tga";

       layer = 10;

       pos_x = 0;

       pos_y = 0;

       red = 128;

       green = 128;

       blue = 128;

       button (400, 95, generate1_tga, generate1_tga, generate2_tga, generate_song, NULL, NULL);

       button_toggle (429, 249, checkbox_on_tga, checkbox_off_tga, checkbox_on_tga, NULL, mute_track, NULL, NULL);

       button_radio (68, 181, radio_on_tga, radio_off_tga, radio_off_tga, NULL, set_length, NULL);

       button_radio (68, 211, radio_on_tga, radio_off_tga, radio_off_tga, NULL, set_length, NULL);

       button_radio (68, 243, radio_on_tga, radio_off_tga, radio_off_tga, NULL, set_length, NULL);

       button_radio (68, 272, radio_on_tga, radio_off_tga, radio_off_tga, NULL, set_length, NULL);

       digits(400, 360, "Seconds left: %.1f", arial_font, 1, current_length);

       window (61, 95, 272, 28, window_tga, window_var, 0);

       flags = VISIBLE;

}

 

Dieses Panel verwendet ein Bitmap der Größe 640 x 480. Dies wird auch die Auflösung des Bildschirms sein, so dass dieses Panel den gesamten Schirm bedeckt. Die RGB Farben sind auf (128, 128, 128) gesetzt. Auf diese Weise wird aller Text und die numerischen Werte des Panels in grauer Farbe angezeigt. Sehen wir uns diese Elemente genauer an.

- Die "button" Definition erzeugt den "Generate!" Knopf. Wird dieser angeklickt, startet das die Funktion "generate_song" (mehr darüber etwas später).

- Die "button_toggle" Definition erzeugt die "Mute" Checkbox, die angewählt sein kann oder auch nicht.

- Die "button_radio" Definition erzeugt vier Auswahlknöpfe, mit denen die Länge des Songs eingestellt werden kann. Der "Song Length" Knopf ist Teil des Panels und wird nicht von den "button_radio" Definitionen erzeugt.

- Die "digits" Definition zeigt "Seconds left:" und einen numerischen Wert, in unserem Fall "current_length".

- Die "window" Definition erzeugt eine Bitmap, die folgendermaßen aussieht.

 

aum80_workshop4

 

Diese ist wie Sie sehen recht groß und nur teilweise auf dem Panel sichtbar.

 

aum80_workshop5

 

Die "window" Definition schneidet den gewünschten Teil aus dem Main Panel aus und erlaubt es der großen Bitmap nach links oder rechts zu gleiten, je nach Bedarf. Eine Window Definition kann Bitmaps auch vertikal verschieben.

 

void main()

{

       randomize();

       fps_max = 70;

       video_mode = 6; // run in 640 x 480 pixels

       video_depth = 32; // 32 bit mode

       video_screen = 2; // start in window mode

}

 

Wie Sie sehen ist die main() Funktion sehr simpel. Die Framerate wird auf 70 fps festgesetzt, die Auflösung und Farbtiefe werden festgelegt und dann wird sichergestellt, dass die Anwendung im Fenster läuft. Dies ist auch die Voreinstellung, aber es schadet nicht, es nochmal festzulegen, das kann sich als nützlich erweisen, wenn sich diese Voreinstellung in späteren 3DGS Versionen einmal ändern sollte.

 

function mouse_startup()

       mouse_mode = 2;

       mouse_map = pointer_tga;

       while (1)

       

               vec_set(mouse_pos, mouse_cursor);

               wait(1);

       }

}

 

Die Funktion mouse_startup() ist eine typische Mausfunktion, die nichts besonderes tut. Sehen wir uns lieber die Funktion an, die aufgerufen wird, wenn der "Generate!" Knopf gedrückt wird.

 

function generate_song()

{

       if (current_length > 0) {return;}

       current_length = music_length;

       var temp;

       while (1)

       {

               temp = 1 + integer(random(12)); // generate a random number in the 1...12 interval

               switch(temp)

               {

                       case 1:

                               loop_handle = snd_play (loop01_wav, 100, 0);

                               str_cpy(playing_str, "Now playing: loop01.wav");

                               break;

                       case 2:

                               loop_handle = snd_play (loop02_wav, 100, 0);

                               str_cpy(playing_str, "Now playing: loop02.wav");

                               break;

                       .......................................................

                       case 12:

                               loop_handle = snd_play (loop12_wav, 100, 0);

                               str_cpy(playing_str, "Now playing: loop12.wav");

                               break;

               }

 

Wir verwenden eine Variable namens current_length; diese speichert die Sekunden, die noch übrig sind. Falls er noch läuft (current_length > 0) möchten wir nicht, dass der Anwender einen neuen startet. Wir nutzen auch eine andere Variable namens music_length. Diese speichert die Länge des Songs in Sekunden (60, 180, 600 oder 3600). Der Wert der Variablen temp wird auf einen zufälligen Wert zwischen 1 und 12 gesetzt und ein switch / case Block spielt das entsprechende Stück. Auf diese Weise wird auch der Name des aktuellen Stücks angezeigt.

 

               while (snd_playing (loop_handle))

               {

                       current_length -= time_step / 16;

                       if (current_length < 0)

                       {

                               current_length = 0;

                               return;

                       }

                       if (music_length == 60)

                       {

                               window_var = 752 - current_length * 12.533; // 12.533 = 752 / 60

                       }

                       if (music_length == 180)

                       {

                               window_var = 752 - current_length * 4.178; // 4.178 = 752 / 180

                       }

                       if (music_length == 600)

                       {

                               window_var = 752 - current_length * 1.254; // 1.254 = 752 / 600

                       }

                       if (music_length == 3600)

                       {

                               window_var = 752 - current_length * 0.209; // 0.209 = 752 / 3600

                       }

                       wait (1);

               }

               str_cpy(playing_str, "#50");

       }

       wait (1);

}

 

Die Stücke werden gespielt, aber wir müssen sicherstellen, dass sie sich nicht überschneiden, also verwenden wir eine "while" Schleife, die wartet bis das vorige Stück vorüber ist. Gleichzeitig wird pro Sekunde 1 von current_length abgezogen, wie bei einem Countdown. Wenn der Song vorüber ist (current_length < 0) wird dieser Wert auf 0 gesetzt, weil wir keine negativen Werte auf dem Schirm anzeigen wollen und verlassen die Funktion.

 

So unglaublich es klingt, diese Schleife kontrolliert auch die "window_var", welche wenn Sie sich erinnern dafür zuständig ist, unser Fenster horizontal zu bewegen. Unsere Bitmap ist 1024 Pixel breit und in der Window Definition werden daraus 272 Pixel "ausgeschnitten". Das heißt wir können maximale 1024 - 272 = 752 Pixel verschieben. Wenn wir diesen Wert durch die Zahl der gewählten Sekunden dividieren, ergibt das die seltsamen Zahlen, mit denen wir current_length multiplizieren müssen. Schließlich setzt die "str_cpy" Zeile den "playing_str" zurück, sobald ein Stück gespielt wird.

 

Es sind nur zwei kleine Funktionen übrig, halten Sie durch!

 

function mute_track()

{

       master_vol = (master_vol == OFF) * 50; // set the volume at 50%

}

 

Die Funktion mute_track setzt die master_vol Variable auf 0 oder 50, je nach der Zahl der Klicks auf der "Mute" Checkbox.

 

function set_length(button_number)

{

       if (button_number == 3) // the first radio button was clicked?

       {

               music_length = 60; // then set the length to 60 seconds (1 minute)

       }

       if (button_number == 4) // the second radio button was clicked?

       {

               music_length = 180; // then set the length to 180 seconds (3 minutes)

       }

       if (button_number == 5) // the third radio button was clicked?

       {

               music_length = 600; // then set the length to 600 seconds (10 minutes)

       }

       if (button_number == 6) // the fourth radio button was clicked?

       {

               music_length = 3600; // then set the length to 3600 seconds (60 minutes)

       }

}

 

Die Funktion set_length erhält die Zahl des gedrückten Knopfes des Panels als Parameter. Wir haben ja mehr als nur die Auswahlknöpfe in der Definition des Panels. Ein Blick in die Definition des Panels zeigt, dass der erste dieser ist das dritte Panel Element, also ist button_number für den ersten dieser Knöpfe gleich 3 und so weiter.

 

Die Funktion setzt "music_length" einfach auf die gewünschte Länge: 1, 3, 10 oder 60 Minuten. Und da haben Sie es: eine Jukebox, die stundenlang, tagelang, monatelang oder sogar jahrelang spielen kann, ohne sich zu wiederholen. Was geschieht, wenn Sie 100 ähnliche Stücke (oder 100 total verschiedene Stücke und Soundeffekte) verwenden? Es wäre sicher interessant das Resultat zu sehen bzw. zu hören.

 

Zeit für das zweite Beispiel im Workshop; öffnen Sie den "guitartuner" Ordner und starten Sie darin guitartuner.c:

 

aum80_workshop2

 

Der Titel sagt schon alles: Ein Programm zum Spielen einer Gitarre. Klicken Sie die gewünschte Saite an und diese wird gespielt. Darüber hinaus, können Sie die Sounds nur einmal spielen oder aber in einer Schleife unterbringen, die in einem einstellbaren Intervall wiederholt wird. Klicken Sie ein paar Saiten an, um ein Gefühl für das Programm zu bekommen und stellen Sie den Slider dann auf einen von 0 verschiedenen Wert ein. Wenn Sie danach eine weitere Saite spielen, hören Sie den Sound wieder und wieder. Ach ja, die grüne 5 ist in Wahrheit ein S und steht für "Sekunden". Alles klar? Sehen wir uns den Code an:

 

PANEL* main_pan =

{

       bmap = "main.tga";

       pos_x = 0;

       pos_y = 0;

       button (31, 16, chord1on_tga, chord1off_tga, chord1over_tga, chord1, NULL, NULL);

       button (76, 16, chord2on_tga, chord2off_tga, chord2over_tga, chord2, NULL, NULL);

       button (120, 16, chord3on_tga, chord3off_tga, chord3over_tga, chord3, NULL, NULL);

       button (166, 15, chord4on_tga, chord4off_tga, chord4over_tga, chord4, NULL, NULL);

       button (210, 16, chord5on_tga, chord5off_tga, chord5over_tga, chord5, NULL, NULL);

       button (254, 16, chord6on_tga, chord6off_tga, chord6over_tga, chord6, NULL, NULL);

       hslider(316, 384, 298, slider_tga, 0, 9, slider_value);

       digits(372, 168, 1, digits_font, 1, sound_delay);

       flags = VISIBLE;

}

 

Wieder überspringe ich die Definition der Variablen und die anderen langweiligen Dinge. Das Haupt Panel enthält einige Button Definitionen. Im letzten Beispiel gab es nur einen Knopf, um das Programm zu starten, dieses Mal gibt es mehrere, einen für jede Saite, die voll ausgereizt werden: es gibt Bitmaps für den Knopf im regulären Zustand, die sich ändern, wenn der Mauszeiger auf dem Knopf steht und sich nochmal ändern, wenn der Knopf angeklickt wird. Die verwendeten Farben dafür sind graue, orange und rote Saiten.

 

aum80_workshop3

 

Ein neues 2D Element ist die "hslider" Definition, welche für den horizontalen Slider zuständig ist, mit dem die Länge des Intervalles eingestellt werden kann. Gamestudio unterstützt horizontale und vertikale Slider. Schließlich gibt es eine "digits" Anzeige, mit welcher die Sekunden zwischen zwei Akkorden angezeigt werden. Sehen wir uns den Rest des Codes an.

 

void main()

{

       fps_max = 70;

       video_mode = 6; // run in 640 x 480 pixels

       video_depth = 32; // 32 bit mode

       video_screen = 2; // start in window mode

       // the following loop makes sure that the slider inputs an integer value

       while (1)

       {

               sound_delay = integer(slider_value + 0.4); // sound_delay should be in the 0...9 range

               wait (1);

       }

}

 

Die Funktion main tut nichts Besonderes; beachten Sie, dass sound_delay, die Werte von 0 bis 9 annehmen kann, ein ganzzahliger Wert ist, was es dem Spieler leichter macht, den maximalen Wert von 9 einzustellen, ohne den Slider pixelgenau auf den rechten Rand zu schieben.

 

function mouse_startup()

       mouse_mode = 2;

       mouse_map = pointer_tga;

       while (1)

       

               vec_set(mouse_pos, mouse_cursor);

               wait(1);

       }

}

 

Die Funktion mouse_startup() ist noch dieselbe wie im vorigen Beispiel, wir können sie also ignorieren. Nur eien Funktion ist noch übrig, nämlich die, welche aufgerufen wird, wenn der Spieler die erste Saite anklickt. Die Funktionen für die anderen Saiten sind identisch, wir müssen sie also nicht extra betrachten.

 

function chord1()

{

       var temp;

       chord_changed = 1;

       wait (3); // stop all the other loops that might be running

       chord_changed = 0;

       snd_stop(chord1_handle);

       snd_stop(chord2_handle);

       snd_stop(chord3_handle);

       snd_stop(chord4_handle);

       snd_stop(chord5_handle);

       snd_stop(chord6_handle);

       if (!sound_delay) // delay = 0?

       {

               chord1_handle = snd_play(one_wav, 100, 0); // then play the chord sound only once

       }

       else // the sound will be played in a loop here

       {

               while (1)

               {

                       chord1_handle = snd_play(one_wav, 100, 0);

                       // stop playing the sound in a loop if the player sets the sound delay to zero

                       while (!sound_delay)

                       {

                               if (chord_changed) return; // get out of here if the player has clicked another chord

                               wait (1);

                       }

                       temp = 0;

                       while (temp < sound_delay)

                       {        

                               if (chord_changed) return; // get out of here if the player has clicked another chord

                               temp += time_step / 16;

                               wait (1);

                       }

               }

       }

}

 

Die globale Variable "chord_changed" stellt fest, ob der Spieler eine andere Saite angeklickt hat; ist dies der Fall, dann wird der vorige Sound gestoppt, falls er in einer Schleife läuft. Die ersten Zeilen setzen "chord_changes" für 3 Frames auf 1, was ausreicht, um die Schleifen zu beenden.

 

Jeder Sound für die Saiten hat ein anderes Handle, daher stoppen wir alle Sounds. Falls das Intervall ("sound_delay") auf 0 steht, spielen wir den Ton nur einmal. Ansonsten verwenden wir eine Schleife, die den Sound wieder und wieder spielt, wobei jeweils für so viele Sekunden gewartet wird wie "sound_delay" angibt. Die Schleife bricht erst ab, wenn "chord_changed" auf 1 steht, also wenn der Anwender auf eine andere Saite klickt. Ich habe hier wieder einen Trick genutzt und die "wait(-x)" Anweisung durch eine Schleife ersetzt, die temp hochzählt, damit ich jederzeit aus der Schleife aussteigen kann, falls nötig.

 

Mit all diesem neuen Wissen sind wir nun bereit, in die faszinierende Welt der 2D Spiele einzutauchen. Ignorieren Sie aber auch nicht das Potential von 2D Anwendungen. Bis nächsten Monat!