Skip to content

animObjectsTut

extrazi edited this page Jun 25, 2023 · 5 revisions
original    original
EN     PL

M4nfo Tutorial

Pokaż pełną ramkę do łatwej nawigacji (en.)

Tutorial animacji obiektów

Introduction
W przeciwieństwie do pojazdów lub stacji (nowe) obiekty nie mają prawdziwego odpowiednika w oryginalnym TTD. Obiekty w TTDPatch i OTTD są, że tak powiem, po prostu "cukierkiem dla oka" (eye candy) . Ale nawet przy braku znaczenia gry mogą wypełnić lukę w grze, w szczególności dla "realistycznego" gracza, zwłaszcza gdy są używane razem z funkcjami związanymi z grą, takimi jak stacje i/lub budynki miejskie. Na przykład Maritime Collection (MariCo) oferuje szeroki zestaw obiektów wizualizujących infrastrukturę portową, takich jak latarnie morskie, krety, mola, budynki portowe i urządzenia do załadunku., etc.

Przykłady
Pamiętaj, że poniższe przykłady nie prowadzą do 100% kompletnych newGRFów, ale zamiast tego koncentrują się na najważniejszych cechach kodu, zamiast implementować go szczegółowo i podawać prawidłowe ID-obiektów lub odwołania do funkcji.

Przykład 1: prosta animacja obiektu
Pierwszy przykład pokaże bardzo podstawową animację, która byłaby stale zapętlona i nie używa żadnych wyzwalaczy. Bazuje na światłach kretów z zestawu MariCo. Przedmiotowy obiekt użyłby swojej animacji do wygenerowania "postaci" (aby pomóc żeglarzom odróżnić to światło od innych). Osiąga się to poprzez rozdzielenie (animowanego) "światła" od obiektu podstawowego ("latarni morskiej" w dalszej części) i pokazanie w ten sposób dwóch różnych obiektów w ciągłej pętli, albo tylko latarni morskiej, albo połączenia latarni i światła.

Definiowanie obiektu

Pierwszą rzeczą do zrobienia byłoby w ogóle zdefiniowanie obiektu. W m4nfo robi się to za pomocą funkcji defineobject() i tak jest:

 defineobject(_LIGHTS,
 	classid(MC01)
 	classname(moles)
 	objectname(molelight)
 	climate(TEMPERATE, ARCTIC, TROPIC)
 	size(1,1)
 	price(80)
 	timeframe(1.1.1880 .. 1.1.2050)
 	flags(NOBUILDONLAND, HASANIMATION)
 	callbacks(CB_TEXT)
 	anim_info(10, LOOP)
 	anim_speed(5)
 	anim_triggers(BUILT)
 	buildingheight(2)
 	numviews(4)
 )

Jak widać z treści funkcji, ID tego obiektu to "_LIGHTS" (pewna liczba zdefiniowana wcześniej), jego ID-klasy jest ustawiony na "MC01" (aby odpowiednio uwzględnić go we właściwej klasie w menu budowania obiektu), jego nazwa_klasy jest ustawiana na łańcuch, do którego odwołuje się identyfikator tekstowy "moles" , a jego nazwa obiektu na łańcuch, do którego odwołuje się "molelight" (obie definicje pominięto w tym tutorial`u, zobacz manual użytkownika ).

Ten obiekt będzie dostępny w klimacie umiarkowanym, arktycznym i subtropikalnym, ma rozmiar 1*1 , może być zbudowany za określoną cenę w ramach czasowych od 1 stycznia 1880 do 1 stycznia 2050, musi być zbudowany na wodzie, ma animacja i używa funkcji zwrotnej (cb) "dodatkowy tekst" .

Jeśli chodzi o animację, używa 10 klatek animacji w pętli, z szybkością animacji "5" , a jedynym wyzwalaczem animacji byłoby zbudowanie tego konkretnego obiektu.

Poza tym ma wysokość "2" (tj. 2 * 8px ) i występuje w 4 "widokach" , tj. 4 różnych reprezentacjach graficznych do wyboru podczas budowania.

Zapewnienie grafiki

Teraz trzeba zdefiniować prawdziwe (graficzne) sprite'y, robi się to w m4nfo w następujący sposób:

 spriteblock(
     ...
 // 8 latarni green (LIGREEN[1])
  set(
    sprite(mole.pcx 10 175 09 22 8 -1 -16)
  )
  set(
    sprite(mole.pcx 20 175 09 26 6 0 -20)
  )
  set(
    sprite(mole.pcx 28 175 09 27 8 -1 -21)
  )
  set(
    sprite(mole.pcx 38 175 09 25 12 -3 -19)
  )
  set(
    sprite(mole.pcx 52 175 09 24 10 -2 -18)
  )
  set(
    sprite(mole.pcx 64 175 09 23 4 1 -17)
  )
  set(
    sprite(mole.pcx 70 175 09 27 8 -1 -21)
  )
  set(
    sprite(mole.pcx 80 175 09 23 6 0 -17)
  )
  ...
 // animated green light (ANIMGREEN)
  set(
    sprite(mole.pcx 171 175 09 3 2 0 0) // green
  )
  ...
 )

Jak widać, użyjemy 8 różnych 'sprites' latarni morskich. Zwróć uwagę, że to nie są "widoki" , ale ten kod będzie losowo wyświetlać te 8 różnych świateł na widok.

Dodatkowo do animacji potrzebujemy zielonego światła (ANIMGREEN). I właśnie do tego służy cała procedura: TTD nie ma animowanego zielonego koloru!

W tym sprite'y graficzne

Teraz, gdy potrzebne sprite'y zostały zdefiniowane w funkcji "spriteblock", potrzebujemy środków, aby skomponować obiekt z poszczególnych 'sprites' w "kafel". Odbywa się to za pomocą funkcji "spriteset":

 //------------------------------------------------------------------
 // sprite sets with 8 lighthouses green
 //------------------------------------------------------------------
 
 // #1
 def(50) spriteset(
 	set(normal(WATER), normal(2), xyz(0,0,0), dxdydz(16,16,6)) // mole
 	set(normal(LIGREEN1), xyz(1,5,6), dxdydz(8,8,16)) // light
 )
 
 def(51) spriteset(
 	set(normal(WATER), normal(2), xyz(0,0,0), dxdydz(16,16,6)) // mole
 	set(normal(LIGREEN1), xyz(1,5,6), dxdydz(8,8,16)) // light
 	set(normal(ANIMGREEN), xyoff(3,2)) // animated light (green)
 )
 
 def(22) anim_frame(
 	ref(50) if(4, 9) // dark
 	ref(51) else
 )

To jest kod jednego (#1) z 8 obiektów, różnią się one pod względem 'sprite' światła i sekwencji animacji.

W m4nfo składanie sprite'ów w kafelki odbywa się za pomocą funkcji "spriteset", która obsługuje wszystkie subtelne sposoby definiowania kafelka w TTD, np. sprite'y definiujące własne "obwiednie" 3D lub tak zwane "sprites podrzędne", dzielące się obwiednią rodziców, zmiennokolorowe lub przezroczyste sprite'y itp.

Najpierw spójrzmy na nieanimowane światło ("podstawa" base ):

 def(50) spriteset(
 	set(normal(WATER), normal(2), xyz(0,0,0), dxdydz(16,16,6)) // mole
 	set(normal(LIGREEN1), xyz(1,5,6), dxdydz(8,8,16)) // light
 )

Przede wszystkim zwróć uwagę na funkcję "def()" . W przeciwieństwie do innych języków "wysokiego poziomu", m4nfo obsługuje ręcznie "c-ID" zwykłego nfo (za pomocą którego ustawiany jest łańcuch kontroli). W ten sposób unika się wprowadzenia nieefektywnego zbierania śmieci, a użytkownik może skorzystać z "ponownego użycia (re-usage) c-ID", co jest bardzo przydatną funkcją w zwykłym nfo, szczególnie ze względu na istniejący limit 255 identyfikatorów c-ID . (O/c , zawsze jest możliwe "etykietowanie" definicji w m4nfo i ponowne ich późniejsze użycie przez tę etykietę. OTOH, używanie zwykłych liczb jest szczególnie jasne w kontekście lokalnym, zamiast używania nieskończenie długich identyfikatorów. ..)

Przyjrzyjmy się więc temu "def(50)" . Definiuje zestaw 'sprites' składający się z dwóch "zestawów" , z których oba definiują tak zwany "sprite rodzicielski" (parent ). Pierwszy 'sprite' nadrzędny (kret, na którym zostanie umieszczona latarnia morska) używa 'sprite' TTD "wody" jako kafelka podłoża i prawdziwej liczby 'sprite' "2" (fragment kreta) z bloku 'sprite' powyżej jako kafelka budynku. Definiuje obwiednię 16 * 16 pikseli o wysokości 6 pikseli, która całkowicie zakrywa płytkę podłoża, ponieważ jej początek znajduje się na współrzędnej (0,0), patrz rysunek poniżej.

Drugim 'sprite' jest latarnia morska, którą należy umieścić na szczycie kreta, używając 'sprite' LIGREEN1 z bloku 'sprites' powyżej. Ten zestaw definiuje mniejszą ramkę ograniczającą o wymiarach 8 * 8 pikseli, umieszczoną w (1,5) na wysokości 6 pikseli, tj. Tuż nad pierwszą obwiednią.

Oba 'sprites' są obsługiwane "normalnie", dlatego jest do nich używana funkcja "normal()" . (Istnieją specjalne sposoby obsługi prawdziwych sprite'ów w TTD/m4nfo, np. Używanie firmowego koloru, dwóch firmowych kolorów lub zmiana koloru, które byłyby obsługiwane przez inne funkcje niż "normal()" .)

Cóż, to jest teraz kafelek latarni morskiej, składający się z 'sprite' wody, kawałka kreta i budynku latarni morskiej.

Teraz potrzebujemy tego samego z dodatkowym zielonym światłem i oto jest:

 def(51) spriteset(
 	set(normal(WATER), normal(2), xyz(0,0,0), dxdydz(16,16,6)) // mole
 	set(normal(LIGREEN1), xyz(1,5,6), dxdydz(8,8,16)) // light
 	set(normal(ANIMGREEN), xyoff(3,2)) // animated light (green)
 )

Pierwsze dwa zestawy są identyczne jak te z def(50), ale teraz jest trzeci. Ten jest tak zwanym "sprite podrzędnym" (child) , tj. Nie ma ramki ograniczającej, ale ma wspólny prostokąt ograniczający poprzedni 'sprite' , czyli latarnię morską. Parametr xyoff() określa przesunięcie sprite ANIMGREEN w odniesieniu do LIGREEN1: przesunięcie w x wynosi 3 piksele, a przesunięcie w y (wysokość) wynosi 2 piksele.

Głęboki powód rozdzielania 'sprites' w ten szczególny sposób polega na problemie wyświetlania kafelka w 4 kierunkach (przód x, tył x, przód, tył), co wymagałoby czterokrotnie większej liczby sprite'ów. w stanie "przesunąć" (shift) świetlny 'sprite' względem sprite'a kreta zgodnie z kierunkiem kafelka, po prostu ustawiając współrzędne xyz () w sprytny sposób.

Wprowadzająca animacja

Cóż, przejdźmy teraz do kroku animacji. Robi się to po prostu przez

 def(22) anim_frame(
 	ref(50) if(4, 9) // dark
 	ref(51) else
 )

Czy nadal pamiętasz, że animacja została zdefiniowana na 10 klatek? Cóż, funkcja anim_frame () odwołuje się do def (50) w czwartej i dziewiątej klatce oraz def(51) do wszystkich pozostałych ramek, generując zgrabny efekt wyłączania światła w klatkach 4 i 9.

Nie trzeba dodawać, że pozostałe 7 wersji świateł używa różnych "postaci" (characters) (sekwencji animacji), pomagając żeglarzom rozdzielić światła.

Tak więc w następnym kroku musimy losować te 8 lekkich wersji:

 def(14) randomrel(CONSTRUCT, 1, ref(22), ref(23), ref(24), ref(25),
 			ref(26), ref(27), ref(28), ref(29)) // green

Ta funkcja definiuje jeszcze jedną def(14) losowanie definicji def 22, 23, ..., 29 w konstrukcji, tj. Po zbudowaniu pozostaną takie same. (Obiekty nie mają już losowych wyzwalaczy).

Niestety, teraz robi się trochę za długo, ponieważ potrzebujemy tych trzech innych kierunków (a potem potrzebowalibyśmy znowu wszystkiego dla 8 czerwonych świateł, doh!), Ale pominiemy to tutaj, ponieważ nie wprowadza nic nowego, z wyjątkiem różnych sprite'ów i przesunięć x/y.

W każdym razie ostatecznie kończymy na tej części kodu:

 //------------------------------------------------------------------
 // mole straight in x with lighthouse green
 //------------------------------------------------------------------
 
 // mole in x, find end of straight line
 def(40) objinfo_water(pos(-1,0), // back
 	ref(14) if(ENDMOLE)      // lighthouse
 	ref(0) else 
 )
 
 def(41) objinfo_water(pos(1,0), // front
 	ref(16) if(ENDMOLE)     // lighthouse
 	ref(40) else 
 )
 
 def(42) objinfo_slope( 
 	ref(10) if(WEST+SOUTH) // slope back
 	ref(12) if(NORTH+EAST) // slope front
 	ref(41) else 	       // flat, check for end tile
 )
 
 def(50) callback(
 	animcontrol(0) if(CB_ACONTROL) // start animation
 	ref(42) else
 )

Pamiętaj, że nfo (i m4nfo) cofają się (backwards) od swojej "action3" (funkcja m4nfo: "makeobject()" ), więc spójrzmy najpierw na def(50) . Jej funkcja "callback()" sprawdza, czy callback CB_ACONTROL (kontrola animacji) jest aktywny i w przypadku, gdy rozpoczyna animację od klatki 0. W przypadku, gdy nie jest to CB_ACONTROL, funkcja rozgałęzia się do def(42) .

Teraz funkcja def(42) sprawdza potencjalne nachylenie (np. Podczas budowania tego obiektu na kafelku wybrzeża). Ponieważ ta część kodu dotyczy tylko kierunku x, wystarczy sprawdzić jedno nachylenie tylne (ZACHÓD+POŁUDNIE) lub jedno nachylenie przednie (PÓŁNOC+WSCHÓD). Na takich zboczach nie można budować kretów, więc te gałęzie są pozostawione do specjalnego budowania kretów na kafelkach wybrzeża (odpowiedzi 10 i 11).

Tak więc, tylko jeśli nie ma tylnego ani przedniego nachylenia, kontynuujemy def(41) . Na szczęście nie musimy szukać płaskiej (wodnej lub lądowej) płytki, ponieważ krety możemy budować tylko na wodzie. Więc następną rzeczą do sprawdzenia byłby wolny koniec kreta (tj. Płytka z czystą wodą), ponieważ chcielibyśmy pozwolić na zbudowanie światła kreta tylko na końcu kreta. Odbywa się to poprzez sprawdzenie sąsiadujących kafelków z przodu iz tyłu kreta pod kątem "wody" za pomocą funkcji "objinfo_water()": Najpierw sprawdzamy ewentualną płytkę z wodą znajdującą się przed kretem ("pos(1,0)"), a następnie z tyłu ("pos(-1,0)"). W zależności od tego kierunku (+1 in x == front, or -1 in x == tył back) rozgałęziamy się do animowanego światła kreta, tj. def(14) z góry otrzymuje odniesienia dla kierunku wstecznego.

Jeśli nie zostanie znaleziony żaden kafelek wody ("ref(0)"), budowany jest normalny prosty kret w kierunku x.

Teraz znowu pominęliśmy trzy inne możliwości, a mianowicie zielone światło w kierunku y i czerwone światła w kierunku x i y. Wyobraź sobie, że te uzyskały def. 51, 52 i 53, a teraz dobiegamy końca całego ćwiczenia:

 //------------------------------------------------------------------
 // make 4 "views" (green light in x and y, red light in x and y)
 //------------------------------------------------------------------
 
 // graphics
 def(60) getviews(
 	ref(50) if(0) // green light in x (this is our test case)
 	ref(51) if(1) // green light in y
 	ref(52) if(2) // red light in x
 	ref(53) else  // red light in y
 )
 
 // menu
 def(61) getviews(
 	ref(36) if(0) // green light in x
 	ref(37) if(1) // green light in y
 	ref(38) if(2) // red light in x
 	ref(39) else  // red light in y
 ) 
 
 def(62) callback(
 	reftxtcb(warn_water) if(CB_TEXT)
 	ref(61) else
 )
 
 makeobject(_LIGHTS,
 	link(ref(62),MENU)
 	default(ref(60))
 )

Po pierwsze, def(60) ustawia 4 "widoki" , które można wybrać z menu budowania. def(50) to zielone światło w x, z którym przybyliśmy dzisiaj, i oczywiście pozostałe trzy warianty są powiązane z pozostałymi trzema widokami.

Teraz to samo jest zrobione z 'sprites' menu, które zostały ustawione gdzie indziej (def. 36 .. 39).

Skończmy więc obiekt: Kiedy w menu budowania (MENU), sterowanie jest przenoszone do def(62) sprawdzania dodatkowego wywołania zwrotnego CB_TEXT (wyświetlającego tekst pomocy) lub do sprite'ów menu; a jeśli nie jest w menu budowania, sterowanie jest przekazywane bezpośrednio do def(60) .

Tak, to był pierwszy tutorial animacji, łatwy. Gratulacje! Wkrótce przejdziemy do czegoś bardziej wymagającego. Bądźcie czujni.