libnds #3 Kafelkowe tło

Przygotowanie

Za podstawę do napisania kafelkowego tła posłużą:

Przykład z libnds:
devkitPro/examples/nds/Graphics/Backgrounds/all_in_one

Pojedyńczy obrazek tła ze spelunky community update:

SpelunkyCommunityUpdateProject/spelunky/Sprites/Blocks/Background/sCaveBG.images/image 0.png

Program do edycji map na NDS/GBA:

http://www.tilemap.co.uk/mappy.php

Odpowiednie kroki

1

Obrazek wskazany wyżej trzeba najpierw przepuścić przez poniższą stronę:

https://online-converting.com/image/convert2bmp/

Wybieramy tryb 8 (indexed).

Po pobraniu wrzucam go podpisanego jako cavebg.bmp do folderu gfx/tilemaps. W nim napisałem następującą regułę grit (wytłumaczenie – odsyłam do docsów grit):

-p!
-gt
-gB8
-mRtpf
-mLs
-mu32
-Mw16
-Mh16

Mój Makefile (gdzie wskazuję gritowi jakie foldery ma odwiedzić) możecie sprawdzić na Githubie spelunky-ds.

2

Teraz kopiujemy odpowiednie pliki z przykładowego projektu do naszego projektu:

  • scrolling.cpp
  • TextBackgrounds.s

Do main.cpp wystarczy skopiować jedynie linie (podmieniłem argument tak aby pasował do naszego obrazka cavebg):

dmaCopy(cavebgTiles, bgGetGfxPtr(bg), sizeof(cavebgTiles));
dmaCopy(Layer256x256Map, bgGetMapPtr(bg), Layer256x256MapLen);
dmaCopy(cavebgPal, BG_PALETTE, cavebgPalLen);

3

Robimy mapę. Ściągamy edytor który podlinkowałem wyżej.

Tworzymy nową mapę przez File->New map. Korzystamy z faktu, że pojedyńczy tile w NDS jest rozmiarów 8×8, nasz obrazek to 32×32 a rozmiar mapy jaki wybraliśmy to 256×256 – będzie więc miała szerokość i wysokość 32, szerokość i wysokość tile-a 8×8 co podajemy w oknie które wyskoczy.

Importujemy obrazek .bmp który stworzyliśmy wcześniej opcją File->Import.

Rysujemy cokolwiek i eksportujemy do pliku txt przez File->Export as text, w oknie które się pojawi niczego nie zmieniamy.

W pliku txt który się wygenerował interesuje nas pierwsza tablica od góry.

Ponieważ nie do końca wiem, czy to jakaś rzecz wynikająca z C++ czy mój błąd, na razie nie kopiuję tej tablicy bezpośrednio do kodu, ale podmieniam wartości w pliku TextBackgrounds.s – inaczej nie działa.

Miejsce które trzeba podmienić, to Layer256x256Map (ponieważ taki rozmiar mapy wybraliśmy). Miejsce to deklaruje wygląd mapy.

Screenshot from 2018-02-25 17-50-58

Wyrzucamy wszystko co jest po Layer256x256Map i jest przed:

@}}BLOCK(Layer256x256)

Teraz ważna rzecz – nie wklejamy tu bezpośrednio tej tablicy. Przepuszczamy ją przez prosty program który napisałem (zmienia tablicę 2d na 1d, zmienia wartości z decymalnych na hexy + dodaje “.hword” na początku każdej linii) wklejamy dopiero output, który wypuścił ten program.

Wrzuciłem jego source do Githuba projektu, do folderu utils. Przed uruchomieniem trzeba wkleić swoją własną mapę do jego kodu.

Wynik

Mamy scrollowalną mapę jak poniżej:

myimage

Co dalej

Zastanawiałem się więc w którą stronę pójść – wyświetlać jako tiled background jedynie te faktycznie statyczne elementy (tło które jest za niszczalnymi elementami), wyszczególnione tutaj:

background

…a zniszczalne elementy wyswietalać jako sprajty. Wtedy logika wyświetlania zniszczonych elementów byłaby prostsza, zero kombinowania, tak samo sprawdzanie kolizji, np. z kolcami czy podmiana sprajta, jeśli np. ten element wybuchł. Plus wszystkie wartości pomocnicze trzymane byłyby w jednej strukturze.

Pytanie tylko, czy wyświetlanie tego w ten sposób jest dobre – NintendoDS ma rzekomo oddzielny układ odpowiedzialny za wyświetlanie backgroundów, nie obciążałbym więc CPU, po prostu kopiowałbym pamięć do odpowiedniego rejestru przez Direct Memory Access, gdybym nie używał sprajtów tylko wyłącznie background. Być może NDS ma też oddzielny układ do wyświetlania sprajtów i nie musiałbym zastanawiać się jak będzie to wyglądać wydajnościowo – problem w tym:

On the Nintendo DS, we can have up to 128 sprites.

https://patater.com/files/projects/manual/manual.html#id2614195

Więc raczej by to nie wyszło, mapa w Spelunky to około 40×40, nie mówiąc o postaciach czy przeszkodach.

Size was one of the benefits of using tilemaps, speed was another. The rendering of tilemaps in done in hardware and if you’ve ever played PC games in hardware and software modes, you’ll know that hardware is good. Another nice point is that scrolling is done in hardware too. Instead of redrawing the whole scene, you just have to enter some coordinates in the right registers.

https://www.coranac.com/tonc/text/regbg.htm

Drugi pomysł, wykorzystujący w 100% tylko backgroundy, to napisanie pomocnicznego programu, który przejdzie przez plik txt z generatora map i stworzy tablicę elementów (powiedzmy, że nazwiemy te elementy SpriteElement {x, y, typ elementu (zwykły klocek, klocek tła, itd), boolan czyZniszczony, szerokość, wysokość, wskaźnik na tablicę intów który będzie wskaźnikiem na hexy ekwiwalentu tego samego tile-a, ale zniszczonego}).

Wtedy za każdą zmianą takiego kawałka tła (np. zniszczeniem pojedyńczego klocka) będzie można robić wpis do rejestru ze zmienioną tablicą i tło nadal będzie się jakoś wyglądać – to które to będą element listy i na jaką wartość ją podmienić będzie wiadomo ze SpriteElement. Kolizje i odpowiednia logika również wyszłaby z tego.

Plus, i tak muszę zrobić coś co będzie w stanie wygenerować mapę od zera (w Spelunky każda mapa jest losowa, generowana proceduralnie) więc jakieś makra pod poszczególne kafelki będę musiał gdzieś w kodzie zadeklarować.

 

Wkrótce napiszę jakąś wstępną implementację.

Efekt powyższego tutoriala można zobaczyć na Githubie tego projektu:

https://github.com/dbeef/spelunky-ds

 

PS: Zauważyłem, że kolejne hexy: 0x0001, 0x0002, 0x0003…0x000F to kolejne elementy (8×8 tile) naszego obrazka .bmp. Składa się to w całość – gdyby ułożyć je warstwami po 4, narysowalibyśmy nasz obrazek. Można w ramach eksperymentu wkleić coś takiego do pliku TextBackgrounds.s:

.hword 0x0000,0x0001,0x0002,0x0003,0x0000,0x0000,0x0000,0x0000
.hword 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
.hword 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
.hword 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
.hword 0x0004,0x0005,0x0006,0x0007,0x0000,0x0000,0x0000,0x0000
.hword 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
.hword 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000


.hword 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
.hword 0x0008,0x0009,0x000A,0x000B,0x0000,0x0000,0x0000,0x0000
.hword 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
.hword 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
.hword 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
.hword 0x000C,0x000D,0x000E,0x000F,0x0000,0x0000,0x0000,0x0000

…powinno narysować klocek + powtarzający się, lewy górny róg klocka wokół.

 


Przydatne linki:

libnds

https://www.coranac.com/tonc/text/objbg.htm

https://www.coranac.com/tonc/text/regbg.htm

https://gbatemp.net/threads/ds-programming-for-newbies.322106/

https://gbatemp.net/threads/tile-mapping-libnds-or-palib.325728/

C++

https://stackoverflow.com/questions/1238613/what-is-the-difference-between-the-dot-operator-and-in-c

https://stackoverflow.com/questions/54585/when-should-you-use-a-class-vs-a-struct-in-c

https://stackoverflow.com/questions/38942013/declaring-variables-in-header-files-c

https://stackoverflow.com/questions/10198046/c-member-variables

https://stackoverflow.com/questions/185844/initializing-private-static-members

https://stackoverflow.com/questions/20019441/how-is-a-header-file-being-compiled-into-the-executable-file

Konfiguracja Code::Blocks pod NDS:

https://whatroidoes.wordpress.com/2014/01/09/nds-development-tutorial-how-to-use-devkitpro-with-codeblocks-and-debug-with-insight/

 

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s