PlanetSwitch Planet3DS PlanetVita PSP.de PlanetiPhone Classics Forum

PGN-ID:[?] (Nicht eingeloggt)
Login
Registrieren
PlanetDS PlanetGameboy N-Page.de

dumme Tutorials Teil 2: Sprites Animieren (DMA update)

zurück

Autor: dummer Anfänger

Kategorie: devtut
Umfang: 1 Seiten

Seite   1  

Kommentare:

Kommentieren (-)

Game Boy Advance Artikel vom 02.10.2002







Sprite
Animationen mit dem GBA








[Wiederholung: Das Sprite]
[Tiles]
[Unsere erste Animation] [Unsere Animations Funktion] [Erstellen der
Funktion
]
[Animate mit DMA] [Beim nächstem mal]
























Autor: dummer
Anfänger
Erstellt: 11.Sep.02
bis

02.Okt.02
Level: Fortgeschrittene
Kenntnisse: GBA Sprites

DMA

Photoshop













Hallo ihr Leute und willkommen zu
meinem zweitem GBA Tutorial.

Dieses Tutorial soll der Auftakt zu einer kleinen Serie werden in dem wir das
A-bis O durchnehmen was wir denn benötigen um ein eigenes kleines Spiel zu
machen. Natürlich wird am Rande auch erklärt wie genau wir dort hin kommen ;)


Ich werde mich sehr bemühen die
ganzen Sachen verständlich zu erklären. Seit mir jedoch nicht böse wenn ich an
mancher Stelle einfach sage "Das ist so!". An diesem Punkt hab ich selbst nicht
so ganz den Überblick gehabt und wie so oft durch ausprobieren herausgefunden
was funktioniert. Das ist übrigens auch mein Tipp an euch: Wenn etwas nicht ganz
so funktioniert wie ihr wollt das es funktioniert... Versucht es mal mit
ausprobieren. Manchmal hat man Glück.

Ehe es nun los geht kommt jedoch erst einmal eine kleine Erläuterung zu diesem
Tutorial selbst und damit verbundenen Sachen wie Copyrights, Zielgruppe usw...
Aaaalso:


Dieses Tutorial wurde von mir in der
Absicht geschrieben Leuten zu zeigen wie man mit dem GBA von der Firma Nintendo,
die alle Rechte an dem GBA und der kommerziellen Veröffentlichung von GBA
Spielen besitzt, umgeht und mit Hilfe anderer Software dafür Programmiert. Ich
gebe keinerlei Garantie das was ich euch hier zeige Funktioniert und übernehme
keinerlei Haftung für Schäden die eventuell bei euch entstehen während ihr
dieses Tutorial durcharbeitet ^^.

Dieses Tutorial richtet sich an Programmierer. Dies bedeutet das ihr die Sprache
"C" beherrschen müsst und darüber in klarem sein solltet was ein Pointer ist.
Auch gehe ich davon aus das ihr bereits in den anderen auf
www.devgba.de erhältlichen Tutorials
geschmökert habt und das dort beschriebene Fachwissen beherrscht. Ihr solltet
wissen was Duale und Hex-Zahlen sind, den Unterschied zwischen Bit und Byte
kennen sowohl als auch in der Lage sein diese Werte zumindest mit einem
Taschenrechner umzuwandeln. Auf Fragen die
eine Programmiersprache selbst bzw. das dort beschriebene Wissen betreffen werde
ich nur ungern Antwort geben bzw. euch wahrscheinlich barsch die URL geben damit
ihr es selbst lesen könnt. Sachen die in deutsch nicht verfügbar sind werden
jedoch gerne von mir, so wahr ich es kann, beantwortet. Überall im Text sind
jedoch genaue Quellenangaben zu sehen wo man sich genauer darüber erkundigen
kann.





Wiederholung: Das Sprite



Der GBA besitzt eine Hardware
Unterstützung für Sprites und damit alles was das sehnsüchtige Spiele
Programmierer Herz sich nur wünschen kann. Hardware Sprites sind nicht nur
relativ schnell sondern sie verfügen über Features wie horizontales und
vertikales spiegeln, Zoomen und sogar Rotation um einige Beispiele zu nennen.
[1]



Fakten:

Der Sprite bzw. Objekt Speicher, OAM (Object
Attribute Memory) ist
1024 Byte groß und hat somit Platz für 128 Sprites dessen Eigenschaften jeweils
in vier Attributen beschrieben sind. Das letzte Attribut beinhaltet die
Rotationsdaten. Es werden 4 Rotationsdaten pro rotierendes Objekt benötigt. Der
GBA zählt automatisch beim Index nehmen jeweils 4 mal das 4. Attribut zusammen.
Der OAM beginnt bei der internen Speicheradresse 0x7000000 und ist write only,
kann also nicht gelesen werden.


Würde man sich das GBA Sprite einmal
exemplarisch wie eine Klasse in C++ vorstellen so hätte man folgende
Eigenschaften und Verweise:



















































































OAM Sprite


y Koordinate
y Position
des Sprites (0-255)

x Koordinate
x Position des Sprites (0-512)


Rotations
Flag

Ist die Rotation Aktiv? (true/false)

Rotations Index
ID
der Rotationsdaten ausgehend von einem Standart Array.


Double Flag

Doppelter Anzeigeraum? (true/false)

Horizontales
Spiegel Flag

Soll das Bild horizontal gespiegelt werden? (true/false)


Vertikales
Spiegel Flag

Soll das Bild vertikal gespiegelt werden? (true/false)

Alpha
Blending

Alpha Blending Einstellungen


Mosaic Flag

Ist der Mosaikeffekt aktiv? (true/false)

Farb Modus

Welcher Farbmodus genutzt wird.

0 = 16 Farben

1 = 256 Farben


Farbpaletten
Index

Gibt bei 16 Farben Sprites an welche
Farbpalette benutzt wird.

Objekt Forum

Beschreibt die Form des Sprites


Sprite Größe

Die Größe des Sprites

Bild Index
Der
Index an dem das Sprite im Bildspeicher beginnt. (0-1023)



Zeichenpriorität

Welches Sprite überzeichnet welches? (0-3)


Uff, args, nun habe ich das wirklich alles noch
einmal aufgelistet... Wie auch immer. Wie wir sehen können gibt es drei Verweise
innerhalb des Sprites.

Einmal haben wir dort den Rotationsindex.
Beabsichtigen wir unser Sprite rotieren zu lassen so benötigen wir diesen Index
wo sich die Daten befinden die beschreiben in welchem Zustand sich das Sprite
befindet. Ist Rotation deaktiviert hat dieses Feld keine Bedeutung.

Dann haben wir den Paletten Index. Ist die
Farbeigenschaft auf 16 Farben gestellt so wird der GBA Automatisch dessen
Farbpalette laden die an dieser Stelle beschrieben ist. Der GBA selbst überprüft
nicht ob ihr nun immer 16 Farben bei Sprites benutzt oder nur dieses Sprite auf
16 Farben justiert wurde. Er wird in jedem fall den Index*16 nehmen und an
dieser Stelle die 16 nächsten Farben benutzen. Diese Spielereien besprechen wir
hier jedoch nicht ^^. Ist das Sprite auf 256 Farben gestellt wird dieser Bereich
ebenfalls ignoriert.

Wichtig:
Alle Sprites die 256 Farben benutzen sind doppelt so groß wie 16
Farben Sprites!


Der Bild Index ist
das worum sich erst einmal unsere Aufmerksamkeit drehen wird. Hier wird
beschrieben wo im "Character Memory" (gefolgt
Sprite Speicher genannt) sich unsere Bilddaten befinden. Dieser Speicherbereich
beginnt bei 0x6010000 und kann 1024 Tiles fassen (256 Farben Tiles verbrauchen
je wieder doppelt so viel Speicher).

Benutzen wir einen Bitmap Modus (3-5) so
breitet sich dieser über die hälfte des Tile Speichers aus wodurch wir nur noch
Tiles mit einem Index ab 512 benutzen können.


Der Sprite Speicher kann zusätzlich auf zwei
unterschiedliche Arten adressiert werden. Wir beschränken uns jedoch auf die 1D
Art die gegenüber der 2D Methode den Vorteil hat das wir die Daten in einem
"Happen" benutzen können.







Tiles, eine Art Leben der Bilder




Tiles sind, wie die meisten noch wissen, die
kleinen Kacheln in die man eine Levelkarte unterteilt um immer nur die selben
Grafikbauklötze benutzen zu können und so eine Menge Zeit zu sparen. Dieser
noble Vorsatz wird auch bei Sprites benutzt und bringt uns bei diesen hingegen
ein wenig Denkakrobatik denn Tiles bestehen aus jeweils 8x8 Pixeln in 16 oder
256 Farben. Damit haben wir vorerst kein Problem aber nehmen wir uns einmal ein
kleines Beispiel:


Da ist er.
Unser Held! Schlank, sportlich und in den Abmessungen 24 zu 32 Pixeln. So mögen
wir ihn in Windows vielleicht sehen, doch da unsere Sprites ebenfalls ihre
Bilddaten in Tiles speichern ist er in Wirklichkeit wie ein Schachbrett zersägt.


Betrachten wir uns unseren Helden in Tiles
geschnippelt einmal im GBA Speicher so würden wir in etwa so etwas hier sehen.
(siehe unten).
Dies bedeutet das wir auch beim zusammensetzen des ganzen Sprites umdenken
müssen. Insbesondere das umwandeln der Grafiken von normalen Bildern in Sprites
die einigermaßen Animationstauglich waren hat mir zunächst größere Probleme
bereitet als mir lieb war.







Unsere erste Animation



Beginnen wir also mit unserer ersten Animation. Nachdem wir uns im klaren
darüber sind wie groß unser Sprite sein soll und wie viele Farben wir
benutzen wollen. Öffnen wir einen guten Grafik Editor um dann dort unsere
Grafiken die wir benutzen wollen fein säuberlich zusammen zu tragen und im
richtigen Abstand zu einander auszurichten.


Genau dieses Ausrichten ist eine Sache an der ich persönlich früher im
Paintbrush gerne einen Nervenzusammenbruch bekommen habe. Zum Glück gibt es aber
heutzutage Programme die ein Raster einblenden können wie zum Beispiel
Photoshop.


Um die Hilfslinien einzublenden
gehen wir im Photoshop Menü (hier 6.0 deutsch) Auf Bearbeitet >
Voreinstellungen > Hilfslinien und Raster
. Idealer weise stellen wir dann
das Raster auf die Breite die unser Sprite haben soll, ich habe hier einmal 32
Pixel genommen, und die Unterteilungen auf einen Wert der 8 Pixel ergibt. In
diesem Fall sind es also 8 Unterteilungen.


Dann bestätigen wir und
aktivieren das Raster unter Ansicht > Extras einblenden und Ansicht >
Einblenden > Raster
. Bei mir hat es sich außerdem sehr bezahlt gemacht ein
paar Menüpunkte weiter unten, also bei Ausrichten an ... ebenfalls Raster
einzustellen. Dadurch konnte ich leichter dafür sorgen das meine Figuren am
rechten Fleck waren.



Wie
ihr dort an dem Bild sehen könnt habe ich ein kleines Animation Table von
unserem allzeit beliebten (wehe wem nicht *g*) Secret of Mana Heldens gemacht.
So in etwa werden heutzutage die Sprites gespeichert damit man sie dann später
im Programm gut benutzen kann.

Unser Problem nun wird jedoch erstmals die 8x8 Tile Ordnung werden.


Alle zur Zeit erhältlichen Bild
nach GBA Konverter arbeiten artig in Tiles und von links nach rechts. Wenn wir
nun aber unser Schema durchgehen würden hätten wir erst mal nur die Haare, dann
nur die Köpfe usw. usw. Kurz: Die Bilddaten währen ohne ein erneutes Umrechnen
im GBA untauglich.


Zum glück gibt es da einen
einfachen Trick, wenn man es überhaupt so nennen darf ;)

Wir machen schlicht alle Animationen unter einander. Dadurch wird das Tool
automatisch das Bild so umwandeln das es in dem selben Format ist wie wir es
später im GBA benötigen. Besser geht's praktisch nicht.


An dieser Stelle schon solltet
ihr darauf achten das ihr einige Regeln beachtet die euch sonst das Leben zur
Hölle machen könnten.















1.) Achtet nun schon darauf eure
Animationen in eine Reihenfolge zu bringen in der dann später ALLE Sprites
arbeiten müssen. Also Laufen, Stehen, Rennen, Kämpfen, Sonderaktionen. Alle
Sprites in einem großen Spiel in dieses Schema zu pressen geht meistens
nicht. Da benutzt man dann sowas wie "Hoch auflösendes Sprite" "Billiges
Sprite" "Monster". Also drei Schemen. Endgegner die nach Erwartung des
Spieles möglichts supertoll sein sollen macht man in der Regel erst dann
wenn man sie brauch per Hand.

2.) Macht keine Schatten auf oder unter euer Sprite. Wenn
euer Held dann spring würde dies ein gigantisches Sprite ergeben drum machen
wir die Schatten erst später im Spiel zusätzlich.

3.) Versucht euer Sprite so auszurichten das ihr einfach nur
ohne weiteres die Bilder wechseln müsst. Das geht nicht immer also merkt
euch als Faustregel: Wenn wir später im Spiel extra die Sprites in Animation
x um 4 Pixel nach links schieben, verbrauchen wir zwar extra Variablen aber
diese werden NIEMALS so groß sein wie wenn ihr eure Figur deshalb in ein
größeres Sprite packt bei dem jedes Tile 8x8x8xFarbtiefe Bit verbraucht!

So, nachdem wir uns darum
gekümmert haben steht dem schreiben einer Funktion die bei unserem Sprite die
Bilder auswechselt nichts mehr im Wege.







Unsere Animations Funktion



Ziel unserer ersten Funktion ist
es klar einfach nur das Bild eines Sprites auszutauschen. Laufen und ähnliches
wollen wir später noch in Ruhe regeln.

Im Prinzip gibt es zwei Möglichkeiten wie wir nun vorgehen könnten um die
notwendigen Daten zu ermitteln die wir brauchen. Wir könnten sie
zusätzlich Speichern etwa in einer eigenen Struktur,
oder aber wir können die schon vorhandenen Daten aus dem
Hardware Register lesen
.

Im Prinzip ist es jedem selbst überlassen wie er es nun genau macht. Das eine
ist verschwenderischer aber angeblich schneller (Struktur) und das andere zwar
sparender aber angeblich langsamer (Auslesen).


Ich persönlich sage dazu nur: Das
Auslesen funktioniert über Binäre Funktionen die von der CPU schneller
verarbeitet werden können als jede + oder - Operation und daher auf jeden Fall
schneller als das übergeben einer vom Benutzer definierten Struktur. Einziger
Nachteil ist das man man Coden etwas nachdenken muss, aber dafür sind wir ja
hier: Um zu lernen, oder?

Ich sage ja nich das alle dieser Meinung sein müssen.


Stellungnahme hin oder her: Ich
bin der Autor und sage wir benutzen die Hardware. Feddich.

Beim erstellen der Funktion sollten wir uns erst einmal Gedanken machen was wir
nun eigentlich brauchen um die Grafik des Sprites auszutauschen.




  1. Wir müssen wissen wo die neuen
    Bilddaten im Speicher sind.

    Das muss uns der Benutzer sagen.




  2. Wir müssen wissen wo die alten
    Bilddaten im Sprite Speicher sind.

    Das kann uns das Sprite sagen.




  3. Wir müssen wissen wie lang die
    Bilddaten sind wozu wir Farbauflösung und Größe wissen müssen.

    Kann uns das Sprite sagen.




  4. Wir müssen wissen welche
    Animationsphase wir nehmen wollen.

    Das muss uns logischerweise der Benutzer sagen.




So gesehen brauchen wir also drei
Angaben um das Bild auszuwechseln: Welches Sprite
mit welcher Animation welcher
Bilddaten
gefüttert werden soll.

Wir gehen mal davon aus das die in den Bilddaten befindlichen Bilder auch zu dem
Sprite passen, währe ja sinnlos sonst.


Unsere grobe
Funktionsumschreibung sieht demnach dann in etwa so aus:



void
animate(u8
sIndex, u16* BitmapData, u16
aPhase);


Drei Übergaben die später bei der
Anwendung leicht von der Hand gehen müssten.

Als erstes haben wir da sIndex, den Sprite Index.
Dabei gehen wir von der Art und weise aus das wir zuvor schon alle 128 Sprites
in einem Array vorliegen haben. Diese Methode benutzt Dovoto in seinem Tutorial
[1] und ich werde daher nicht näher darauf eingehen.

Dann haben wir mit BitmapData einen Pointer auf das
Array in dem die Bilddaten enthalten sind die von der Auflösung her natürlich
auch zu dem Sprite passen müssen.

Zuletzt haben wir in aPhase die Animationsphase die
unser Sprite bekommen soll.


Alles in allem kann man die
Funktion schon zum erstem füllen des Sprites mit Bilddaten benutzen.







Erstellen der Funktion



So, ich hoffe ihr habt alle schon
einmal etwas über die Binären Grundrechenarten AND und OR gelesen die im Laufe
dieses Schrittes benutzt werden.

Wenn nicht so solltet ihr einmal euren Informatik Lehrer fragen oder das Thema
in www.devgba.de auf dem Forum ansprechen.
Ich gebe dann auf Wunsch eine kurze Einführung dazu doch an dieser Stelle währe
es denen die so etwas bereits beherrschen ungerecht gegenüber alles noch mal
durch zu kauen.


Zuerst kommen, wie in jeder neuen
Funktion, die Parameter in denen wir unsere Rechenergebnisse speichern wollen.

Speziell nun wo wir die Register Werte des Sprites filtern müssen ist es wichtig
den Überblick zu behalten über das was wir tun.




int lenght;
// Länge eines Sprites in tiles

int colord;
// Farbtiefe

int charstart;
// Wo im Bildspeicher?

int loop = 0;
// Loop Variable


So, ab nun geht es richtig los.

Für bessere Übersicht habe ich die folgenden Schritte einmal deutlich
aufgeteilt. Sie einzeln zu lesen macht dennoch wenig Sinn.

Solltet ihr nach wiederholten Schritten
nicht wissen wies funktioniert macht euch keine Gedanken, später benutzen wir
diese Funktion eh nur noch und dann ist es uns egal zu wissen wie sie
funktioniert denn dann freuen wir uns einfach über das was sie kann ^-^;



1. Die Länge unseres Sprites.

Die Länge des Sprites befindet sich am ende der 16 Bit Kette des zweiten Sprite
Registers. Das bedeutet wir müssen erst einmal die anderen Werte heraus filtern
ehe sie unser Ergebnis stark, SEHR stark unbrauchbar machen. Haben wir dies
getan haben wir also eine Zahl um die 16 000 die noch umgewandelt werden muss in
eine Zahl von 0-3. Schauen wir es uns erst einmal an!

































bits




15




14




13




12




11




10




9




8




7




6




5




4




3




2




1




0



Attribute1 -

zweites Reg.



Größe



VF



HF



Rotations-Data

Index



X Koordinate



Nun müssen wir das ganze Filtern. Dazu benutzen wir eine
UND (zu englisch AND)
Verknüpfung. Dies bedeutet das er nur die Werte beibehält die sowohl im Register
als auch bei unserem Filter 1 stehen. Ist einer von beiden 0 dann ergibt dies
eine null. Erster UND zweiter Wert halt.





















































































bits




15




14




13




12




11




10




9




8




7




6




5




4




3




2




1




0


z.B.:

1

0

1

1

0

0

0

0

0

0

0

0

1

1

1

0

Unser Filter:

1

1

0

0

0

0

0

0

0

0

0

0

0

0

0

0

Ergibt:


1


0

0

0

0

0

0

0

0

0

0

0

0

0

0

0


Nun gut, nun haben wir die unwichtigen Werte also schon einmal rausgekürzt.
Nun interessiert uns aber halt nicht der riesige Wert, sondern der Wert von 0-3
der die Größe des Sprites angibt und den wir im Prinzip auch haben, leider am
falschem Ende der Zahlenkollone.



Daher benutzen wir nun die nächste Binäre Funktion, dass
Schieben.

Schrieben
bedeutet im Prinzip nur das wir den ganzen Wert um x Bitstellen schieben.

test>>2 Schiebt den Inhalt von test um 2 Bitfolgen nach rechts.

test<<5 Schiebt den Inhalt von test um 5 Bitfolgen nach links.
Eigentlich simpel, oder?


































































bits




15




14




13




12




11




10




9




8




7




6




5




4




3




2




1




0



Unser Wert:


1


0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

>>14

0

0

0

0

0

0

0

0

0

0

0

0

0

0


1


0

Hmm .... schaun wir nun, wo wir den Wert so haben wie wir ihn wollte ... mal
genau hin.

Also eigentlich war das Filtern vorher ja unnötig oder? Die Werte stehen ganz am
Rand daher brauchen wir das Filtern an dieser Stelle nicht. Ein Ausnahmefall.
Wie dem auch sei, so sieht das ganze in unserer Funktion dann aus:


lenght =
sprites[sIndex].attribute1>>14;


Gehen wir weiter.

Wir haben nun die Länge in einer Zahl von 0-3 isoliert und
wissen das die 0 für 8x8 (also ein Tile) die 1 für 16x16
(also 4 Tiles) usw. steht. Nun gibt es sicher einen einfachen Algo. mit dem die
Anzahl der Tiles sicherlich leicht berechnett werden kann. Da ich aber zu faul
war darüber nachzugrübeln und meine 5 im Abschlusszeugnis Mathe sicher nicht
umsonst gehabt habe nehmen wir eine If Anweisung. So ersparen wir uns auch das
includen sämtlicher Mathefunktionen in unser Programm.


if(lenght==0)
lenght=2*2;

else if(lenght==1) lenght=4*4;

else if(lenght==2) lenght=8*8;

else if(lenght==3) lenght=16*16;


Damit ist unsere Länge in Tiles also bestimmt.



2. Die Lage der Spritedaten

Als nächstes wollen wir wissen wo das Sprite seine Bilddaten speichert. Diese
sind im 3. Spriteregister enthalten und stehen ganz am Ende, was bedeutet das
wir sie nur heraus filtern müssen. Also erstellen wir uns wieder unseren
geliebten Filter der nur die ersten 10 Bits stehen lassen soll.



0000 0011 1111 1111


So sieht er aus, schnell in den Taschenrechner eingetippt und in einen Hexwert
umgerechnet ergibt das also
0x03FF und folgende Quellcodezeile:


charstart =
sprites[sIndex].attribute2 & 0x03FF;



3. Die Farbtiefe bestimmen

So, da sind wir endlich beim letzten Wert den wir noch bestimmen müssen: Der
Farbtiefe.

Wie schon erwähnt ist ein 16 Farben Tile nur halb so groß wie ein 256 Farben
Tile. Wenn wir also die Farbtiefe nicht einbeziehen kommen wir in große
Schwierigkeiten.

Dieses mal interessiert uns also im Prinzip nur ein einziges Bit. Für dieses zu
filtern und zu schieben währe demnach eine eigentlich überflüssig komplizierte
Angelegenheit da uns ja zu guter letzt nur interessiert ob er da ist oder nicht.
Eine If Anweisung tut es da sicherlich auch.


if( (sprites[sIndex].attribute0 &
0x2000) != 0) colord=2;

else colord=1;


Mit dieser Funktion ist im
Prinzip alles gesagt.

Wir haben wieder unseren Filter erstellt (
0010
0000 0000 0000
bzw.

0x2000
)
und UND Verknüpft.

Haben wir nun 256 Farben, müssen also alles mal 2 nehmen, dann haben wir als
Ergebnis einen Wert der größer ist als 0. Haben wir nur 16 Farben so ist auch
dieses eine Bit eine Null und wir kommen insgesamt auf den selben Wert.

Eine Schlichte aber wohl ausreichende Lösung.



4. Die Werte Kopieren

Endlich kommen wir zum eigentlichem kopieren. An diesem Punkt stellt sich
nun die Frage ob wir die CPU in einer Schleife oder DMA zum kopieren benutzen
wollen. Doch egal ob wir CPU oder DMA benutzen, wir brauchen erst einmal
folgende 3 Werte.




  1. Wie weit, also wie viele Daten müssen wir kopieren?



  2. Von wo müssen wir die Daten kopieren?



  3. Nach wo müssen wir die Daten kopieren?



OOoookai. Beginnen wir mit "Wie weit?". Die genaue Länge unserer
Animationsphase im Speicher ist Länge in Tiles*(8 für eine 16bit
Farbe*Farbtiefe)
. In Quellcode ausgedrückt also


lenght*(8*colord)
. Die Klammer hab ich gesetzt damit man genauer
sieht wo es um die Tiles und wo es um die Datenlänge geht. Falls man das später
ändern muss geht es so leichter hoffe ich.


"Von wo?" müssen die Daten wech? Nudenn,
wir wollen die Daten ja mit der CPU kopieren, besitzen also zwei Arrays dessen
Index es nur anzugeben gilt. Den Index der Daten "von wo" beschreiben wir mit
Zählvariable+(AnimationsPhase*(Länge in Teiles*4
für eine 16bit Farbe*farbtiefe))
.

Ich gebe zu dies ist der kritischste Teil der
Funktion und falls mir hier ein Fehler unterlaufen aus welchem resultierend das
hier nicht funktioniert, bitte ich diesen zu melden.

loop+(aPhase*(lenght*4*colord)
ist der Code dazu.

Sicherlich ist eure erste Frage aber
wieso dort ein 4 benutzt wird obwohl ein 16 Farben Wert doch 8Bit lang ist. Nun
aber wir arbeiten hier in einem u16 Array (Siehe
Code Download wo es deklariert ist) was zur Folge hat das wir immer gleich zwei
Pixel auf einmal kopieren. Ich konnte leider noch keine Test's mit 16 Farben
Sprites machen... Wenn Fehler auftreten mailt mir einfach einmal euren Quellcode
(vollständig bitte).


"Wohin?" müssen die Daten?
loop+(charstart*8*colord)! Also
Zählvariable+(SpeicherBereich*8*Farbtiefe)
.


Wenn wir uns damit herrumgekloppt haben kommen wir
nun endlich zur eigentlichen Funktion wie sie auch funktioniert.


void animate_cpu(u16
sIndex, u16* BitmapData, u16
aPhase) {

int lenght = 8;

int colord = 2;

int charstart;

int loop = 0;



lenght = sprites[sIndex].attribute1>>14;



if(lenght==0) lenght=2*2;

else if(lenght==1) lenght=4*4;

else if(lenght==2) lenght=8*8;

else if(lenght==3) lenght=16*16;



charstart = sprites[sIndex].attribute2 & 0x03FF;



if( (sprites[sIndex].attribute0 &
0x2000) != 0) colord=2;


else
colord=1;



for(loop=0; loop<(lenght*(8*colord));
loop++) OAMData[loop+(charstart*8*colord)] = BitmapData[loop+(aPhase*(lenght*4*colord))];

}







Animate mit DMA



Nun da wir unsere Funktion fertig haben sollten
wir auch einmal einen Gedanken daran verschwenden DMA zu benutzen. Da wir von
vorn herein sowohl bei der Umwandlung des Bildes als auch bei der Wahl von 1D
Speicher Management darauf geachtet haben das wir alles mit einem Happen
kopieren können sollten wir nun auch Gebrauch von dem Hochgeschwindigkeits-Bus
machen.


Mancher mag sich nun fragen ob das wirklich so
viel Sinn macht und ich bin der Meinung: Ja macht es.

Nicht nur das die CPU langsamer ist als DMA, sie muss auch bei unserer Schleife
quasi nach jedem 16 Bit Wert den sie schreibt neu absetzen, den zählwert neu
inkrementieren, den Index neu berechnen und wieder aufsetzen. Wir sehen, das
dauert etwas ^^

Gewiss ist bei kleinen Sprites der Geschwindigkeitszuwachs nicht der Rede wert
aber schließlich macht es ja die Menge aus. 128 Sprites kosten schon einiges
mehr Rechenzeit die wir nicht verschwenden wollen.


void animate_dma(u16
sIndex, u16* BitmapData, u16
aPhase) {

int lenght = 8;

int colord = 2;

int charstart;

int loop = 0;



lenght = sprites[sIndex].attribute1>>14;



if(lenght==0) lenght=2*2;

else if(lenght==1) lenght=4*4;

else if(lenght==2) lenght=8*8;

else if(lenght==3) lenght=16*16;



charstart = sprites[sIndex].attribute2 & 0x03FF;



if( (sprites[sIndex].attribute0 &
0x2000) != 0) colord=2;


else
colord=1;



DMA3SAD = &BitmapData[aPhase * (lenght * 4 * colord)];

DMA3DAD = 0x6010000 + (charstart*8*colord);

DMA3CNT = (lenght * ( 8 * colord)) | DMA_16NOW;

}







Beim nächstem mal





Gut, nun haben wir eine einfache Möglichkeit
jederzeit von allen möglichen Sprites die Grafiken auszutauschen und damit die
wichtigste Grundlage für ein Spiel geschaffen. Für einen einfachen vertikal
Shooter mag das vielleicht sogar schon reichen doch natürlich werden wir hier an
dieser Stelle nicht aufhören.


Im nächsten Teil des Tutorials werden wir uns
Gedanken darüber machen wie wir ein Sprite effektiv animieren können ohne das
wir jedes mal per Hand die Animation ändern und das Bild verschieben. Es soll
schließlich genügen dann

sprite_do(sprite,HIT);
zu sagen damit
unser Sprite zuschlägt wie wir es zuvor sagten das er es machen soll.

Am Ende des nächsten Tutorials werden wir dann also eine Figur haben mit der wir
umher laufen können und die eine beliebige Anzahl von Tätigkeiten ausführen
kann.


In dem Tutorial darauf (falls ich bis dahin nicht
die Lust verloren habe da Tutos schreiben viel Zeit verschlingt) werden wir uns
ausführlich mit Maps beschäftigen. Selber welche mit einem Level Editor
erstellen und uns einmal eine simple Kollisionsabfrage überlegen.

Wie ihr seht haben wir also sehr viel vor ^^


Was danach kommt wage ich nicht zu sagen. Ich
denke aber es währe ein kleines zusammenhängendes Spiel und ein paar
Diskussionen darüber wie man ein Spiel so programmiert das viele nicht-Coder
dran arbeiten können ohne den Quelltext und die Compiler installiert zu haben.



Aber woher soll ich schon wissen wie das alles
geht? Ich bin ja nur ein dummer Anfänger ;)


Bis zum nächstem mal.



Quellen:

[1]: www.thepernproject.com


Downloads:

Die Funktion und Beispielcode



< vorige Seite Seite   1   nächste Seite >