Fade Map Editor. Stare narzędzie przerobione na nowo

Przerobiłem dla siebie stary, ale fajny edytor do budowania fade’ów. Stworzył go kiedyś Zielok/Miracles ale narzędzie nieco się zestarzało.

… i właśnie dodałem do repozytorium nowy, niewielki projekt: Fade Map Editor. Repozytorium możnaznaleźć tutaj:https://github.com/c64portal/c64_code/tree/main/fade_editor a sam edytor można także odpalić online tutaj:

Jest to małe narzędzie do jednej konkretnej rzeczy: ręcznego przygotowania mapy czasów dla efektu fade-in na ekranie 40 x 25. Ale po kolei…

Po co taki edytor?

W demach i grach na C64 często liczy się nie tylko to, co pojawia się na ekranie, ale też jak się pojawia. Fade-in editor pozwala zbudować wejście: obraz może wypływać od środka, od krawędzi, po przekątnej, falą, smugą, zgodnie z konturem postaci albo zupełnie nieregularnie.

Na C64 takie rzeczy są szczególnie fajne, bo efekt jest prosty w idei, i w sumie prosty do zaimplementowania dzięki mapie kolorów dla bitmap. Kilka tablic, trochę logiki koloru i nagle statyczny obrazek „ożywa”. Taki edytor przyda mi się też w innych projektach (np. mój engine do gier), bo ręczne rysowanie kolejności pojawiania się znaków daje dużo kontroli. Nie trzeba za każdym razem pisać generatora ani zgadywać, czy matematyczna funkcja da ładny efekt. Można po prostu kliknąć mapę.

Skąd pomysł?

Punktem wyjścia był stary edytor autorstwa Zieloka, opisany w release na CSDB: https://csdb.dk/release/?id=100002

To narzędzie robiło swoją robotę, ale u mnie na macOS nie chciało już sensownie działać. Do tego było napisane w Javie / Processingu, było już dość wiekowe i nie miało manuala. Ponieważ sam format danych jest bardzo prosty i nadal użyteczny, postanowiłem zrobić współczesną wersję w HTML5 i JavaScripcie.

Efekt jest praktyczny: otwieram edytor w przeglądarce, klikam pola, zapisuję plik .fade, a potem używam go w kodzie na C64.

Jak działa Fade Map Editor?

Edytor pokazuje mapę 40 x 25, czyli dokładnie tyle komórek, ile ma standardowy ekran tekstowy C64. Każda komórka przechowuje jeden bajt: numer kroku, w którym dana część ekranu ma zacząć się rozjaśniać.

Typowy workflow wygląda tak:

  1. Wczytuję obrazek jako podkład przyciskiem Load pic.
  2. Ustawiam numer fazy fade’u.
  3. Klikam lub przeciągam po komórkach, które mają zapalić się w tej fazie.
  4. Przechodzę do następnej fazy klawiszem kursora w prawo albo przyciskiem >.
  5. Maluję kolejne pola.
  6. Na końcu zapisuję wynik jako plik .fade.

Lewy przycisk myszy maluje aktualny numer fazy. Prawy przycisk myszy kasuje komórkę. Są też UndoRedo, zmiana przez << / >>, pole do wpisania dokładnej fazy, regulacja przezroczystości podkładu i wbudowany manual.

Co jest wynikiem pracy?

Wynikiem jest surowy plik binarny .fade o rozmiarze 1000 bajtów. Jeden bajt odpowiada jednej komórce ekranu:

offset = x + y * 40

Wartość 0 oznacza brak przypisanej fazy. Wartości 1..126 oznaczają moment startu fade’u dla danej komórki. Dzięki temu format jest banalny do użycia po stronie programu na C64. Nie trzeba parsować tekstu, nie ma nagłówków, nie ma metadanych. Jest tylko mapa ekranu.

Użycie w kodzie C / Oscar64

W repozytorium dodałem też kod w C dla Oscar64, który pokazuje, jak używać wygenerowanej mapy. Plik .fade jest dołączany bezpośrednio do programu:

const unsigned char FadeAnim[1000] = {
	#embed "women.fade"
};

Obrazek w formacie Koala jest podobnie rozbijany na bitmapę, screen RAM, color RAM i kolor tła:

__export const unsigned char BitmapData[8000] = {
	#embed 8000 2 "pic.prg"
};

const unsigned char KoalaScreen[1000] = {
	#embed 1000 8002 "pic.prg"
};

const unsigned char KoalaColor[1000] = {
	#embed 1000 9002 "pic.prg"
};

Sam fade polega na przechodzeniu przez kolejne progi czasowe. Dla każdej klatki kod sprawdza, które komórki powinny właśnie wejść w jeden z ośmiu kroków rozjaśniania:

for (threshold = 1; threshold < FADE_LAST_STEP; ++threshold) {
	vic.color_border = 0x00;

	fade_step_realtime(
		KoalaScreen,
		KoalaColor,
		FadeAnim,
		fadeBase,
		tablePage,
		KOALA_CELLS,
		threshold
	);

	vic.color_border = VCOL_BLACK;
}

W wariancie realtime kolor jest mapowany w locie. To czytelne i wygodne do przykładu:

void fade_step_realtime(
	const unsigned char * sourceScreen,
	const unsigned char * sourceColor,
	const unsigned char * fadeAnim,
	const unsigned char fadeBase[FADE_COLOR_STEPS][FADE_COLORS],
	const unsigned char * tablePage,
	unsigned int cellCount,
	unsigned char threshold
)
{
	unsigned int cell;

	for (cell = 0; cell < cellCount; ++cell) {
		unsigned char fadeValue = fadeAnim[cell];

		if (fadeValue >= threshold && fadeValue < threshold + FADE_COLOR_STEPS) {
			unsigned char step = tablePage[fadeValue - threshold];
			const unsigned char * colors = fadeBase[step];

			Screen[cell] = fade_map_screen_byte(sourceScreen[cell], colors);
			Color[cell] = colors[sourceColor[cell] & 0x0f];
		}
	}
}

Jest też wariant z wcześniej zbudowaną tablicą fadeTable, który może być szybszy kosztem pamięci. W praktyce można wybrać, co bardziej pasuje do danego efektu i układu pamięci.

Manual – w końcu

Poprzednie narzędzie nie miało instrukcji obsługi co mogło nieco zniechęcać. Nieco… 😉 Dodałem więc zakładkę „Manual” gdzie można przejrzeć jak używać programu.

A co z ASM?

Format .fade pozostał kompatybilny z podejściem Zieloka. To oznacza, że pliki wygenerowane nowym edytorem HTML/JS można wykorzystać nie tylko z kodem C / Oscar64 z mojego repozytorium, ale także z przykładowym kodem ASM Zieloka. (dostępne na CSDB są przykłady jak użyć jego kodu) .

Zadanie domowe: fade-out

Na razie przykład skupia się na fade-in. Naturalne zadanie domowe dla czytelników: pobawić się i zrobić fade-out 😉 Można podejść do tego na kilka sposobów:

  1. Odwrócić logikę progów czasowych.
  2. Zmienić kierunek tablic kolorów.
  3. Użyć tej samej mapy .fade, ale interpretować ją jako kolejność znikania.
  4. Dodać drugi plik .fade specjalnie dla fade-outu.

Jak powstał ten mini projekt?

Oryginalny edytor Zieloka był napisany w Javie / Processingu. Ponieważ był dostępny jako stary JAR, został zdekompilowany, a jego kod posłużył jako „dokumentacja” do zaimplementowania: rozmiaru siatki, sposobu zapisu bajtów, obsługi faz i ogólny workflow. Następnie użyłem Codexa do analizy zdekompilowanego źródła i zbudowania nowej wersji HTML5 / JavaScript. Do tej pracy użyty był model GPT-5.5 z ustawieniem medium reasoning effort. Kod w Oscar64 C powstał na bazie przykładowego kodu ASM Zieloka. Zacząłem go pisać samodzielnie, ale potem to Codex pomógł przerefaktorować go do czytelniejszej wersji w C, zachowując kompatybilność plików .fade.

Wyszło z tego małe narzędzie, które nie udaje niczego większego, niż jest. Ale robi swoją robotę: pozwala szybko zaprojektować kolejność fade’u, zapisać ją do pliku i użyć w projekcie na Commodore 64 albo Commodore 64 Ultimate.

Zobacz także