Hallo Besucher, der Thread wurde 35k mal aufgerufen und enthält 69 Antworten

letzter Beitrag von RKSoft am

Einfache Spieleentwicklung am Beispiel :-)

  • Huhu,
    ich habe in ein, zwei Stunden eine Idee angefangen in Code umzusetzen und das ging recht zügig und einfach.
    Daher dachte ich daran, evtl. ein paar Zwischenschritte und Ansätze inkl. Binaries hier zu posten....
    Mal sehen ob am Ende ein "echtes" Spiel daraus wird.
    Wenn ein wenig Interesse besteht, bereite ich die Tage mal die ersten Schritte entsprechend auf :-)
    Gruss,
    Martin

  • Superidee, immer her damit! Das ist motivierend und bringt bestimmt ein paar mehr User dazu, da auch mal kreativ zu werden!


    P.S.: Mal schauen obs da auch ein CSDB-Release pro veröffentlichter Folge gibt, oder wie war das bei Endurion :freude

  • Ich dachte speziell daran hier einfache Schritte fuer ein einfaches Projekt
    zu kommentieren. Also NICHT eines der vielen Brocken die bei mir rumliegen
    und an denen ich immer mal weiterschraube, sondern grade etwas Einfaches.
    Auch soll es kein copy-paste Text werden mit moeglichst generischen Libraries
    oder gar ein Tutorial fuer Assembler oder so.
    Eher ein Motivator, um Ideen und Konzepte einfach mal mutig anzugehen.
    Oft ergibt sich alles Weitere naemlich fast von allein ;-)
    Vielleicht erarbeitet sich ja jemand parallel z.B. Plants vs Zombies oder etwas ganz eigenes...

    Seit ein paar Wochen schaue ich mir den GameboyAdvance genauer an, um dafuer
    zu entwickeln und stiess dann auf einem Flohmarkt am Sonntag auf das Spiel
    "Kuru Kuru Kururin":
    https://www.youtube.com/watch?v=AYdUThdo4Fs


    Das macht recht viel Spass und wie immer war der erste Gedanke:
    geht das auch am C64?


    Es sollte :)


    Zunaechst sollte man sich ueberlegen, welche technischen Limitierungen es gibt.
    Dabei gilt fuer allem fuer Anfaenger: wenn man etwas noch nie auf dem C64 gesehen hat
    hat das vermutlich einen Grund ;-)
    Natuerlich ist ein (und vor allem mein) Anreiz ganz Neues & Spektakulaeres zu schaffen,
    aber derartige Projekte brauchen naturgemaess auch wesentlich laenger und sind absolut
    nicht geeignet fuer einen Einstieg. Nicht selten kommen sie nie wirklich zu einem Ende :)


    Kurz gesagt:
    Doom auf dem C64: ungeeiget
    Turrican 4: eher ungeeignet
    Elite II: nein
    Bobby geht Heim: ja


    Nun ist Kuru Kuru Kururin nicht GANZ so trivial wie 'Bobby geht Heim'.
    Man ueberlegt also wo die groessten Probleme liegen duerften und wie man sie
    zumindest theoretisch loesen kann.
    Punkt 1: der grosse Rotor
    Punkt 2: etwa pixelgenaue Kollisionsabfrage
    Punkt 3: scrollender Screen
    Punkt 4: bunte Grafik
    Punkt 5: Levelmaps
    Punkt 6: lustige Musik


    Damit gehe ich dann einige Minuten, Tage, Wochen im Hinterkopf herum (Zeit zum echten
    Coden am Rechner ist sehr begrenzt. Sehr viel passiert vorab im Kopf oder mal auf nem
    Notizblock bei mir).


    In diesem Fall war es eine Autofahrt als Beifahrer :)
    Zu 1) das sollte man als Sprite umsetzen fand ich (MUSS nicht).
    Dann kann man auch 2) loesen und zwar mit dem VIC Kollisionsregister*.


    * den benutzt man sonst fast nie wirklich bei Spielen. Er arbeitet pixelgenau und
    setzt ein Bit (pro Frame soweit ich weiss) wenn eines der 8 Sprites auf IRGENDEIN
    gesetztesBit der Grafik trifft. Problem: man hat keine Ahnung WO das passiert ist
    es ist pixelexakt und gilt fuer alle gesetzten Grafikbits, also nicht nach Farben oder sowas.
    Dazu spaeter mehr...


    Bevor es mit 1 und 2 weitergeht noch zu den anderen Punkten.
    Welchen Grafikmodus will ich?
    a) Charset Multicolor
    b) Charset Hires?
    c) extended (in etwa Hires mit mehr Farben aber nur 64 chars)
    d) HiResBitmap
    e) Multicolor Bitmap
    f) obskurer Kram


    Aus guten Gruenden arbeiten die meisten Spiele mit a).
    Vor allem Punkt 3) naemlich ein frei (?) scrollender Bildschirm legt einen Charmode nahe.
    Das Spiel soll moeglichst bunt werden trotzdem. Also Charset Multicolor.
    Mit f) anzufangen ist eine ganz schlechte Idee. Hinterher kann man sowas ueberlegen.
    Fuer dieses Projekt sehe ich das aber eher nicht.
    e) ist immer eine interessante Option wenn man eh gute Grafiker 'zur Hand' hat.
    Alles was sich oft, gross oder schnell bewegt nervt aber im Bitmap mode UND man braucht
    fast immer mehr Speicher dafuer (oder es crasht auf einige C64s :)


    Punkt 4 haben wir also gleich mit erledigt. Im MC Charmode kann man einiges machen.
    Ich habe mir angewoehnt immer selbst alle Grafiken zu machen zunaechst. Das hat drei
    gewaltige Vorteile:
    - man muss auf keine Grafiker warten
    - man verschwendet nicht kostbare Pixelzeit der Grafiker wenn sich am Ende doch etwas am
    Modus oder den Farben aendert oder das ganze Projekt stirbt.
    - nichts motiviert gute Grafiker mehr als schlechte Grafik ;-)


    Merksatz:
    Unser Gameprojekt startet IMMER mit Code :)
    Dann Grafik, dann Sound.
    Es gibt Ausnahmen, aber man kommt so erstmal besser voran wenn man nicht eh SEHR eng
    als Team arbeitet von Anfang an.


    Punkt 5 waren Levelmaps.
    Das ist immer kniffelig. Im vorliegenden Fall sehe ich drei Moeglichkeiten:
    1) level spaeter selbst basteln
    2) jemand hat die Levelmaps schonmal extrahiert als Daten oder PNG oder sowas
    3) man reversed das GBA rom image irgendwie selbst um an die Maps zu kommen


    Loesungen:
    2) ist ideal! Einen Konverter von PNG/JPG in ein Levelformat ist schnell geschrieben
    1) ist gefaehrlich! Ein RIESENVORTEIL von Portierungen ist, dass man sich eben NICHT
    mehr mit Gamedesign rumschlagen muss. Gamedesign ist sehr riskant und schwer! Nur
    wenige Konzepte gelingen wirklich gut. All die Retroports und Spiele-Serien sind
    kein Zufall. Die selbe Engine mit anderen Levels kann ganz schnell aus einem Hit
    einen Haufen Muell machen...
    3) darauf wird es vermutlich hinauslaufen. Das ist momentan die groesste Gefahr fuer
    das Projekt :)


    Punkt 6 ignoriere ich bei jedem Projekt bis kurz zum Schluss.
    Eine Engine um SID und auch SFX abzuspielen laesst sich meist ziemlich gut hinterher
    reinfriemeln. Ich code ohnehin meist ohne Ton und Musiker sind noch genervter als
    Grafiker wenn ihr Werk zu lange brach liegt. Ausserdem ist es einfacher die Musik dem
    Spiel anzupassen als umgekehrt von der Atmosphaere her.
    Es gibt wie immer Ausnahmen: Heartlight64 z.B. spielt permanent NMI samples ab. Das ist
    also integraler Bestandteil der Engine und alles muss darauf hin abgestimmt werden vom
    Timing.


    KuruKurublabla braucht nette Hintergrundmusik und einige SFX. Wenn Code + Grafik stimmen
    finden sich mitunter sehr faehige Musiker fuer so ein fertiges Projekt!
    An fehlender Musik ist noch kein Projekt gescheitert. An fehlender Grafik nur wenn der
    Code wirklich mies war und fehlenden Code gibt es bei tollen """Projekten""" immmer :)


    Nun zu Punkt 2 und Punkt 1 die einen Grossteil der eigentliche Spieleengine ausmachen.
    Ich habe mich erstmal fuer Sprite-BG-Kollision mit Kollisionsregister entschieden.
    Das kann spaeter noch Probleme bereiten ist aber ein guter Start.
    Vorteil: ich habe Pixelgenaue abfragen ohne die Kreisbewegung des Rotors in Charpositionen
    umrechnen zu muessen (zunaechst) und es kostet mich fast Null Aufwand :)
    Also ideal um erstmal einen Prototypen zu basteln. Tatsaechlich laesst sich das Prinzip
    mit 2 Zeilen Assembler testen spaeter!
    Nachteil: alles was NICHT Kollisionen hervorrufen soll MUSS als MC-Farbe mit der
    Bitkombination %00 vorliegen. Also EINE Farbe fuer ein 8x8 char.
    Das bedeutet die Strecke hat immer die selbe Farbe, zumindest in einer Aufloesung von 8x8.
    Das muss aber gar nicht mal so schlecht sein. Die meisten (alle?) Rennspiele kommen
    auch mit einer Strassenfarbe aus :)
    Weitere Nachteile gelten zunaechst auch fuer alternative Kollisionsmethoden.


    Nun zu Punkt1:
    der Rotor.
    Der einfachste Ansatz ist zunaechst ein Sprite.
    Man legt einfach fuer jeden Drehwinkel ein Sprite ab und kann fuer die Animation dann
    ganz einfach die Spritepointer aktualisieren. Einfacher gehts nicht.
    Schlauer schon! aber das ist erstmal nicht der Punkt.
    Ersteinmal schnell erkennen, OB und wie es geht.
    Dafuer habe ich in GIMP ein 24x21 Pixel Bild erstellt und einen weissen Balken durch
    die Mitte gemalt ;-)
    Siehe bild1.png. In gruen sieht man das 8x8 Grid das ich eigentlich immer einschalte.
    Das dann um genau 18 Grad gedreht. Dabei kommen neue Bereiche ins Bild die in GIMP
    zunaechst transparent erscheinen. Hier hilft in GIMP -> Image -> FlattenImage um
    die transparenten Bereiche mit der Hintergrundfarbe zu fuellen.
    Das habe ich 10 mal gemacht. Wieso 10? 10 x 18 = 180 Grad und bei einem Stab der sich
    um den Mittelpunkt dreht sind 0-180 Grad natuerlich identisch mit 180-360 Grad.
    Also 10 einzelne Rotationsschritte.
    Achtung: Rotation als Rastergrafik ist verlustbehaftet.
    Also nicht immer 18 Grad weiterdrehen, sondern das Ursprungsbild mal 18, dann 36, 54,...
    drehen. Sieht idR besser aus :) Probiert's mal aus.
    Jedes Bild dann noch schnell in ein Sprite konvertiert.
    Das kann GIMP selbst mit den CBM-plugs.
    Ich verwende ein eigenes Pythonscript.
    Sehr gut funktioniert aber z.B. auch dieses hier auf der codebase:
    http://www.codebase64.org/doku.php?id=base:sprite_converter
    (generell finden sich SEHR schoene Dinge auf dem Wiki)
    Ein einfaches binary haengt an:
    10frames.prg


    Mit Feuer kann man die 'Drehrichtung' aendern.
    Ein Sprite ist 24x21 Pixel gross. Damit kann so ein Rotor lediglich 21 Pixel lang sein.
    Das ist etwas mau :)
    Loesung 1) extended sprites - das kann der C64 ja in Hardware sogar!
    Siehe hier:
    10frames_large.prg
    Die Groesse finde ich ganz ok, aber haesslich ist es schon.
    Das war allerdings von Anfang an klar. Daher war der Ursprungsgedanke auch gleich
    von Vornerein ZWEI Sprites zu verwenden:
    10frames_twice.prg



    Gruesse,
    Martin /enthusi

  • Teil2...


    Supi - jetzt muessen die nur noch irgendwie waehrend sie sich jeweils um die eigene
    Achse drehen auch noch umeinander drehen!
    Das ist einfacher als es klingt.
    Man braucht nur Sinus und Cosinus.
    Um das mal schnell zu testen hab ich ein kleines Pythonscript geschrieben, welches mir
    pro Animationsframe ein x und y-Offset fuer jedes Sprite berechnet.
    Um diese Offesets korrigiere ich die beiden Sprites jetzt einfach.
    10frames_translate.prg
    Man sieht das ganze ist noch etwas wackelig, funktioniert aber prima!
    Beide Sprites drehen sich nach wie vor einfach um die eigene Achse.
    Hier das ganze mal ohne 'Animation':
    1frame_translate.prg


    So habe ich mir das im Kopf etwas vorgestellt. Bisher sind am PC
    nur etwa 30 Minuten vergangen, ein wenig mehr vielleicht.


    Jetzt geht's erstmal um's erste Verfeinern. Die Luecke in der Mitte muss nicht sein,
    darf aber, da wir die maximale Spannweite wollen. Ich setze einfach eine 'Kugel' rein.


    Dann sind die 18 Grad pro Schritt etwas grob.
    Der code laesst sich einfach anpassen. Nur GIMP von Hand macht dann keinen Spass mehr.
    Python ist auch hier sehr maechtig:

    Code
    1. imgo = Image.open(sys.argv[1]).convert("1")
    2. for i in range(frames):
    3. img=imgo.rotate(-i*3).resize((24,21))
    4. ...


    Diese Zeilen oeffnen ein Bild und legen es in 'imgo' ab.
    Dann wird es sooft wie in der Variablen 'frames' steht um -3 * i Grad gedreht und
    hinterher auf die Groesse von 24x21 gebracht (Python Image schneidet sonst etwas ab).
    Das .convert("1") erzeugt Bitmaps mit 1-bit Farbtiefe (s/w).
    Also statt 10 Frames von Hand zu erzeugen, generiert mir mein Script nun 180/3 = 60
    Sprites von allein.


    Das sieht dann schon netter aus:
    60_frames.prg


    Mit dabei:
    - einfache, buggy Joysteuerung :)
    - fire fuer Richtungsaenderung


    3 Sprites also in Verwendung.
    Da ist noch Platz. Ohne dass es dem Spiel besonders nutzt, habe ich noch fix
    einen Motionblur eingebaut - damit es auch Spass macht ;-)
    Dabei merkt sich der code die letzten beiden Drehwinkel und plottet
    jeweils ein dunkleres sprite mit niedrigerer Prioritaet unter die neueren.
    Und eine mini-Routine die zufaellig ein paar Hindernisse ins Bild schreibt.
    Dann wie angekuendigt eine einfache Kollisionskontrolle:

    Code
    1. lda $d01f ;Kollisionsregister der beim lesen geloescht wird!
    2. sta $d020


    Die versprochenen zwei Zeilen.
    So sieht das grade aus:
    60_frames_blur.prg
    (lila ist einfach %00000100)
    Wenn man den Rotor geschickt plaziert bekommt man auch alle 4 Bits gesetzt fuer hellgrau!)


    Das ist der aktuelle Stand nach ca 1-2 Stunden Code (inkl. Tee etc.).
    Die Sprites sind immernoch nicht wirklich auf einer Linie wie man jetzt deutlicher sieht.
    Die Korrekturtabelle ist ja lediglich etwas grob berechnet. Spaeter kann und sollte
    man jeden Frame einzeln nochmal ueberpruefen und korrigieren.
    Ob man wirklich 60 Frames braucht sei dahingestellt. Der Speicher gibt es locker her,
    aber 6 Grad pro Schritt reichen vermutlich auch.


    Da man jetzt WEISS dass das Spiel moeglich ist (Groesse und Aussehen des Rotors gefallen mir),
    kann man sich an die anderen Dinge wagen.


    I)
    Wenn ich das naechste mal Zeit finde, versuche ich mal ein wenig die originalen Levelmaps
    zu reversen oder als test mal von Hand zu kopieren. Wieviele Chars man fuer die ganzen 'Kurven' und 'Schraegen' braucht wird sich zeigen.
    Vorteil: Auch GBA benutzt 8x8 Tiles, also besteht Hoffnung :-)


    II)
    Dann eine einfache Scrollengine einbauen. Die darf auch ruhig $d800 mitkopieren, da
    ja sonst nicht ALLZU viel passiert pro Frame (keine Aliens, Rastersplits, etc...).
    Das wird vermutlich der langweiligste Teil. Vor allem wenn man dann mal 'freies Scrolling' will...


    III)
    Wir haben ein Problem mit der Kollision ignoriert. Wenn der Rotor eine Wand trifft
    soll ja nicht der Rahmen lila aufflackern sondern wirklich etwas passieren.
    Vorteil: wir wissen wenigstens sofort welches ENDE des Rotors kollidiert ist.
    Mein Gedanke dazu: die letzten n Schritte aufzeichnen und dann bei Kollision kurz
    in die andere Richtung weg-bewegen bis keine Kollision (pro Frame!) mehr stattfindet.
    Ob das geht? Man wird sehen ;-)


    Das war es zunaechst einmal.
    Bei Bedarf ergaenze ich das mal mit mehr Sourcen oder Grafiken, aber ich denke der schnelle
    Ansatz sind schonmal deutlich geworden.
    Sobald man _irgendeine_ Levelmap zum Testen hat, faengt es auch an Spass zu machen :)
    (hoffe ich).


    Bis auf weiteres mit Gruss,
    Martin /enthusi

  • Genau! Man fängt niemals nicht mit dem Titelbild an :)


    Das kann man nicht so pauschal sagen. Als ich vom CPC (BASIC/Z80) auf den Amiga umstieg und später mit Oberon programmiert hatte, da hatte ich auch erst mit dem 'Titelbild' angefangen, um mich mit der Sprache usw. vertraut zu machen.

  • Nachteil: alles was NICHT Kollisionen hervorrufen soll MUSS als MC-Farbe mit der Bitkombination %00 vorliegen. Also EINE Farbe fuer ein 8x8 char.

    Soweit ich weiß, gelten bei MC-Charset die Bitkombinationen %00 und %10 als Hintergrund und lösen keine Sprite-Char-Kollision aus.

  • M.W. hat ssdsa mit den Kollisionen recht.
    Die spannende Frage ist, wie weit man mit der Abfrage des Hardware-Kollisions-Registers wirklich kommt.
    Vermutlich würde ich von vornherein per Softwarelösung abfragen.
    Ich hoffe du ziehst das Projekt durch!
    Das Spiel hat Aspekte, die über Anfängerfragen deutlich hinausgehen, zumindest, wenn es dem Original
    möglichst nahe kommen soll.


  • Stimmt, zuerst sollte die Musik fertig sein. Und das Design für die Modul-Verpackung.


    Ach was, VM zeigt wie's geht: Immer zuerst das Ganze placeholderfertig als Erstes bereitstellen! :thumbsup: