W tej serii na warsztat chcę wziąć istniejącą grę Spelunky i przepisać ją na konsolę Nintendo DS.
W pierwszym poście chcę jedynie pokazać możliwości a nie wprowadzić od podstaw do libnds – to być może przedstawię w innych postach (pisanie na dsa wydaje mi się dobrym wprowadzeniem do programowania na systemach embedded), tymczasem poniższe jest ciekawostką.
Przedstawienie Spelunky
Spelunky to darmowa gra napisana przez Dereka Yu w 2009 (która dorobiła się kontynuacji w 2012) z gatunku cave-exploration.
Spelunky is a cave exploration / treasure-hunting game inspired by classic platform games and roguelikes, where the goal is to grab as much treasure from the cave as possible. Every time you play the cave’s layout will be different. Use your wits, your reflexes, and the items available to you to survive and go ever deeper! Perhaps at the end you may find what you’re looking for…
Można ją pobrać ze strony autora, tutaj:
http://www.derekyu.com/games/spelunky_1_1.zip
Wydanie Spelunky przypadło na moje lekcje informatyki w gimnazjum (R.I.P), nie miało konkurencji przez to że waży tylko 9.5 MB, nie wymaga instalacji i działa bez problemu przez Wine.
Co istotne, Derek Yu udostępnił kod i inne zasoby gry na poniższej licencji;
http://www.spelunkyworld.com/files/COPYING.txt
…więc jest to świetny cel do przeniesienia na inną platformę (pomijając fakt że Spelunky zrobione są w Game Makerze i wydobycie czegoś z udostępnionych plików .gmk wymaga posiadania wymienionego).
Gameplay z komentarzem:
Wydobycie grafik
Znalezienie assetów oryginalnych Spelunków zacząłem nie od Game Makera ale od Githuba (z przeczucia że ktoś już próbował ruszyć temat).
To co znalazłem, to:
- Oryginalne Spelunky przeniesione do HTML5
- Generator poziomów Spelunky oparty o kod gry:
- Wersję community Spelunky (!!!)
Właśnie to ostatnie było tym czego szukałem, skatagolowane pliki gry z odpowiednimi podpisami i kodem. Miałem już wszystko co potrzebne.
Przedstawienie libnds
Libnds wraz z przykładami dostarczany jest wraz z projektem devkitPro który zbiera wszystkie narzędzia potrzebne do pisania amatorskich programów na ds-a.
Do zautomatyzowanej instalacji devkitPro na linuksie udostępniony jest skrypt który wystarczy uruchomić interpreterem perla:
https://sourceforge.net/projects/devkitpro/files/Automated%20Installer/devkitARMupdate.pl/download
Po całej operacji brakuje tylko jednej rzeczy – emulatora NintendoDS – tutaj zdaje się że najlepszą opcją jest desmume.
Po przejściu do ~/devkitPro/examples/nds/Graphics i pierwszym uruchomieniu narzędzia make poproszeni zostaniemy o dodanie zmiennych DEVKITPRO i DEVKITARM do /etc/environment.
Po drugim uruchomieniu zbudowane zostaną pliki .nds, które będzie można włączyć emulatorem. Np. ~/devkitPro/examples/nds/Graphics/3D/3D_Both_Screens:
Zamierzam zrobić jakiś mały tutorial pisania na ds-a w następnych postach, więc daruję sobie przedstawianie szczegółów teraz. Gdyby ktoś szukał materiałów do nauki, to w kolejności od moim zdaniem najlepszych:
- Patater + wspomniane przykłady od devkitPro
https://github.com/Patater/manual
- (niedokończony) tutorial z Githuba :
https://github.com/jdriselvato/NDS-Development/tree/master/examples/Graphics
- (krótki) tutorial z czyjegoś bloga:
https://www.liranuna.com/nds-2d-tuts
- W każdym z powyższych tutoriali przydaje się wyszukiwanie detali w docsach od libnds:
http://libnds.devkitpro.org/dma_8h.html#ac77a16a7cb9f2fa2b4361c22fe84245d
- Czyjaś gotowa gra tekstowa:
https://github.com/richelbilderbeek/CityOfThieves
Użycie oryginalnej czcionki Spelunky do programu na ds-a
Najprostsze wprowadzenie jakie może być, bo polegające na wypisaniu tekstu w konsoli-konsoli. Jedyna różnica, to użycie czcionki innej niż systemowa.
Na bazie przykładu z ~/devkitPro/examples/nds/Graphics/printing/custom_font widać, że potrzebny będzie tilesheet z złączonymi wszystkimi znakami jakie potrzebujemy, aby móc pisać własną czcionką na ekranie ds-a.
W pobranym SpelunkyCommunityUpdateProject /spelunky/Sprites/sFont.images dostępne jest 59 znaków (od spacji do ‘Z’) w oddzielnych plikach ponumerowanych od 0 do 58, co odpowiada kodom znaków z tabeli ASCII przesuniętym o 32 (istotne później):
Do złączenia tych znaków w jeden plik wykorzystałem narzędzie online Stitches:
http://draeton.github.io/stitches/
Aby Stitches nie zmieniło kolejności i wygenerowało jedną kolumnę ze znakami dokładnie w tej kolejności jak w tabeli ASCII, musiałem zmienić nazwy plików ze znakami tak jak poniżej (inaczej mocno się przetasowywało), czyli co 10 plików dodając kolejną literę alfabetu:
Po całej operacji Stitches wygenerowało taki plik:
Teraz ważna rzecz:
używane grafiki, muszą przejść przez (dołączony w devkitPro) program grit, wtedy w kodzie dołączone są jako plik nagłówkowy (z obrazek.bmp powstaje obrazek.h dołączony jako #include <obrazek.h>). Wytłumaczenie z tutoriala patater:
Working with the Makefile
The default template makefile will turn your graphic files into object files for linking into your program. Never include data as a header file.
The graphics must be in a lossless image format, such as gif, tif, bmp, or png in order to work with the provided template makefile. I prefer the png graphic format. Image conversion is usually done by a program called grit. The provided template makefile will ask grit to convert images in the gfx folder of your project root to a format ready for the Nintendo DS.
The provided template makefile, adapted from the default libnds template makefile, is a good base for most all projects. It will look in a folder called gfx (in the same directory as the makefile) for your graphics. If any are found, it uses a special bin2o rule to tell grit to turn your images into .o files, according to grit rule files (with the .grit files extension), which can be linked into your program. grit will create a header file (.h) for your data. The name format for them works like so: if a file is called orangeShuttle.png the header file will be called orangeShuttle.h. Inside this header file will be a reference to the data in the .o, named orangeShuttleTiles and orangeShuttlePal or orangeShuttleBitmap, depending on how the grit file specifies which format to convert your image into. It will also include the length in bytes of the data references as orangeShuttleTilesLen and orangeShuttlePalLen or orangeShuttleBitmapLen.
For our project, we’ll be putting the our graphic files and grit rule files into the gfx directory and having the makefile use grit on them.
Jeśli teraz nie rozumiecie, to po rzuceniu okiem na kod wszystko się wyjaśni.
Otrzymany obrazek .png koniecznie konwertujemy na .bmp z 4 lub 8 bitową głębią kolorów, dowolnym narzędziem, ja wykorzystałem to:
https://online-converting.com/image/convert2bmp/
…i wybrałem opcję ‘8 (indexed)’.
Trzeba dodać ważną rzecz – jak wrzucić font w formacie bmp, aby zawierał przeźroczyste obszary tzn. znak/cyfra na przeźroczystym tle (format bmp nie wspiera kanału alfa – przeźroczystości)?
Wybieramy kolor który nie pojawia się w czionce (piksele o tym kolorze będą interpretowane jako przeźroczystość), sprawdzamy jego wartość w hexach i wrzucamy do pliku grit z przykładu. Przykład z docsów grit:
NDS 16bpp bitmap, with cyan as transparent color -gb -gB16 -gT 00FFFF
Dopisuję odpowiednią flagę wraz z wartością (209c00) i edytuję spritesheet w gimpie, zmieniając tło znaków/liter/cyfr na zielony #209C00:
Zerknijmy teraz na kod który wypisze nasz tekst na ekran ds-a:
Tutaj jak wspomniałem wcześniej, przydaje się znajomość od którego znaku ASCII zaczynamy nasz spritesheet (linia z font.asciiOffset = 32).
W powyższym kodzie pojawia się kilka makr i funkcji biblioteki nds, nie ma co przedstawiać ich tutaj wybiórczo bo potrzebny jest do tego trochę pełniejszy obraz który można znaleźć w wymienionych wcześniej tutorialach.
Całość kompilujemy i włączamy emulatorem:
Koniec części 1.
Inne
Konfiguracja ndslib pozwalająca jednocześnie wypisywać konsolę + rysować sprite-y:
Coś co mnie zastanawiało przeglądając przykłady ndslib:
Operatory binarne w cpp takie jak ‘|’ czy ‘&’ są tu co chwile używane przy zmienianiu wartości rejestrów, warto się zapoznać:
https://www.tutorialspoint.com/cplusplus/cpp_operators.htm
Parę słownikowych terminów które się pojawi:
https://stackoverflow.com/questions/4917205/interrupt-masking-why
https://pl.wikipedia.org/wiki/Direct_Memory_Access
https://pl.wikipedia.org/wiki/Przerwanie
c++:
https://stackoverflow.com/questions/2575048/arrow-operator-usage-in-c
https://stackoverflow.com/questions/33333144/using-void-as-c-equivalent-of-java-object
https://stackoverflow.com/questions/4269034/what-is-the-meaning-of-prepended-double-colon
https://stackoverflow.com/questions/4269034/what-is-the-meaning-of-prepended-double-colon
Jak działają bitmapy:
http://paulbourke.net/dataformats/bitmaps/
https://pl.wikipedia.org/wiki/Głębia_koloru