S PIS TREŚCI
O autorze .....................................................................................................................11 Wstęp . .........................................................................................................................13 Niezbędne oprogramowanie ...................................................................................................................14 Czytelnik docelowy ......................................................................................................................................15 Czy taka książka jest rzeczywiście potrzebna? ...................................................................................17 Czy w tej książce jest wszystko na temat tworzenia gier? .............................................................17 Jak należy czytać tę książkę? ....................................................................................................................18 Czy ta książka jest jeszcze aktualna? ......................................................................................................19 Dziesięcioetapowy tok pracy ...................................................................................................................19 Pliki powiązane z książką ...........................................................................................................................20
Rozdział 1. Dziesięcioetapowy tok pracy . ................................................................21 Wprowadzenie do dziesięcioetapowego toku pracy ......................................................................22 Etap 1. Burza mózgów ................................................................................................................................24 Etap 2. Projekt wstępny ..............................................................................................................................27 Zarys gry ...................................................................................................................................................28 Opis szczegółów ....................................................................................................................................29 Załączniki .................................................................................................................................................29 Etap 3. Tworzenie prototypu ....................................................................................................................29 Etap 4. Dopracowanie projektu ..............................................................................................................30 Zarządzanie projektem ..............................................................................................................................31 Zinwentaryzuj środki ...........................................................................................................................32 Zmniejsz odległości .............................................................................................................................32 Zaplanuj pracę .......................................................................................................................................33 Etap 5. Tworzenie elementów gry ..........................................................................................................33 Etap 6. Importowanie elementów gry do silnika ..............................................................................34 Etap 7. Projektowanie poziomów ...........................................................................................................36 Etap 8. Kodowanie .......................................................................................................................................37 Etap 9. Testowanie .......................................................................................................................................39 Etap 10. Budowanie .....................................................................................................................................40 Zalecenia praktyczne ..................................................................................................................................41 Podsumowanie .............................................................................................................................................42 5
6
Spis treści
Rozdział 2. Od Blendera do Unity . ............................................................................45 Konfigurowanie interfejsu Blendera ......................................................................................................46 Ciemny motyw .......................................................................................................................................46 Etykietki bez pythonowych instrukcji ...........................................................................................48 Kontrolki z programu Maya ..............................................................................................................49 „Błąd” zamykania bez zapisu ....................................................................................................................51 Przenoszenie modeli z Blendera do Unity ...........................................................................................53 Pliki .blend ...............................................................................................................................................54 Ćwiczenie: ręczny eksport do FBX ..................................................................................................55 Zawartość pliku FBX ....................................................................................................................................70 Ćwiczenie: importowanie plików FBX w Unity ..................................................................................71 Współrzędne UV mapy światła ........................................................................................................71 Współczynnik skali ...............................................................................................................................73 Podsumowanie .............................................................................................................................................74
Rozdział 3. Modułowe środowiska i siatki statyczne . .............................................77 Zalety metody modułowej .......................................................................................................................79 Rozpoczynanie prac nad środowiskiem modułowym ...................................................................79 Używanie klocka podstawowego ...........................................................................................................83 Opracowywanie modułów w Blenderze ..............................................................................................83 Odwracanie normalnych ....................................................................................................................84 Ukrywanie ścianek odwróconych tyłem . .....................................................................................85 Funkcja przyciągania ...........................................................................................................................85 N-kąty ........................................................................................................................................................88 Wyszukiwanie n-kątów .......................................................................................................................91 Cofanie operacji i usuwanie duplikatów ......................................................................................92 Mirroring ..................................................................................................................................................93 Grupowanie wierzchołków ...............................................................................................................94 Parametry wyświetlania siatki ..........................................................................................................97 Mapowanie UV i tworzenie tekstur ........................................................................................................98 Wyznaczanie szwów, mapowanie UV i modelowanie ............................................................98 Atlas tekstur i pokrywanie się współrzędnych UV ................................................................. 100 Ustalanie gęstości tekselowej . ..................................................................................................... 102 Importowanie i konfigurowanie środowisk w Unity .................................................................... 104 Stosowanie prefabrykatów . .................................................................................................................. 107 Wsad statyczny . ......................................................................................................................................... 109 Podsumowanie . ........................................................................................................................................ 109
Rozdział 4 . Teren ..................................................................................................... 111 Tworzenie terenu w Unity . .................................................................................................................... 111 Parametry terenu . ............................................................................................................................. 113 Rzeźbienie terenu . ............................................................................................................................ 114 Malowanie terenu teksturami . ..................................................................................................... 115 Ocena terenów generowanych przez Unity . .................................................................................. 118
Spis treści Modelowanie terenu w Blenderze . .................................................................................................... 119 Metoda edycji proporcjonalnej .................................................................................................... 121 Metoda tekstury przemieszczeń .................................................................................................. 124 Metoda rzeźbienia . ........................................................................................................................... 127 Rozdzielczość terenu . .............................................................................................................................. 130 Malowanie terenu teksturą . .................................................................................................................. 131 Wyznaczanie współrzędnych UV terenu . ................................................................................. 132 Generowanie tekstury . .................................................................................................................... 132 Malowanie w oknie UV/Image Editor . ....................................................................................... 134 Malowanie w oknie 3D View . ........................................................................................................ 138 Malowanie teksturami . ................................................................................................................... 140 Tworzenie dróg i ścieżek . ....................................................................................................................... 143 Modelowanie dróg ............................................................................................................................ 144 Podsumowanie . ........................................................................................................................................ 148
Rozdział 5 . Tok pracy animacyjnej ........................................................................ 149 Klatka kluczowa jako jednostka animacji . ....................................................................................... 150 Przygotowanie Blendera do tworzenia animacji . ......................................................................... 151 Wykorzystaj specjalny, animacyjny układ interfejsu Blendera .......................................... 152 Uważaj na automatyczne kluczowanie . .................................................................................... 152 Wstawiaj pojedyncze klatki kluczowe . ...................................................................................... 153 Długość animacji . .............................................................................................................................. 155 Eksportowanie animacji do formatu FBX . ................................................................................ 156 Praca z wieloma animacjami . ....................................................................................................... 156 Prosta animacja kluczowana — od Blendera do Unity . ............................................................. 158 Animowanie ruchu wzdłuż ścieżki i wypalanie animacji . .......................................................... 166 Miksowanie kształtów i klucze kształtu . ........................................................................................... 172 Kości i rigowanie . ...................................................................................................................................... 177 Zawsze nadawaj nazwy poszczególnym kościom . ............................................................... 178 Szkielety symetryczne a funkcja X-Axis Mirror ........................................................................ 178 Kinematyka prosta i odwrotna . .................................................................................................... 180 Kości deformowane i sterujące . ................................................................................................... 182 Eksportowanie zrigowanych postaci .......................................................................................... 182 Importowanie zrigowanych postaci do Unity ......................................................................... 184 Podsumowanie .......................................................................................................................................... 186
Rozdział 6. Obiekty, zależności i programowanie zdarzeniowe . ........................ 187 Zależności zaprogramowane . .............................................................................................................. 188 Rozwiązanie DI — projektowanie komponentowe i komunikaty ........................................... 190 Projektowanie komponentowe . .................................................................................................. 190 Komunikaty . ........................................................................................................................................ 191 Funkcja BroadcastMessage i hierarchie . .......................................................................................... 194 Wysyłanie komunikatów do wybranych obiektów ....................................................................... 195 Wysyłanie komunikatów do obiektów nadrzędnych ................................................................... 197
7
8
Spis treści System powiadomień .............................................................................................................................. 197 NotificationsManager w szczegółach ................................................................................................ 200 Singletony .................................................................................................................................................... 201 Komunikaty a obiekty aktywne ............................................................................................................ 202 Przemierzanie hierarchii obiektów ..................................................................................................... 203 Podsumowanie . ........................................................................................................................................ 204
Rozdział 7. Retopologizacja ................................................................................... 205 Siatki high-poly, czyli o dużej gęstości . ............................................................................................ 206 Siatki wysokorozdzielcze a gry czasu rzeczywistego ................................................................... 208 Retopologizacja w praktyce . ................................................................................................................ 209 Etap 1. Wymodeluj metodą pudełkową wstępną wersję obiektu ................................... 209 Etap 2. Zwiększ rozdzielczość siatki . .......................................................................................... 212 Etap 3. Rzeźbij i dziel . ....................................................................................................................... 216 Etap 4. Retopologizuj . ..................................................................................................................... 221 Dziesiątkowanie ......................................................................................................................................... 231 Podsumowanie .......................................................................................................................................... 234
Rozdział 8 . Zapisywanie stanu gry a trwałość danych . ....................................... 235 Dane trwałe . ............................................................................................................................................... 236 Preferencje gracza . ................................................................................................................................... 237 Preferencje gracza — ciąg dalszy ........................................................................................................ 238 Wybieranie danych trwałych . ............................................................................................................... 239 Pliki XML — a może JSON lub binarne . ............................................................................................ 240 Pliki JSON . ............................................................................................................................................ 241 Pliki binarne . ....................................................................................................................................... 241 Serializacja klasy . ....................................................................................................................................... 242 Przygotowanie danych do serializacji ................................................................................................ 243 Przesyłanie danych do pliku XML ........................................................................................................ 245 Odczytywanie danych z pliku XML ..................................................................................................... 246 Dodatkowe uwagi na temat klasy SaveState .................................................................................. 248 Podsumowanie .......................................................................................................................................... 249
Rozdział 9 . Wypalanie ............................................................................................ 251 Czym jest wypalanie? . ............................................................................................................................. 252 Wypalanie oświetlenia statycznego . ......................................................................................... 252 Wypalanie oświetlenia dynamicznego . .................................................................................... 252 Wypalanie nawigacji . ....................................................................................................................... 253 Przygotowanie mapowania światła w Unity ................................................................................... 255 Mapowanie światła. Rozdzielczość mapy światła . ........................................................................ 257 Tryb mapowania światła . ....................................................................................................................... 259 Oświetlenie pośrednie i okluzja otoczenia . .................................................................................... 260 Wypalanie map światła . ......................................................................................................................... 263 Wypalanie map w Blenderze . ............................................................................................................... 265
Spis treści Komponowanie renderingów w GIMP-ie . ....................................................................................... 271 Wypalanie oświetlenia dynamicznego z użyciem próbników światła ................................... 274 Wypalanie nawigacji . .............................................................................................................................. 278 Podsumowanie . ........................................................................................................................................ 285
Rozdział 10 . Unity, Blender i inne programy . ...................................................... 287 Inne programy . .......................................................................................................................................... 287 MakeHuman . .............................................................................................................................................. 288 GIMP . ............................................................................................................................................................. 288 Inkscape . ...................................................................................................................................................... 290 MyPaint i Krita . ........................................................................................................................................... 292 Synfig Studio . ............................................................................................................................................. 293 Tiled . .............................................................................................................................................................. 293 MonoDevelop . ........................................................................................................................................... 294 BMFont . ........................................................................................................................................................ 295 TexturePacker . ........................................................................................................................................... 296 LibreOffice . .................................................................................................................................................. 297 Anime Studio Pro ...................................................................................................................................... 298 Audacity ........................................................................................................................................................ 298 SFXR . .............................................................................................................................................................. 299 Podsumowanie . ........................................................................................................................................ 300
Dodatek A Inne źródła wiedzy ............................................................................... 301 Witryny internetowe ................................................................................................................................ 301 Książki . .......................................................................................................................................................... 302 Filmy wideo . ............................................................................................................................................... 302
Skorowidz . .............................................................................................................. 303
9
10
Spis treści
O AUTORZE
Alan Thorn jest niezależnym twórcą gier z ponad 12-letnim doświadczeniem w tej dziedzinie. Jest założycielem londyńskiej wytwórni Wax Lyrical Games i autorem nagradzanej wielokrotnie gry przygodowej Baron Wittard: Nemesis of Ragnarok. Jako wolny strzelec brał udział w realizacji ponad 500 projektów, wśród których były nie tylko gry, ale również symulatory, infokioski i systemy poszerzonej rzeczywistości przeznaczone dla wytwórni gier, muzeów i tematycznych parków rozrywki. Przemawiał na wielu konferencjach poświęconych tworzeniu gier i napisał kilkanaście książek o tej tematyce, m.in.: Teach Yourself Games Programming, Unity 4 Fundamentals i UDK Game Development. Obecnie pracuje nad mającą się wkrótce ukazać grą zatytułowaną Mega Bad Code. Więcej informacji o Alanie Thornie i jego firmie Wax Lyrical Games znajdziesz na stronach: www.alanthorn.net i www.waxlyricalgames.com.
11
12
O autorze
W STĘP
Tematem tej książki jest tworzenie gier komputerowych. Ma ona charakter praktyczny i holistyczny. Jest holistyczna, ponieważ dotyka niemal wszystkich dyscyplin, które bezpośrednio wiążą się z grami komputerowymi, od programowania i projektowania poziomów aż po modelowanie 3D i edycję obrazów cyfrowych. Opisuje też wiele programów, głównie Unity i Blender, ale jest w niej mowa również o GIMP-ie i innych tego typu narzędziach. Praktyczność tej książki przejawia się w dwóch aspektach: ■ Czytelnik nie tylko przyswaja przekazywaną mu wiedzę, ale dzięki zamieszczonym wskazówkom może również sprawdzić ją w praktyce. ■ Zagadnienia, programy i pomysły są prezentowane w sposób podkreślający ich rzeczywiste znaczenie dla twórców gier. Teoria jest przywoływana tylko w takim wymiarze, jaki jest konieczny do zrozumienia omawianych treści i do pełniejszego wykorzystania dostępnych narzędzi. Nie będziemy się zajmowali rozwiązywaniem abstrakcyjnych problemów matematycznych ani roztrząsaniem tego, co jest tylko potencjalnie możliwe; zostawimy to innym książkom i publikacjom. Krótko mówiąc: istotna będzie dla nas tylko ta wiedza, która może dać konkretne rezultaty praktyczne i którą można zastosować w pracy z dostępnymi obecnie narzędziami. Jest to wiedza, której możesz użyć tu i teraz, a także w najbliższej, dającej się przewidzieć przyszłości. Chcę jednak podkreślić, że mimo swojego holistycznego i praktycznego charakteru książka ta nie jest typowym poradnikiem pokazującym krok po kroku, jak stworzyć grę komputerową. Pisząc ją, zakładałem, że czytelnik ma już pewną wiedzę na temat gier i ich tworzenia, że przynajmniej ogólnie zna stosowane w tej branży narzędzia i techniki. Moim zamiarem było napisanie książki dla tych, którzy chcieliby się zająć tworzeniem gier i szukają konkretnych przykładów pokazujących, jak te wszystkie narzędzia, procedury, pomysły i projekty połączyć w jedną całość i nadać im określony realny wymiar.
13
14
Wstęp
Podręcznik użytkowania programu Unity uczy posługiwania się programem Unity, podręcznik użytkowania Blendera uczy posługiwania się Blenderem, ale ta książka powinna być traktowana jako podręcznik wyższego rzędu. Jej zadaniem jest pokazanie, jak używając wielu narzędzi, osiągnąć zamierzony rezultat. Opanowanie takiej umiejętności jest niezwykle ważne i daje swoiste poczucie pewności siebie. A przede wszystkim skłania do patrzenia na tworzenie gier jak na dziedzinę wielodyscyplinarną. Przez dostrzeganie współzależności między poszczególnymi narzędziami i technikami oraz przez nabywanie biegłości w ich stosowaniu wprowadzasz do swej pracy pewien stopień racjonalności. Ma to znaczenie nie tylko wtedy, gdy zamierzasz kierować zespołami ludzi realizującymi konkretne projekty, ale również gdy planujesz pracować w małym zespole bądź samodzielnie i musisz wszystko sam kontrolować. A zatem celem tej książki nie jest nauczenie Cię obsługi takich czy innych narzędzi bądź stosowania takich czy innych technik, lecz uczynienie z Ciebie sprawniejszego twórcy gier.
Niezbędne oprogramowanie W książce poświęconej tworzeniu gier komputerowych programy muszą grać ważną rolę. Dlatego większą jej część poświęciłem na opis niezbędnych aplikacji i sposobów ich użytkowania. Obecnie twórcy gier posługują się tak wielką liczbą rozmaitych programów, że opisanie wszystkich byłoby niemożliwe, ale i nie jest też potrzebne. Trzeba jednak dokonać selekcji. Ja wybrałem Unity i Blendera. Przyjrzymy się również, choć bardziej pobieżnie, GIMP-owi i jeszcze paru innym aplikacjom. Jeśli nie znasz tych narzędzi, zapoznaj się z nimi. Oto ich strony internetowe: ■ Unity. Jest to silnik gier komputerowych. Więcej informacji znajdziesz pod adresem: https://unity3d.com/. ■ Blender. Jest to program do modelowania 3D i animowania. Możesz go pobrać ze strony: www.blender.org. ■ GIMP. Jest to aplikacja służąca do edycji obrazów cyfrowych. Możesz ją pobrać ze strony: www.gimp.org. Dlaczego wybrałem te programy, a nie inne? Odpowiedź jest wielopłaszczyznowa: ■ Wymienione programy są całkowicie darmowe (Unity istnieje też w wersji darmowej!). Możesz więc je natychmiast pobrać i używać beż żadnych opłat. Oczywiście musisz zainwestować w komputer, połączenie internetowe i naukę obsługi tych narzędzi, ale same programy nie kosztują nic. ■ Programy te są bardzo rozbudowane i ciągle się rozwijają. Nie tylko ja uważam je za pełnowartościowe! Są używane przez wiele firm i przyczyniły się do powstania setek gier komercyjnych, freemiumowych czy darmowych. Przykładami niech będą Yo Frankie! i Dead Cyborg.
Czytelnik docelowy
■ Są to narzędzia ciągle rozwijane i doskonalone przez liczną społeczność zapaleńców. Są też nieźle udokumentowane, a to zawsze daje pewien komfort wynikający z przekonania, że jeśli podczas użytkowania napotkamy jakiś problem, to rozwiązanie będzie dostępne w sieci, bo ktoś już je znalazł i opisał. ■ Dzięki temu, że programy te są całkowicie bezpłatne, książka staje się przydatna dla szerszego grona czytelników. Używać ich może każdy niezależnie od zasobności portfela. Mój wybór nie powinien być odczytywany jako negatywna opinia o pozostałych programach. Jest wiele aplikacji komercyjnych (Photoshop, Maya, 3DS Max, Strata, ZBrush, Mudbox i inne), o których nie wspominam, ale nie dlatego, że uważam je za złe. Absolutnie nie. Pomijam je tylko z tego powodu, że ich ceny są poza zasięgiem wielu małych zespołów i niezależnych twórców gier, a to właśnie do nich przede wszystkim kieruję tę książkę. Mam nadzieję, że ta książka przekona Cię (jeśli jeszcze trzeba Cię przekonywać), że tak naprawdę znaczenie ma nastawienie samego twórcy, jego wyobraźnia, umiejętności i kreatywność, a nie narzędzia, których używa. Narzędzia mogą mieć znaczenie, gdy w grę wchodzi wygoda i szybkość pracy, a także gdy trzeba współpracować z ludźmi używającymi innych narzędzi, ale o ostatecznym wyniku i tak zdecydują Twoje umiejętności i determinacja. Największe dzieła tworzą ci, którzy mają talent i umiejętności, a przy tym potrafią posługiwać się wieloma narzędziami, zarówno tymi darmowymi, jak i komercyjnymi. Nic nie stoi na przeszkodzie, abyś za pomocą narzędzi całkowicie darmowych stworzył grę na profesjonalnym poziomie. Przez poziom profesjonalny rozumiem taki, który pozwoli produktowi zwrócić na siebie uwagę szerszej rzeszy klientów. Tak, używając takich narzędzi, możesz stworzyć grę, która będzie się sprzedawać!
Czytelnik docelowy Każda książka ma swojego czytelnika docelowego i ta wcale nie jest inna. Krótko mówiąc: jeśli myślisz o tworzeniu gier, ale nie masz pieniędzy na drogie programy i chciałbyś zobaczyć na konkretnych przykładach (ze szczegółowymi podpowiedziami i wskazówkami), jak mimo to można takie rzeczy robić, to ta książka jest prawdopodobnie dla Ciebie. Jeśli masz znakomity pomysł na grę, lubisz pracować samodzielnie i zrobisz wiele, by zrealizować swoją wizję, to tym bardziej ta książka jest dla Ciebie. Takich ludzi jest dość dużo. Są wśród nich studenci, hobbyści, niezależni twórcy, pracownicy średnich i małych firm, projektanci indywidualni, a nawet doświadczeni twórcy gier chcący poszerzać swoje umiejętności przez sięganie po nieznane jeszcze narzędzia i techniki. Gdziekolwiek jesteś w tym szerokim spektrum, na pewno dostrzeżesz i docenisz wielodyscyplinarny charakter tej książki. Jest to odzwierciedleniem wymagań, jakie są stawiane profesjonalistom przez współczesny przemysł gier komputerowych. Dużą wszechstronnością muszą się wykazać głównie twórcy pracujący w małych i średnich zespołach.
15
16
Wstęp
Tworzenie gier komputerowych jest dyscypliną techniczną obejmującą wiele rozmaitych dziedzin, takich jak: programowanie, fizyka, sztuczna inteligencja, grafika, modelowanie 3D, animacja, nagrywanie muzyki, testowanie oprogramowania itp. Dlatego w małych studiach o niewielkich budżetach szybko okazuje się, że pracownicy muszą być niemalże omnibusami. W zespołach naprawdę małych (jedno- lub dwuosobowych) wszechstronność jest wręcz konieczna. I to właśnie do takich małych i średnich zespołów kieruję tę książkę. A zatem oznacza to, że z książki tej nie dowiesz się, jak zostać „ekspertem” w jednej dziedzinie, ale raczej jak zdobyć wszechstronność — bez której niezależny twórca ma małe szanse na sukces. Książka jest o tym, jak zostać „ekspertem od wszystkiego” lub „specjalistą w każdej dziedzinie”. Nauczysz się trochę programowania, projektowania poziomów, modelowania w trzech wymiarach, animowania i wielu innych rzeczy. Nie jest to łatwe, ale musisz pójść tą ścieżką, jeśli masz zamiar uczynić tworzenie gier swoim zajęciem etatowym na dłuższy czas. Oczywiście nauka nie skończy się na lekturze tej książki. To musi być proces ustawiczny. Celem mojej książki jest jedynie pokazanie, od czego i jak zacząć tworzenie gier. Obecnie bardziej niż kiedykolwiek wcześniej do głosu dochodzą małe zespoły i twórcy indywidualni. Coraz częściej właśnie tacy wszechstronnie przygotowani indywidualiści odnoszą komercyjne sukcesy, tworząc gry na różne platformy, a zwłaszcza na komputery stacjonarne i urządzenia mobilne. Możesz być jednym z nich. Z tej książki dowiesz się, od czego i jak zacząć. Napisałem, dla kogo jest ta książka, ale czy jest ktoś, kto nie odniesie żadnej korzyści z jej przeczytania? Chociaż nie jest moim zamiarem odwodzenie kogokolwiek od czytania, wymienię dwie grupy czytelników, którzy najprawdopodobniej nie uznają tej książki za szczególnie przydatną. Pierwszą stanowią ci, którzy nigdy wcześniej nie interesowali się tworzeniem gier — nigdy nie programowali, nie zajmowali się grafiką komputerową i nawet nie zastanawiali się, jak gry działają. Z pewnością każdy kiedyś musi zacząć się uczyć, ale ta książka nie będzie chyba najlepszym źródłem umiejętności dla takich ludzi, przy jej pisaniu zakładałem bowiem, że czytelnik dysponuje już pewnym zasobem wiedzy i nie muszę wszystkiego tłumaczyć od podstaw. Mam tu na myśli elementarne rozumienie, czym jest silnik gry i jak się do niego mają te wszystkie narzędzia służące do tworzenia wizualnej strony gry, jak chociażby programy do modelowania obiektów 3D. Założyłem również pewną znajomość języków programowania (najlepiej C#, JavaScript lub C++) i obycie z elementarnymi pojęciami grafiki 3D, takimi jak wierzchołek, krawędź czy wielokąt. Jeśli nie masz takiej wiedzy, sięgnij najpierw po inne książki, jak chociażby te, które wymieniłem w dodatku A. Do drugiej grupy zaliczam tych, którzy są już twórcami gier, ale chcą zostać ekspertami i szukają wiedzy specjalistycznej z jednej tylko dziedziny, np. z programowania grafiki trójwymiarowej. Przykładowo: jeśli widzisz siebie wyłącznie jako programistę lub jedynie artystę i szukasz publikacji poświęconej tylko tej jednej dziedzinie, moja książka
Czy taka książka jest rzeczywiście potrzebna?
prawdopodobnie nie da Ci tego, czego oczekujesz. Jak już wspominałem, napisałem ją głównie z myślą o uniwersalistach, a nie specjalistach. Oczywiście tak naprawdę nie wierzę, że nie można jednego z drugim połączyć. Nie są to przecież rzeczy, które by się wzajemnie wykluczały. Możesz być specjalistą w jednej dziedzinie i jednocześnie mieć rozległą wiedzę w wielu innych obszarach. Ale wtedy z tej książki skorzystasz tylko połowicznie — jako uniwersalista.
Czy taka książka jest rzeczywiście potrzebna? Książka ta ma szczególną wartość głównie ze względu na całościowe podejście do tematu. Przy ciągle rosnącej liczbie małych zespołów i indywidualistów pragnących tworzyć gry rośnie też zapotrzebowanie na źródła wiedzy całościowej — takiej, która pozwala swobodnie operować rozmaitymi narzędziami i wykonywać najróżniejsze zadania, aby jednoosobowo lub w małej grupie stworzyć grę od początku do końca. Jeśli efektem końcowym ma być produkt rynkowy, a cały proces produkcyjny ma się zamknąć w określonych ramach czasowych i budżetowych, ludzie ci muszą opanować bogaty arsenał umiejętności z wielu różnych dziedzin. Oczywiście można taką wiedzę zdobyć, ucząc się na własnych błędach i studiując rozmaite książki czy poradniki. I rzeczywiście, część arsenału trzeba zgromadzić w ten sposób. Ale jeśli masz być uniwersalistą, ta książka może Ci bardzo pomóc. Dlaczego? Bo pisałem ją z myślą właśnie o takim odbiorcy. Omawiam w niej problemy, które napotykają twórcy gier, a które można rozwiązać pod warunkiem, że ma się kosztowne zaplecze specjalistów lub jest się osobą wszechstronnie przygotowaną. Jeśli chcesz tworzyć gry z prawdziwego zdarzenia, a nie masz licznego zespołu specjalistów, musisz sam stać się uniwersalistą — takim „szwajcarskim scyzorykiem”. W tej książce znajdziesz mnóstwo wskazówek podpowiadających, jak to osiągnąć — by móc swobodnie zmieniać tryby pracy i wykonywać to, co na danym etapie procesu produkcyjnego jest niezbędne. Pomoże Ci to zaoszczędzić mnóstwo pieniędzy i innych zasobów, bo nie będziesz musiał zatrudniać innych, żeby zrobili to, co przecież możesz zrobić sam. Poza tym, jako człowiek o wszechstronnej wiedzy i szerokim zakresie umiejętności, łatwiej znajdziesz zatrudnienie, zwłaszcza w branży gier niezależnych. Łatwiej Ci też będzie poprowadzić własny biznes w tej branży, bo będziesz doskonale wiedział, jak mają wyglądać poszczególne etapy procesu produkcyjnego i co jest potrzebne, aby wszystko przebiegało bez większych zakłóceń.
Czy w tej książce jest wszystko na temat tworzenia gier? Jeśli weźmiesz jakąkolwiek książkę o tematyce komputerowej i przejrzysz w internecie jej recenzje, zobaczysz, że pewien zarzut jest wciąż powtarzany, a wygląda to mniej więcej tak: Książka nie jest wyczerpująca. Pewne rzeczy są w niej pominięte. Wszystko, co zawiera, można za darmo znaleźć w internecie.
17
18
Wstęp
Chciałbym się od razu rozprawić z takim zarzutem w odniesieniu do mojej książki. Owszem, książka ta nie zawiera wszystkiego, co trzeba wiedzieć na temat tworzenia gier. Nie zawiera też wszystkiego, co można by powiedzieć o współpracy Blendera i Unity. Z prostej przyczyny: w żadnej książce, w żadnym filmie, kursie czy poradniku nie da się zawrzeć tego wszystkiego. Możliwości wykorzystania tych narzędzi są wprost nieograniczone, tak jak nieograniczone są możliwości posługiwania się pędzlem malarskim czy dłutem rzeźbiarskim. Nie zamierzałem napisać szczegółowej recepty na tworzenie gier, a raczej starałem się podać praktyczne wskazówki, jakie mi się nasuwają po ponad 12 latach działalności w tej branży. Każdą osobiście przetestowałem i mogę zapewnić, że wszystkie przyczyniły się wydatnie do osiągnięcia sukcesu przy produkcji gier zarówno w moich własnych firmach, jak i u moich klientów. Tak, prawie wszystkiego, co jest w tej książce (ale chyba nie zupełnie wszystkiego), można się nauczyć za darmo online. Jednak nikogo nie powinno to dziwić, ani tym bardziej nie powinno być powodem do krytykowania. Tworzenie gier jest dziedziną łączącą naukę ze sztuką. Nie jest to wcale jakaś tajemna wiedza, do której dostęp mogą mieć tylko nieliczni. Niemal wszystko, co tutaj piszę, zostało już w takiej czy innej formie powiedziane lub napisane. Wartością tej książki nie jest oryginalność tego, co napisałem, lecz sposobu, w jaki to zaprezentowałem. Możesz się tego wszystkiego nauczyć, przeglądając internet przez wiele dni, tygodni, a może nawet miesięcy, ale możesz też po prostu przeczytać tę książkę. Jej szczególna wartość bierze się stąd, że zawiera to, do czego sam, niekiedy z wielkim trudem, doszedłem. Całą swoją wiedzę zawarłem w jednej poręcznej publikacji i nadałem jej formę jak najbardziej przejrzystą, łatwo przyswajalną i zapadającą w pamięć. Nie jest to oczywiście wiedza kompletna ani tym bardziej ezoteryczna. Niemniej jednak sądzę, że dzięki tej książce Twoja nauka i praca mogą przebiegać sprawniej i szybciej.
Jak należy czytać tę książkę? Książka jest tak napisana, że można ją czytać podręcznikowo lub selektywnie. Czytelnicy podręcznikowi chcą przyswajać sobie nowe rzeczy stopniowo, jak na lekcjach w szkole, zapoznając się z każdym tematem oddzielnie. Taki czytelnik podchodzi do książki w sposób systematyczny i czyta ją od początku do końca, rozdział za rozdziałem. Natomiast zwolennik czytania selektywnego często korzysta ze skorowidza i stosuje techniki szybkiego czytania, aby jak najprędzej pozyskać wiedzę na temat, który go w danej chwili interesuje; często też wraca do tych samych miejsc, żeby zweryfikować jakieś szczegóły. Tę książkę można czytać na oba sposoby, ale moim zdaniem najwięcej pożytku odniesiesz, wybierając metodę podręcznikową. Jeśli będziesz czytał w sposób uporządkowany i systematyczny, zetkniesz się z tematami, które przy czytaniu selektywnym uszłyby
Czy ta książka jest jeszcze aktualna?
Twojej uwadze, bo może już je znasz albo uważasz za mało istotne, a które jednak mogą zawierać „perełki”, o jakich nie miałeś pojęcia. Czasami nawet pozornie drobna informacja może się okazać niezwykle cenna. Ponieważ zależy mi na tym, aby lektura tej książki dała Ci jak najwięcej korzyści, proponuję, abyś przyjął strategię podręcznikową.
Czy ta książka jest jeszcze aktualna? Prawie każda książka techniczna (jak np. ta) ma określony czas „przydatności”. Czytelnik, sięgając po nią, zazwyczaj chce się z niej jak najwięcej dowiedzieć. I chce, żeby ta wiedza była obowiązująca nie tylko tu i teraz, ale również w dającej się przewidzieć przyszłości. Zanim ta książka trafi do Twoich rąk, minie trochę czasu, a w branży gier komputerowych czas biegnie niezwykle szybko. Rodzi się więc uzasadnione pytanie, czy zawarte w niej treści będą jeszcze miały dla Ciebie jakąś wartość. Trzeba też dodać, że książka jest nie tylko o metodach pracy, ale również o programach komputerowych, a te, jak wiadomo, są ciągle rozwijane i udoskonalane. Ich twórcy ciągle dodają nowe funkcje i usprawniają istniejące. Jest zatem niemal pewne, że gdy pobierzesz najnowszą wersję opisanego przeze mnie programu, nie będzie on już dokładnie taki sam jak teraz. Jednak kwestią zasadniczą jest, czy to ma istotny wpływ na wartość wiedzy zawartej w książce. Na podstawie obserwacji czynionych w przeszłości mogę śmiało odpowiedzieć, że nie. Przecież gdyby każda modyfikacja programu obalała całą dotychczasową wiedzę, to nikt nie byłby w stanie porządnie opanować żadnego programu. Programy się zmieniają, ale te zmiany są zawsze wkomponowywane w istniejący już interfejs i stali użytkownicy jakoś radzą sobie z tym bez większych problemów. Prawdopodobnie będzie tak, że w wyniku tych zmian będziesz miał do dyspozycji więcej opcji, ustawień i kontrolek niż ja mam teraz, ale to w żaden sposób nie przeszkodzi Ci w wykonywaniu opisanych przeze mnie czynności. Co więcej, będziesz mógł przecież pójść dalej, wykorzystując funkcje, których ja jeszcze używać nie mogłem.
Dziesięcioetapowy tok pracy Książka składa się z dziesięciu rozdziałów, a każdy z nich zawiera dokładne opisy rozmaitych programów i technik stosowanych przy tworzeniu gier. Rozdziały są dość zróżnicowane pod względem treści, jednakże jest jeden temat, który przewija się we wszystkich i łączy je w spójną całość. Nazwałem go „Dziesięcioetapowy tok pracy”, a szczegółowo opisałem w rozdziale 1., który właśnie tak zatytułowałem. W tym rozdziale znajdziesz dokładny opis każdego z dziesięciu etapów, przez jakie trzeba przejść, aby pomysł zamienić w prawdziwą grę. Nie jest to żaden standard ani wymóg, ale raczej rezultat moich obserwacji poczynionych w trakcie prac nad wieloma rozmaitymi projektami w branży gier komputerowych. Oczywiście zawsze może się zdarzyć jakiś projekt czy okoliczności, które nie będą pasować do tego dziesięcioetapowego wzorca.
19
20
Wstęp
Z tym trzeba się liczyć, biorąc pod uwagę wielką różnorodność współczesnych gier. Zauważyłem jednak, że pamiętanie o tych etapach zawsze pomaga w utrzymaniu właściwego tempa prac, niezależnie od charakteru danego projektu. Do szczegółów wrócę w rozdziale 1., a tutaj chciałem tylko zasygnalizować temat i podkreślić jego znaczenie.
Pliki powiązane z książką Książka zawiera opisy programów komputerowych i ćwiczenia do samodzielnego wykonania, a więc nie mogło zabraknąć plików z niezbędnymi materiałami. Znajdziesz w nich wszystko, co jest potrzebne do przećwiczenia prezentowanych przeze mnie technik i uzyskania takich samych rezultatów, jakie pokazałem na zrzutach ekranu. Pliki pochodzą z wszystkich omawianych programów, czyli z Unity, Blendera, GIMP-a, Inkscape’a i Audacity, a zatem żeby je otworzyć, musisz mieć te programy zainstalowane. Cały zestaw plików ćwiczeniowych możesz pobrać ze strony: ftp://ftp.helion.pl/przyklady/ unible.zip.
R OZ DZ I AŁ 1
D ZIESIĘCIOETAPOWY TOK PRACY
Sztuka programowania jest sztuką porządkowania złożoności… — Edsger W. Dijkstra Po przeczytaniu tego rozdziału: ■ zrozumiesz, na czym polega dziesięcioetapowy tok pracy; ■ będziesz umiał zastosować ten tok w swoich własnych przedsięwzięciach; ■ uświadomisz sobie znaczenie planowania i projektowania; ■ będziesz w stanie stworzyć właściwą dokumentację dla swojego przedsięwzięcia; ■ będziesz umiał zarządzać produkcją gier. Tworzenie gier nie jest sprawą łatwą. Nieważne, czy gra wydaje się prosta, czy skomplikowana, zawsze jest ona rezultatem poważnego przedsięwzięcia inżynieryjnego. Musisz to sobie uświadomić, zanim sam spróbujesz jakąś stworzyć. Nie będziesz wtedy zaskoczony rozmiarem prac, jakie trzeba wykonać. A właśnie to zaskoczenie jest jedną z najczęstszych przyczyn porzucania rozpoczętych przedsięwzięć — nadmiar pracy zwykle prowadzi do zniechęcenia. Oczywiście już samo rzucenie się w wir pracy bez żadnego planu i spędzanie długich godzin i dni z kolegami lub samotnie nad grą może być ekscytującym, a nawet pouczającym doświadczeniem, bez względu na to, czy ten wysiłek zakończy się sukcesem, czy nie. Lecz jeśli poważnie myślisz o zarabianiu pieniędzy na tworzeniu gier, to musisz swoje projekty doprowadzać do końca. Żaden gracz nie da ani grosza za niekompletną grę. No, może czasem, ale nie powinieneś na to liczyć, bo takie sytuacje są naprawdę wyjątkowe. (Czasami gracze płacą za wersje alfa i za wersje rozwojowe, ale oczekują, że wkrótce otrzymają dzieło ukończone). A zatem bez doprowadzenia prac do końca nie masz co liczyć na sukces. 21
22
Rozdział 1
Dziesięcioetapowy tok pracy
Gdyby przejrzeć wszystkie projekty gier, włącznie ze „studenckimi” i z „hobbystycznymi” (nie widzę powodu, dla którego mielibyśmy je pomijać), najprawdopodobniej okazałoby się, że większość z nich nigdy nie została ukończona. Prace nad nimi zostały zamrożone, wstrzymane lub przerwane. Musisz więc sprawę doprowadzenia prac do końca potraktować bardzo poważnie. Jak to osiągnąć? Przede wszystkim przez dokładne poznanie wszelkich faz, przez jakie gra musi przejść w całym procesie produkcyjnym. Pomaga to w uzyskaniu klarownego obrazu wszystkich prac, jakie trzeba wykonać na drodze od pomysłu do finalnego produktu. Być może brzmi to zbyt naukowo i akademicko. Pewnie tak. Jednak implikacje praktyczne takiej wiedzy są trudne do przecenienia. To one decydują, czy przedsięwzięcie jest udane, czy nie. Dlatego w tym rozdziale przyjrzymy się dokładnie podstawowym fazom procesu tworzenia gry i ich implikacjom. Z grubsza cały ten proces można podzielić na dziesięć etapów. Nie jest to podział ustandaryzowany ani nawet „branżowo uzgodniony” i o ile mi wiadomo, nie został jeszcze w żadnej książce opisany. Po prostu istnienie takich właśnie faz stwierdziłem, uczestnicząc przez ostatnie 12 lat w realizacji ponad 500 projektów. Oczywiście zawsze może się zdarzyć jakiś przypadek wyjątkowy, który nie będzie pasował do przedstawionego przeze mnie wzorca — niewykluczone, że będzie to akurat Twoja gra. Jednakże opisany przeze mnie tok pracy jest tak ogólny i abstrakcyjny, że z powodzeniem można go zastosować w większości przypadków. Uwa ga
Ponieważ w książce prezentuję mnóstwo praktycznych wskazówek i porad, ktoś może odnieść wrażenie, że promuję swój tok pracy i swoje pomysły jako najlepsze i jedynie słuszne. Naturalnie jest to nieprawda. Nie istnieje jeden, „najlepszy” tok pracy. Ten sam cel można osiągnąć, podążając różnymi ścieżkami. I tak też uważam, ale w książce postanowiłem skupić się na jednym rozwiązaniu i wybrałem to, które w mojej praktyce sprawdziło się wielokrotnie.
Wprowadzenie do dziesięcioetapowego toku pracy Wyobraź sobie proces tworzenia gry jako oś czasu skierowaną od lewej do prawej, z góry na dół lub po prostu od początku do końca. Opisywany przeze mnie tok pracy wymaga wprowadzenia podziału tej osi na dziesięć segmentów. Zazwyczaj będą one różnej długości, bo czas trwania poszczególnych etapów może być przecież zróżnicowany — jedne prace można wykonać szybciej, innych nie da się przyspieszyć. Na rysunku 1.1 jest pokazana taka linia czasu z dziesięcioma fazami, włącznie z projektowymi. W sumie cały tok pracy składa się z następujących etapów: ■ burza mózgów, ■ projekt wstępny, ■ tworzenie prototypu,
Wprowadzenie do dziesięcioetapowego toku pracy
Rysunek 1.1. Dziesięcioetapowy tok pracy dzieli czas tworzenia gry na dziesięć etapów o niekoniecznie jednakowej długości
■ dopracowanie projektu, ■ tworzenie elementów gry, ■ importowanie elementów gry do silnika, ■ projektowanie poziomów, ■ kodowanie, ■ testowanie, ■ budowanie.
23
24
Rozdział 1
Dziesięcioetapowy tok pracy
Tok zaczyna się od pomysłu, a kończy na uzyskaniu kompletnego produktu. Zazwyczaj poszczególne etapy są realizowane po kolei, czyli następny zaczyna się po zakończeniu poprzedniego. Niektórzy nazywają to metodą kaskadową ze względu na podobieństwo do wody spadającej z kolejnych progów. Pomińmy jednak tę wodną analogię i skupmy się na poszczególnych etapach tworzenia gry. Uwa ga
Czytelnicy obeznani z systemem projektowania zwinnego (agile) mogą poczuć się skrępowani tym dziesięcioetapowym tokiem pracy z powodu jego liniowej natury (zob. rysunek 1.1). Agile zachęca twórców do stosowania podejścia iteracyjnego, a nie liniowego. Jednakże nic nie stoi na przeszkodzie, aby każdą iterację czy cykl w systemie agile traktować jako inicjację nowego dziesięcioetapowego toku pracy. W dziesięcioetapowym toku pracy każdy etap można traktować jako pewien moduł aktywności. Ma on punkty wejściowe i wyjściowe. W punkcie wejściowym przyjmuje rezultat wygenerowany przez wszystkie poprzednie etapy, a do punktu wyjściowego przekazuje to, co zostało wytworzone w bieżącej fazie produkcji. Co konkretnie dzieje się pomiędzy tymi dwoma punktami, zależy od konkretnej gry i konkretnego studia produkcyjnego. Nie jest więc takie ważne, co dzieje się wewnątrz modułu, byleby na jego wyjściu pojawiło się dokładnie to, co powinno. A teraz przejdźmy do bardziej szczegółowego omówienia poszczególnych etapów.
Etap 1. Burza mózgów Jeśli już zdecydujesz się stworzyć grę, powinieneś zacząć od burzy mózgów. Każdy twórca gier przechodzi przez tę fazę i zawsze jest to faza pierwsza. Burza mózgów oznacza głównie kreowanie nowych pomysłów, ale nie tylko. Chodzi nam także o znalezienie powiązań między pomysłami. Celem tej fazy jest wygenerowanie fizycznego lub mentalnego obrazu gry, którą chcemy stworzyć. Uwa ga
Burzę mózgów — podobnie jak i pozostałe etapy — można przeprowadzić na wiele sposobów. Ważny jest tylko efekt końcowy. Powinien być zgodny z naszymi intencjami. Jeśli tak nie jest, należy ten etap powtórzyć z odpowiednimi zmianami. Postępujemy tak dotąd, aż uzyskamy to, o co nam chodziło. Nie postępuj na tym etapie zbyt pochopnie. Bardzo łatwo o błąd przy ocenie pomysłu, który na pozór wydaje się klarowniejszy niż jest w rzeczywistości. Stare indiańskie przysłowie mówi: „Nigdy nie znasz siebie tak dobrze, jak ci się wydaje”. Dlatego podczas burzy mózgów zapisuj każdy pomysł, bo już sama próba ujęcia go w krótką, ale treściwą notatkę może znacząco ułatwić jego właściwą ocenę. Przykładowo: załóżmy, że zamierzasz stworzyć grę fabularną (RPG) i masz już pewne pomysły, jak gra ma przebiegać, jakie mają być postacie i jak mają wyglądać miasta,
Etap 1. Burza mózgów
w których będzie się toczyła akcja. Nie masz jeszcze tylko sprecyzowanych szczegółów wyglądu tych miast i postaci (nic nie szkodzi). I prawdopodobnie nie wiesz też, na jakiej platformie ta gra ma działać, jak dużo jej egzemplarzy uda się sprzedać i czy będzie przeznaczona dla jednego gracza, czy dla wielu. Odpowiedzi na tego typu pytania są niezwykle istotne, bo od nich w dużej mierze zależy implementacja gry i harmonogram jej opracowania. Gry przeznaczone na urządzenia mobilne muszą mieć określoną rozdzielczość i ograniczone wymagania sprzętowe, a w przypadku gier kierowanych do użytkowników konsol i komputerów ograniczenia te są dużo łagodniejsze. Z kolei w grach wieloosobowych występują ograniczenia co do liczby obiektów i postaci, które mogą jednocześnie wchodzić we wzajemne interakcje. Tego typu ograniczenia techniczne wyznaczają ramy, w obrębie których możesz się poruszać, i musisz je poznać, zanim zaczniesz coś konkretnego projektować. Etap burzy mózgów jest do tego najlepszym momentem. Poniżej przedstawiam listę najważniejszych zagadnień, które powinno się rozstrzygnąć na etapie burzy mózgów. Zauważ, że niektóre z nich nie będą miały zastosowania w przypadku gier o określonej specyfice. Na przykład gry sportowe na ogół nie wymagają żadnej obudowy fabularnej ani listy głównych bohaterów. Lecz jeśli jakieś zagadnienie jest istotne dla Twojej gry i nie znasz odpowiedzi na wynikające z niego pytania, to najwyraźniej nie zakończyłeś jeszcze etapu burzy mózgów. Nie przechodź do następnej fazy, zanim wszystkiego nie ustalisz. ■ Tytuł ostateczny lub roboczy. Zanotuj ostateczny tytuł gry (jeśli go już znasz) lub chociaż roboczy, którego będzie można używać, dopóki nie zostanie ustalony ostateczny. Jakikolwiek tytuł będzie potrzebny, żebyś mógł się odnosić do swojej gry na blogach i w mediach, a także by móc rozpocząć przygotowywanie materiałów marketingowych. ■ Gatunek. Może to być jeden z już istniejących gatunków gier albo zupełnie nowy. Może też być typ mieszany (hybrydowy). Spośród istniejących gatunków najpopularniejsze są gry: RPG, strategiczne, symulatorowe, sportowe, przygodowe, platformowe, FPS, MMORPG i casualowe. Uwa ga
W grach wideo typy są zdefiniowane bardzo luźno (jeśli w ogóle). Zazwyczaj w grach RPG, takich jak Skyrim czy Diablo, gracz musi sterować grupą postaci, które eksplorują fantazyjny świat pełen rozmaitych stworów, rozwiązują zagadki i podejmują wojownicze wyprawy. W grach akcji typu FPS, takich jak Quake, Doom czy Duke Nukem, gracz steruje działaniami bohaterów, którzy krążą po polach bitwy, bazach kosmitów lub kompleksach wojskowych wrogich armii i strzelają do wszystkiego, co mają w zasięgu wzroku. Inaczej jest w grach przygodowych, takich jak Myst czy Monkey Island, które mają mocną podbudowę fabularną, zawierają mnóstwo dialogów i wymagają rozwiązywania licznych zagadek. Krótko mówiąc: świat gier jest niezwykle różnorodny, a liczba gatunków rośnie z każdym rokiem wraz z pojawianiem się nowych możliwości technicznych.
25
26
Rozdział 1
Dziesięcioetapowy tok pracy
■ Sceneria, postacie i fabularna otoczka. Przynajmniej w zarysach nakreśl fabułę gry, opisz jej scenerię i scharakteryzuj bohaterów. Nie musisz na razie podawać takich szczegółów jak pochodzenie każdej postaci czy dokładna mapa każdego miejsca akcji; takie detale określisz i dopracujesz w fazie następnej (przy opracowywaniu projektu wstępnego). ■ Platformy. Są to systemy komputerowe, na których gracze będą uruchamiali Twoją grę. Mogą być następujące: Windows, Mac, Linux, Android, iOS, Windows Phone OS, BlackBerry, Web, Flash i konsole. Wybór nie jest łatwy. Każdy system narzuca własne standardy odnośnie do wymagań sprzętowych, wydajności i dostępności zasobów. ■ Obsługiwane rozdzielczości. Pod tym pojęciem kryją się wyrażone w pikselach wymiary okna, w którym gra będzie wyświetlana. Znajomość tych wymiarów będzie niezbędna przy projektowaniu skalowalnych elementów graficznych należących zarówno do świata gry, jak i do jej interfejsu. Sprecyzuj te parametry jak najwcześniej, aby mieć pewność, że gra będzie się dobrze prezentowała we wszystkich docelowych rozdzielczościach. ■ Model biznesowy. Określa sposób, w jaki zamierzasz sprzedawać swoją grę. Może to być system przedsprzedaży, model z darmową wersją podstawową i płatnymi dodatkami, model sponsorowany przez reklamodawców, których ogłoszenia są prezentowane użytkownikom, albo jakiś zupełnie inny system. Istnieje wiele rozwiązań i na temat ich efektywności toczy się od dawna gorąca dyskusja (ale to nie miejsce ani czas, by się do tej dyskusji włączać). Rada? Wybierz jeden lub kilka modeli — im wcześniej to zrobisz, tym lepiej. Uwa ga
Zainteresowanych zgłębieniem problematyki modeli biznesowych w branży gier komputerowych odsyłam do artykułu zamieszczonego na stronie: www.adobe.com/devnet/flashplayer/articles/right-businessmodel.html. ■ Grupa docelowa. Czy Twoja gra jest dla wszystkich, bez względu na wiek, wykształcenie, zainteresowania itp.? Czy raczej jest przeznaczona dla określonej grupy graczy, np.: lubiących gry akcji, dorosłych, młodych? Są to kolejne pytania, na które trzeba jak najwcześniej odpowiedzieć. Odpowiedzi te będą miały wpływ zarówno na to, co w tej grze umieścisz (i z niej wykluczysz), jak i na to, kto będzie chciał ją sprzedawać. Powyższa lista nie jest bynajmniej wyczerpująca. Zapewne będziesz musiał sobie odpowiedzieć na jeszcze wiele innych pytań związanych z grą, ale będą to już pytania odnoszące się do jej specyfiki. Udzielone odpowiedzi pozwolą Ci nakreślić obszar dozwolonych wartości, w obrębie którego będziesz musiał potem pracować. Wytyczone granice będą istotnymi wskazówkami szczególnie w fazach projektowania. Dzięki nim
Etap 2. Projekt wstępny
będziesz wiedział, jak daleko i w którym kierunku możesz pójść ze swoją wyobraźnią, aby nie przesadzić. W istocie rzeczy każdy spośród omawianych dziesięciu etapów wprowadza jakieś nowe ograniczenia. Im dalej postępujesz w ramach takiego toku pracy, tym bardziej zawęża się dozwolony obszar, aż w końcu osiągasz ostatni etap i gdy gra jest gotowa, nie możesz zrobić już nic więcej. Ograniczenia mogą się wydawać czymś niekorzystnym, ale tak naprawdę są one jak busola pozwalająca utrzymać raz obrany kurs. W tak złożonym przedsięwzięciu, jakim jest wyprodukowanie gry, można się czasami pogubić, ale właśnie dzięki wspomnianym ograniczeniom łatwo jest wrócić na właściwą ścieżkę. Oczywiście pod warunkiem, że będziesz je respektował! A zatem gdy zakończysz fazę burzy mózgów, potraktuj wszystkie swoje odpowiedzi bardzo poważnie.
Etap 2. Projekt wstępny Po etapie burzy mózgów przychodzi czas na sporządzenie pierwszej wersji dokumentu projektowego dla naszej gry (game design document — GDD), w którym będą uwzględnione wszystkie ustanowione do tej pory ograniczenia. Będzie on produktem wyjściowym pierwszej fazy toku pracy. Powinien zawierać (co najmniej) następujące rodzaje danych: ■ tekst, ■ diagramy, ■ szkice, ■ rysunki, ■ zdjęcia, ■ wykresy, ■ tabele, ■ listy. O dokumencie tym myśl jak o przeznaczonym jedynie do odczytu pliku PDF, w którym zamieszczono w maksymalnie skondensowanej formie opis gry ze wszystkimi istotnymi informacjami. Dokument powinien być tak sporządzony, aby ktoś, kto go przeczyta, mógł sobie dokładnie wyobrazić, jak gra będzie wyglądała. Jeśli coś takiego jest możliwe bez żadnych dodatkowych wyjaśnień, to znaczy, że dokument właściwie spełnia swoją funkcję. Jeśli jednak takie wyjaśnienia są konieczne, potraktuj je jak informację zwrotną i wykorzystaj do wprowadzenia stosownych poprawek. Konieczność udzielania dodatkowych wyjaśnień będzie oznaczała najprawdopodobniej, że w dokumencie czegoś brakuje lub coś jest niejasno sformułowane. Będziesz więc musiał takie miejsca odnaleźć i uzupełnić braki lub przeredagować zapisy.
27
28
Rozdział 1
Dziesięcioetapowy tok pracy
Etap projektowania wstępnego trwa zazwyczaj o wiele dłużej niż burza mózgów, ponieważ wymaga większej precyzji i sumienności. Mimo to nie należy jego rezultatu, czyli dokumentu GDD, traktować jako definitywnie zakończonego. To jest dopiero jego pierwsza wersja. Potem będą powstawały kolejne. Jednak na każdym etapie powinieneś tworzyć ten dokument w taki sposób, jakby to miała być jego wersja ostatnia. Wtedy będzie pewne, że jest sporządzony z należytą starannością. W praktyce GDD jest ciągle udoskonalany. Nie dlatego, że tak sobie zaplanowałeś, lecz w wyniku późniejszych spostrzeżeń (zwłaszcza po obejrzeniu prototypu), które często każą spojrzeć na istniejący projekt z nieco innej perspektywy. Nigdy nie jesteś w stanie przewidzieć, jakie zmiany będą jeszcze konieczne, więc każdą traktuj tak, jakby miała być ostatnia. Co taki dokument powinien zawierać? Mówiąc krótko: powinno w nim być wszystko, co na temat gry można na danym etapie powiedzieć. Gdyby pewnego deszczowego popołudnia ktoś zwrócił się do Ciebie z prośbą: „Opowiedz mi w końcu o tej twojej grze, ale nie pomijaj żadnych szczegółów”, wówczas mógłbyś mu podsunąć liczący kilkaset stron egzemplarz GDD. Jego zawartość będzie oczywiście zależała od konkretnej gry, bo każda gra jest inna i ma swoją specyfikę. Poniżej podaję, jakie elementy powinny się bezwzględnie znaleźć w GDD. Potraktuj to jako wskazówkę na temat samej struktury dokumentu. Otóż według mnie „idealny” dokument projektowy gry powinien składać się z trzech zasadniczych części: ■ zarysu gry, ■ opisu szczegółów, ■ załączników.
Zarys gry Bez względu na to, jaką grę stworzysz, zawsze znajdzie się ktoś, kto powie, że mu się nie podoba, mimo że ani razu w nią nie zagrał. Po prostu nie lubi „tego rodzaju gier”. Taki człowiek spojrzy tylko na wstępne informacje — wymagania systemowe, gatunek, tytuł, krótkie streszczenie fabuły — a resztę zignoruje jako rzecz niegodną uwagi. Radzę zatem umieścić w części zwanej zarysem gry wszystkie istotne szczegóły, i to w taki sposób, aby do zapoznania się z nimi nie trzeba było się specjalnie zmuszać. Po prostu ta część musi być możliwie najkrótsza. Podchodząc do dokumentu w taki sposób, zaczynasz też porządkować szczegóły gry. Zarys gry powinien zawierać informacje, które już wymieniłem (wymagania systemowe, gatunek itd.), a także to wszystko (albo prawie wszystko), co zostało ustalone na etapie burzy mózgów. Nie rozwijaj ich jednak — zrobisz to w następnej części. A zatem jeśli tworzysz grę z gatunku RPG, umieść w tym miejscu jej tytuł, docelową platformę, gatunek, krótkie streszczenie i jeszcze kilka podobnych informacji, ale nie podawaj listy
Etap 3. Tworzenie prototypu
wszystkich miejsc akcji, dostępnych rodzajów broni, wrogów, postaci ani zadań głównych i pobocznych (questów i subquestów). Jeśli jest to gra sportowa, nie wymieniaj wszystkich zawodników, regulaminów rozgrywek, stadionów i strojów. Podobne reguły zastosuj w odniesieniu do gry każdego innego gatunku.
Opis szczegółów W tej części musisz się nieco bardziej zagłębić w szczegóły gry. Po przeczytaniu tego, co napisałeś w zarysie, osoba przeglądająca dokument ma już pewne pojęcie o grze i teraz oczekuje czegoś więcej. Trzeba więc rozwinąć to, co zostało wcześniej tylko zasygnalizowane. Tutaj jest miejsce na wyszczególnienie wszystkich trybów gry, rodzajów broni, występujących postaci, zastosowanych stylów, obiektów itp. Możesz z tego zrobić całą powieść (niektórzy tak czynią), rozciągając te informacje i opisy na wiele stron, ale ja zdecydowanie zalecam nadanie temu wszystkiemu jakiejś przejrzystej struktury z systemem numerowania każdej pozycji, aby nie było problemu z identyfikacją poszczególnych elementów. Niech każda broń, każde miejsce, każda postać i każdy obiekt mają swoje nazwy i numery. Bądź systematyczny.
Załączniki Po przeczytaniu zarysu i opisu szczegółów osoba zainteresowana grą powinna wiedzieć o niej niemal wszystko. Bądź jednak przygotowany na dodatkowe pytania. W przypadku gry z gatunku RPG może paść pytanie: „A jak wygląda plan miasta X?” albo: „Czy wiadomo już, jak będzie wyglądała postać X?”. Gdy gra będzie należała do gatunku sportowych, pytania mogą brzmieć: „Jak będą wyglądały stroje drużyny X?” lub: „Jak przedstawia się tabela rozgrywek ligi X?”. Na każde takie pytanie powinieneś mieć odpowiedź w postaci odpowiednich rysunków, map, planów, zestawień, wykresów, schematów i innych tego typu materiałów. To powinno być zawartością ostatniej części głównego dokumentu gry — możesz jej nadać nazwę Suplement. Uwa ga
Jeśli stanowisz zespół jednoosobowy, nie sądź, że GDD jest Ci niepotrzebny. Dokument ten nie tylko przyda się w rozmowach na temat szczegółów gry, ale również pomoże Ci w jaśniejszym precyzowaniu i zapamiętywaniu swoich pomysłów. Dlatego każda gra powinna mieć swój główny dokument.
Etap 3. Tworzenie prototypu Prototypowanie to nic innego jak studium wykonalności. W tym momencie trzeba na podstawie GDD wykonać zgrubną, miniaturową wersję gry. Zwykle nazywa się ją prototypem. Taki prototyp jest dla gry tym czym szkic dla obrazu. Prototyp nie jest czymś, co istnieje tylko w teorii i na papierze. To jest prawdziwa gra, w którą można zagrać. Tylko wygląda topornie, nie ma dźwięków, płynnych animacji
29
30
Rozdział 1
Dziesięcioetapowy tok pracy
i żadnych efektów wizualnych. Postacie, karabiny, pistolety i inne obiekty są zastępowane szarymi prostopadłościanami lub innymi prymitywami (sferami, stożkami, ostrosłupami itp.). Zazwyczaj na tym etapie nie wykonuje się żadnych artystycznie wykończonych elementów. Pokazuje się co najwyżej puste obrazy, zapałczane ludziki i kreskówkowe stwory. Prototyp nie ma być kompletną grą, ma tylko służyć do sprawdzenia podstawowych założeń gry. Wykonanie prototypu umożliwia m.in.: ■ Oszacowanie nakładu pracy. Podczas tworzenia prototypu programiści zyskują pewną wiedzę na temat tego, co ich czeka w przyszłości. Widać też, czy główne założenia gry dają się łatwo przełożyć na kod zrozumiały dla komputera. Są więc pewne podstawy do podejmowania prób oszacowania, ile czasu zajmie pełna implementacja gry i ile to będzie kosztowało. W tym momencie można też ocenić, czy trzeba będzie poszerzyć zespół o dodatkowych fachowców i zakupić dodatkowe oprogramowanie. ■ Usunięcie istotnych błędów. Napotkałeś jakieś techniczne trudności przy sporządzaniu prototypu? Jeśli tak, to jakie z tego wnioski wyciągnąłeś? Czy te trudności wynikały ze specyfiki projektu, czy po prostu z niewłaściwego spojrzenia na ten projekt? Pytania takie jak te pomogą Ci też w spojrzeniu na grę jak na projekt inżynieryjny i przewidzeniu potencjalnych problemów, zanim te ujawnią się w gotowym produkcie. Będziesz miał więc czas, żeby je zawczasu rozwiązać. Niekiedy wystarczy w tym celu spojrzeć na całość z nieco innej perspektywy i wprowadzić drobne poprawki, ale czasami trzeba też pewne rzeczy z projektu usunąć lub całkowicie przeprojektować. ■ Wizualizację gry. Prototyp pozwala pokazać innym ludziom, na czym gra będzie polegała. Teoretycznie powinien do tego wystarczyć główny dokument gry, ale teoria nie zawsze przekłada się na praktykę. Niektórzy ludzie po prostu nie lubią czytać tekstów technicznych, inni nie mają na to czasu, a jeszcze inni twierdzą, że wyczytanych informacji nie pamiętają. Dla takich ludzi prototyp może być doskonałym sposobem na zapoznanie się z projektem gry. Może też być źródłem inspiracji i nowych przemyśleń — pożywką dla pomysłów i eksperymentów myślowych, które były zbyt mgliste i trudne do sprecyzowania, gdy projekt był jedynie na papierze. To właśnie dopiero przy oglądaniu prototypu często słychać słowa: „No tak, teraz, gdy widzę, jak to wszystko działa, sądzę, że tę grę można by jeszcze wzbogacić o kilka nowych funkcji”.
Etap 4. Dopracowanie projektu Prototypowanie jest fazą praktycznego testu dla GDD. Rezultatem tamtego etapu jest lista sugestii, poprawek, pomysłów, uzupełnień i udoskonaleń. Celem czwartego etapu jest wcielenie tych wszystkich sugestii, poprawek, pomysłów, uzupełnień i udoskonaleń do projektu, czyli uwzględnienie ich w głównym dokumencie gry. W praktyce zazwyczaj wykonuje się to w procesie iteracyjnym złożonym z następujących czynności:
Zarządzanie projektem
1. Przemyślenie zmiany. 2. Wprowadzenie jej do dokumentu. 3. Ocena skutków.
Jeśli zmiana jest niezadowalająca, powtarzasz proces, aż otrzymasz coś satysfakcjonującego. Postępujesz tak, dopóki nie uzyskasz kompletnej dokumentacji finalnego projektu. Teoretycznie na wyjściu tego etapu powinien pojawić się dokument ostateczny i niezmienny — taki, którego modyfikacja byłaby i niemożliwa, i niepotrzebna. W nim powinno być nieodwołalnie rozstrzygnięte, jak gra ma wyglądać i działać. Dokument ten powinien być stałym punktem odniesienia i nie powinien być zmieniany na późniejszych etapach. Zauważyłem jednak, że w praktyce utrzymanie tej niezmienności jest bardzo trudne. Życie zawsze sprawiało mi jakieś niespodzianki i zmuszało do modyfikowania projektu. Czasami było to związane z opóźnieniami w pracach niektórych zespołów i trzeba było decydować o rezygnacji z jakiegoś elementu gry, aby zmniejszyć niezbędne nakłady pracy. Zdarzało się też, że twórcy nagle wpadali na jakiś pomysł i koniecznie chcieli go zaimplementować. Tego typu scenariusze nie są wcale rzadkością i dlatego trzeba się zawsze liczyć z tym, że projekt może być jeszcze zmieniony. Zmiany same w sobie nie są niczym złym, ale mogą też być niebezpieczne, bo zmodyfikowany projekt może okazać się częściowo lub nawet całkowicie niefunkcjonalny. Dlatego jeśli kiedykolwiek staniesz przed koniecznością dokonania zmian w projekcie już po jego dopracowaniu, sprawdź najpierw, jakie będą skutki takiego posunięcia. Nie twierdzę, że nie powinieneś się nigdy zgadzać na jakiekolwiek zmiany, ale podchodź do takich propozycji z rezerwą, a nawet niechęcią. Na przykład zmiana gry przygodowej na grę akcji już po opracowaniu większości elementów z pewnością nie byłaby najmądrzejszym posunięciem, ale wydłużenie gry przez dodanie nowych postaci i miejsc może być całkiem rozsądnym krokiem, zwłaszcza jeśli w aktualnym stanie gra wydaje się rzeczywiście zbyt krótka, a zaproponowane zmiany dadzą się zaimplementować bez większych problemów.
Zarządzanie projektem Dotąd wszystkie etapy opisywałem po kolei, tak jakby tworzenie gry było procesem całkowicie liniowym i jednakowym dla wszystkich. Jednakże już po zakończeniu etapu czwartego proces może przebiegać rozmaicie, w zależności od struktury zespołu wykonawczego. Jeśli jesteś sam, to zapewne będziesz realizował wszystkie dziesięć etapów w kolejności dokładnie takiej, w jakiej je tutaj opisuję. Lecz jeśli Twój zespół liczy więcej osób o różnych umiejętnościach i doświadczeniu, prawdopodobnie kilka etapów będzie realizowanych równolegle, bo każda osoba zajmie się wykonywaniem swoich zadań. Programiści przystąpią do programowania, artyści zajmą się artystyczną stroną gry itd. Niezależnie od tego, który z tych przypadków ma miejsce, etapy implementacyjne pochłaniają najwięcej pracy i wymagają odpowiedniej koordynacji. I właśnie to kryje się pod pojęciem „zarządzanie projektem”.
31
32
Rozdział 1
Dziesięcioetapowy tok pracy
Oczywiście można pracować bez żadnego spójnego systemu zarządzania projektem, ale najczęściej kończy się to tylko zmarnowaniem czasu i pieniędzy. Gdy praca nie jest właściwie zorganizowana, wówczas często zdarza się, że niektórzy siedzą bezczynnie i czekają, aż inni skończą swoje zadania. Na pewno nie jest to optymalne wykorzystanie posiadanych środków. Jak więc należy prawidłowo zarządzać projektem? Niestety, odpowiedź na to pytanie wypełniłaby całą książkę. Zamiast tego podam kilka praktycznych wskazówek, które pomogą Ci szybko usprawnić funkcjonowanie zespołu. Powiem też, że nie od rzeczy będzie, jeśli spróbujesz dowiedzieć się więcej na temat zarządzania. Kilka interesujących publikacji z tej dziedziny proponuję w dodatku A.
Zinwentaryzuj środki Zacznij od ustalenia wszystkich zasobów, którymi masz zarządzać w ramach danego projektu. A zatem dowiedz się: ■ Kto należy do zespołu i jaką pełni funkcję. ■ Gdzie każda z tych osób będzie wykonywać swoją pracę (czy wszystkie w tym samym pomieszczeniu, czy może każda w innej części świata). ■ Jakie są różnice czasowe między miejscami przebywania pracowników. ■ Jakim sprzętem i oprogramowaniem dysponuje zespół oraz kto i jak będzie tych narzędzi używał (w książce będę omawiał programy takie jak Unity, Blender, GIMP, Inkscape i Audacity). ■ Jak dużo czasu zostało do ostatecznego terminu ukończenia prac. ■ Jaką sumą pieniędzy dysponujesz (całkowity budżet). Upewnij się, że znasz te wszystkie rzeczy, bo od tego będzie zależało, czy opracowany przez Ciebie harmonogram będzie realny.
Zmniejsz odległości Odległość to czas. Jeśli dwaj członkowie Twojego zespołu znajdują się po przeciwnych stronach Ziemi, będzie miało miejsce znaczące opóźnienie w komunikacji między nimi ze względu na różne strefy czasowe (gdy jeden będzie się budził, drugi będzie się kładł do łóżka). Niemożność komunikowania się w czasie rzeczywistym zdecydowanie utrudni skonsolidowanie zespołu. Zrób jednak wszystko, aby zminimalizować znaczenie tych przesunięć czasowych. Jeśli Ci się to uda, pozornie zbliżysz tych ludzi do siebie. Jednym ze sposobów na osiągnięcie czegoś takiego może być utworzenie wirtualnego biura — miejsca w przestrzeni internetowej, gdzie wszyscy członkowie zespołu mogą się logować, aby zostawić wiadomość, pobrać nowe zadania, sprawdzić terminy, zweryfikować aktualny stan zaawansowania
Etap 5. Tworzenie elementów gry
prac i dzięki temu poczuć przynależność do zespołu. Ze zdobyciem oprogramowania dla takiego biura nie ma większego problemu — można skorzystać z gotowych rozwiązań, takich jak BaseCamp (www.basecamp.com), ActiveCollab (www.activecollab.com), Unfuddle (www.unfuddle.com) itp.
Zaplanuj pracę Nie ma czegoś takiego jak złożony problem. Złożoność to tylko nagromadzenie mniejszych elementów składowych. A zatem problem złożony to nic innego jak zbiór drobnych problemów. Czasami wyodrębnienie składników większej całości bywa trudne, ale w zarządzaniu projektem jest konieczne. W tym celu musisz rozbić proces tworzenia gry na małe, łatwe do ogarnięcia segmenty, takie jak „konstrukcja postaci wilkołaka”, „programowanie inteligencji wroga” czy „komponowanie ścieżki dźwiękowej dla poziomu nr 1”. W większości programów wspomagających zarządzanie projektami segmenty te są nazywane zgłoszeniami (tickets). Każde zgłoszenie reprezentuje określone zadanie. Cały proces tworzenia gry można rozbić na setki lub tysiące takich zgłoszeń. Wyodrębnij je wszystkie, a następnie nałóż na kalendarz projektu i wyznacz dla każdej pozycji nieprzekraczalny termin realizacji, uwzględniając potrzebę zachowania płynności procesu. Na koniec przydziel każde zgłoszenie właściwemu członkowi zespołu. Jeśli będziesz stosował taki system przez cały czas realizacji projektu, przekonasz się, że wszystko przebiega całkiem sprawnie. Wspomniałem o zarządzaniu projektem w tym miejscu, bo jest to zagadnienie o szczególnym znaczeniu dla pozostałych etapów pracy, które obejmują zadania o bardzo zróżnicowanym charakterze — od modelowania przestrzennego i animacji po projektowanie poziomów i programowanie. Tym wszystkim trzeba przecież jakoś zarządzać. A teraz wróćmy do pozostałych etapów i zobaczmy, co się w nich kryje.
Etap 5. Tworzenie elementów gry Etap piąty to tworzenie elementów gry, czyli wszystkich grafik i animacji. Są to elementy interfejsu (okna, kursory itp.), grafiki dwuwymiarowe (ekrany powitalne, tła), dźwięki i grafiki trójwymiarowe (siatki). Wszystko, co widać i słychać podczas grania, jest elementem gry. Wytworzenie tych elementów jest zwykle najdłuższą i najdroższą częścią procesu produkcyjnego (chyba że większość elementów gry jest już gotowa i nadaje się do użycia). Jeśli efekt końcowy ma być dobrej jakości, na tym etapie nie możesz oszczędzać. Artyści muszą poświęcić dużo czasu i wykazać się zaangażowaniem. To właśnie na tym etapie znajdą zastosowanie programy takie jak GIMP, Blender i Inkscape. Zapoznasz się z nimi już w następnym rozdziale. Teraz chciałbym jedynie, abyś zachował w pamięci kilka rad.
33
34
Rozdział 1
Dziesięcioetapowy tok pracy
■ Wykorzystuj te same elementy wielokrotnie. Jeśli akcja gry toczy się w wielu podobnych miejscach (np. w biurach), w każdym z nich będą potrzebne takie same albo podobne obiekty, np. krzesła. W takiej sytuacji możesz wymodelować tylko jedno lub dwa krzesła, a potem w miarę potrzeb będziesz je tylko kopiował. Nie musisz każdego jednego egzemplarza modelować od podstaw. Nie przejmuj się za bardzo powtarzalnością, bo ten sam obiekt może wyglądać zupełnie inaczej w otoczeniu innych przedmiotów i w innych warunkach oświetleniowych. Jeżeli powtarzalność będzie się rzucać w oczy, zmodyfikujesz zastosowane kopie, w ostateczności utworzysz zupełnie nowy obiekt. Nie zakładaj jednak już na wstępie, że problem taki na pewno wystąpi. Spróbuj zaoszczędzić trochę czasu i pracy. ■ Używaj zdjęć jako materiałów referencyjnych. W większości prac plastycznych widać dążenie do jak najwierniejszego odtworzenia elementów świata rzeczywistego. Nawet mityczne stwory, jak smoki, skrzaty czy elfy, zawierają jakąś cząstkę realizmu, choćby przez to, że są podobne do stworzeń, które naprawdę istnieją. Tak naprawdę wszystko, co zostało stworzone na potrzeby gier, w mniejszym lub większym stopniu nawiązuje do świata rzeczywistego i było wzorowane na materiałach fotograficznych. Po prostu graficy, tworząc swoje dzieła, korzystają ze zdjęć jako materiałów referencyjnych. Materiały te są niezwykle istotne. Nie sądź, że wszystko uda się wymyślić i że wystarczy do tego sama wyobraźnia. Jeśli chcesz, żeby gra zrobiła dobre wrażenie wizualne, musisz zgromadzić odpowiednie materiały referencyjne. Poświęć na to tyle czasu, ile trzeba. Rób zdjęcia i zbieraj je, gdzie tylko się da. Każda chwila, jaką na to przeznaczysz, nie będzie zmarnowana, wcześniej czy później przyniesie Ci wymierne korzyści. Pierwszą korzyścią będzie szybsze tworzenie wizualnych elementów gry. ■ Stosuj outsourcing. Outsourcing to po prostu wystąpienie do kogoś z zewnątrz z propozycją wykonania elementów Twojej gry. Zabieg taki jest dość często stosowany, a najczęściej wtedy, gdy studio ma poważne opóźnienia i potrzebuje pomocy, by nadrobić stracony czas, ale nie chce na stałe zatrudniać dodatkowych pracowników. Jednakże wiążą się z tym dodatkowe koszty — a nie każdy zespół znajdzie odpowiednie rezerwy w swoim budżecie. Zanim podejmiesz decyzję o outsourcingu, przeanalizuj dokładnie wszystkie za i przeciw.
Etap 6. Importowanie elementów gry do silnika Po utworzeniu wszystkich elementów trzeba je zaimportować do silnika gry. W tej książce opiszę tworzenie elementów za pomocą Blendera, GIMP-a i Inkscape’a, a silnikiem będzie pakiet o nazwie Unity. Mogłoby się wydawać, że przenoszenie elementów z jednego pakietu do drugiego to nic wielkiego i nie warto tworzyć z tego odrębnego etapu. Jednakże przenoszenie czegoś, co zostało wytworzone w jednym programie, i umieszczanie tego w silniku gry często
Etap 6. Importowanie elementów gry do silnika
wymaga żmudnego dostrajania wielu parametrów i wprowadzania poprawek. Niekiedy może to zająć trochę czasu i spowodować opóźnienia w realizacji całości. Dlatego przeznaczyłem na te czynności oddzielny etap. Do ich omawiania wrócę jeszcze w następnym rozdziale, a na razie podam tylko ogólne informacje na temat importowania elementów gry. ■ Formaty plików i kompatybilność. Sprawdź, jakie formaty plików są możliwe do zaimportowania przez wybrany silnik gry. Każdy silnik obsługuje określony zestaw formatów dla siatek, animacji, filmów i obrazów. Dowiedz się, jakie one są, i upewnij się, że programy użyte do tworzenia elementów gry mogą generować pliki w takich formatach albo że są dostępne odpowiednie konwertery. Opisywane przeze mnie programy (Unity, Blender, GIMP i Audacity) są pod tym względem w pełni kompatybilne. Oznacza to, że możesz tworzyć elementy gry w Blenderze, GIMP-ie i Audacity, a potem bez większych problemów zaimportujesz je do Unity. Formaty obsługiwane przez Unity Formaty obrazów
Formaty siatek
PSD
MA
TIFF
MB
JPEG
MAX
PNG
JAS
GIF
C4D
BMP
BLEND
TGA
LXO
IFF
FBX
PICT
3DS OBJ
Formaty dźwięku
Formaty filmów
MP3
AVI
OGG
MOV
AIFF
ASF
WAV
MPG
MOD
MPEG
IT
MP4VIDEO
S3M
Ogg Theora
XM Więcej informacji na temat obsługiwanych formatów i procesu importowania plików w Unity znajdziesz na stronach: • http://unity3d.com/unity/workflow/asset-workflow/, • http://docs.unity3d.com/Documentation/Manual/AssetWorkflow.html.
35
36
Rozdział 1
Dziesięcioetapowy tok pracy
■ Wymagania dotyczące elementów gry. Fakt, że Unity obsługuje format, w jakim zapisane są elementy gry, nie gwarantuje wcale prawidłowego ich wyglądu po zaimportowaniu i uruchomieniu silnika. Zanim przystąpisz do tworzenia pierwszego elementu, zapoznaj się dokładnie z wymaganiami i ograniczeniami stawianymi przez silnik. Zwróć szczególną uwagę na parametry związane z liczbą wielokątów, strukturą siatki, liczbą materiałów, mapowaniem UV itp. Najprawdopodobniej spotkasz się z surowymi wymaganiami dotyczącymi struktury i konfiguracji elementów, a zwłaszcza siatek. Szczegółami tego zagadnienia w odniesieniu do silnika Unity zajmiemy się w rozdziałach 2. i 3. Na razie możesz zaczerpnąć trochę informacji z instrukcji dostępnej pod adresem: http://docs.unity3d.com/Documentation/Manual/ ImportingAssets.html.
Etap 7. Projektowanie poziomów Po udanym zaimportowaniu elementów gry do jej silnika możesz rozpocząć konstruowanie poziomów i środowisk. Proces budowania poziomów zmienia się w zależności od konkretnej gry. Może wymagać łączenia siatek, jak cegieł, w celu uformowania kompletnego środowiska gry trójwymiarowej, albo malowania bitmapowych teł dla gry dwuwymiarowej. Ostatecznym celem projektowania poziomów jest zbudowanie świata, w który gracz ma się zagłębić. Aby to osiągnąć, musisz stworzyć świat interesujący i interaktywny. Z tego powodu projektowanie poziomów często odbywa się równolegle z pisaniem kodu i skryptów, czyli z czynnościami zaliczanymi do następnego etapu. Jeśli chodzi o projektowanie poziomów, to mam trzy bardzo ważne wskazówki, chociaż tak naprawdę należałoby wymienić znacznie więcej. ■ Stosuj metodę modułową. Kuszące jest patrzenie na poziomy i środowiska z perspektywy gracza. Wtedy wydaje się, że są kompletnymi i zespolonymi całościami. Nie poddawaj się takiej pokusie. Gdy szukasz wyjścia z labiryntu korytarzy i lochów lub przemierzasz bezkresne obszary pustynne bądź dżunglowe, wszystko sprawia wrażenie, jakby zostało w całości wygenerowane przez artystę in situ. Ty jednak powinieneś myśleć o poziomach raczej w kategoriach klocków Lego lub bloków budowlanych. Zazwyczaj poziomy układa się właśnie z takich małych zazębiających się kawałków. Są kawałki narożne, proste, krzyżowe, końcowe i wszelkich innych rodzajów. Każdy można połączyć z dowolnym innym, co w rezultacie daje właściwie nieskończoną liczbę konfiguracji, w jakich można je ułożyć. Naprawdę uwielbiam tę metodę. Projektowanie poziomów może dać dużo satysfakcji i radości! Uwa ga
W rozdziale 3. opisuję techniki projektowania poziomów z użyciem Blendera. Zobaczysz tam, jaki można zrobić użytek z modułowych klocków, które idealnie do siebie pasują.
Etap 8. Kodowanie
■ Projektuj poziomy zgodnie z planami. Podczas tworzenia elementów środowiska w sposób modułowy łatwo dać się ponieść fantazji. Na przykład w edytorze pakietu Unity można bez trudu wrzucać kolejne elementy środowiska i łącząc je w najrozmaitsze sposoby, tworzyć fantazyjne i zupełnie zwariowane światy. Zabawa jest znakomita, ale może się źle skończyć. Swoboda tworzenia może doprowadzić do tego, że wygenerowane poziomy i środowiska będą dalekie od pierwotnych planów. Jeśli się w porę nie opamiętasz, może się okazać, że Twoje dzieło w ogóle nie będzie się nadawało do użytku i tylko stracisz czas. Pamiętaj o tym i w trakcie projektowania poziomów często zaglądaj do swoich projektów, map i szkiców. Improwizacja nie jest rzeczą złą, ale powinna być podejmowana świadomie i nie może burzyć pierwotnego zamysłu. ■ Sprawdzaj, co inni sądzą. A więc masz już zachwycające poziomy do swojej gry. Ale czy rzeczywiście są tak zachwycające, jak Ci się wydaje? Według mnie kwestia estetyki poziomów gry nie istnieje. Uważam, że nie ma poziomów „dobrych” lub „złych”, tak samo jak nie ma „dobrych” lub „złych” gier — przynajmniej w sensie obiektywnym. Możesz się z tym zgadzać albo nie. Tak czy inaczej, jeśli planujesz sprzedawać swoją grę, to musisz brać pod uwagę, co inni ludzie o niej myślą. Dlatego powinieneś przy najbliższej sposobności dać ją do przetestowania przynajmniej trzem osobom. Pozwól im pograć na wszystkich poziomach i w tym czasie obserwuj ich reakcje. Wsłuchuj się w ich komentarze i wyciągaj wnioski. Czy będą to uwagi pozytywne, czy negatywne, traktuj je wszystkie jak instrukcje mówiące, co masz poprawić. Tych negatywnych nie odnoś do siebie i nie obrażaj się ani nie wpadaj w depresję. Nie pozwól, aby emocje wzięły górę nad zdrowym rozsądkiem.
Etap 8. Kodowanie Kodowanie zazwyczaj odbywa się równolegle z projektowaniem poziomów. Świat gry powinien nie tylko ładnie wyglądać, ale również muszą się w nim dziać pewne rzeczy. Mogą tam być np. windy przenoszące bohatera na różne kondygnacje budowli albo potwory rozwalające ściany i atakujące intruzów, którzy podeszli zbyt blisko. To wszystko trzeba zaprogramować (zakodować) w Unity za pomocą języka skryptowego. Unity oferuje trzy takie języki: C#, JavaScript i Boo. Każdy z nich ma olbrzymie możliwości, więc przy wyborze możesz się kierować jedynie swoimi upodobaniami. Uwa ga
Wszystkie prezentowane w tej książce skrypty napisałem w języku C#. Nie oznacza to wcale, że inne uważam za gorsze. Po prostu ten język znam najlepiej i lubię się nim posługiwać. Programowanie może trwać długo i wymagać dużego nakładu pracy. Aby ten proces przebiegał efektywnie, pamiętaj o następujących zasadach:
37
38
Rozdział 1
Dziesięcioetapowy tok pracy
■ Pracuj zgodnie z planem. Nigdy nie programuj na ślepo. Zanim napiszesz pierwszy wiersz kodu, przemyśl dokładnie, co chcesz uzyskać. Przecież żeby rozwiązać jakikolwiek problem, najpierw trzeba dobrze zrozumieć, na czym on polega. Skoro już o rozwiązywaniu problemów mowa: nie staraj się szukać rozwiązania dobrego wyłącznie dla bieżącego przypadku, ale takiego, które będzie miało zastosowanie również w sytuacjach podobnych. Załóżmy np., że masz zaprogramować stwora, który po odniesieniu ran w walce będzie szukał fantów przywracających zdrowie i siły. Możesz się skupić na tym jednym konkretnym stworze, ale przecież takich stworów może być w tej samej grze więcej, a skoro tak, to dlaczego nie miałbyś napisać ogólnej procedury, którą dałoby się wykorzystać wielokrotnie. Właśnie taki styl programowania będziesz miał okazję zobaczyć w drugiej połowie książki. ■ Bądź konsekwentny. Unity pozwala na luksus programowania w kilku językach — możesz używać C#, JavaScript lub Boo. Możesz nawet stosować kombinacje tych trzech języków. Jednakże odradzam takie postępowanie. Wybierz jeden język i konsekwentnie stosuj go w całym projekcie. Unikniesz dzięki temu ciągłego przestawiania się na inny sposób myślenia, a w zamian zyskasz większy komfort pracy i możliwość pogłębienia znajomości konkretnego języka. Jeśli przyjdzie Ci włączyć do swojego kodu jakąś procedurę napisaną w innym języku, „przetłumacz” ją na wybrany przez siebie język, chyba że zaistnieją naprawdę ważne przeszkody, takie jak brak czasu lub szczupłość budżetu. ■ Komentuj swój kod. Komentarze zamieszczane w kodzie są czytelnymi dla człowieka objaśnieniami, mającymi ułatwić zrozumienie, jakie zadanie wykonuje dany fragment kodu. Nie zaniedbuj tej czynności nawet wtedy, gdy jesteś jedynym programistą i nikt inny nie będzie Twojego kodu czytał. Naturalnie gdy go piszesz, wszystko w nim jest klarowne i zrozumiałe, ale już po paru tygodniach lub miesiącach sytuacja może wyglądać zupełnie inaczej. Komentarze przydadzą się zarówno innym programistom, jak i Tobie. Ty sobie szybciej przypomnisz, o co w tym kodzie chodzi, a inni będą mieli ułatwione zadanie, jeśli będą musieli go zaadaptować do swoich potrzeb. Kod należy pisać tak, aby mógł być wielokrotnie wykorzystywany, i komentarze właśnie temu celowi służą. W s k a z ów ka
Zachęcam do przeczytania artykułu Roedy’ego Greena zatytułowanego How to Write Unmaintainable Code (http://thc.org/root/phun/unmaintain.html). Jest to utrzymany w ironicznym tonie poradnik na temat, jak pisać kody „jednorazowego użytku”, ale jednocześnie instruujacy, jak tego nie robić. Autor, pisząc ten artykuł, miał na uwadze język Java, ale zaprezentowane argumenty są uniwersalne i można je odnieść do każdego innego języka.
Etap 9. Testowanie
Etap 9. Testowanie Etap testowania składa się z kilku faz zależnych od rodzaju gry i stopnia jej ukończenia. Samo testowanie polega w głównej mierze na próbnej grze. Pierwszym testerem będziesz zapewne Ty sam, ale powinny to zrobić też inne osoby. Celem jest sprawdzenie, czy wszystko w grze działa zgodnie z projektem i czy przy prawidłowym użytkowaniu nie pojawiają się błędy. Podczas takich testów należy sprawdzać wszystkie możliwe warianty, nawet te mało prawdopodobne. Jeśli gracz ma do wyboru dwoje drzwi: X i Y i z przebiegu gry jasno wynika, że należy wybrać drzwi X, to tester powinien sprawdzić również, co kryje się za drzwiami Y. Na tym etapie trzeba grę sprawdzać tak długo i wszechstronnie, jak tylko pozwalają na to warunki czasowe. Oto kilka wskazówek, o których warto pamiętać podczas przeprowadzania testów: ■ Testuj jak najwcześniej. Nie myśl o testowaniu jak o czymś, co ma nastąpić dopiero wtedy, gdy gra będzie gotowa. Testowanie powinno być procesem ciągłym. Testuj poszczególne funkcje i wprowadzane zmiany, gdy tylko je zaprogramujesz. Zaplanuj też regularne bardziej ogólne testy w całym procesie produkcyjnym, aby kontrolować prawidłowość zestrojenia gotowych już elementów. Podczas takich testów bądź bezwzględny i nie pozwalaj sobie na żadne pobłażanie. Nie bój się, że coś może nie zadziałać i trzeba będzie poświęcić dużo czasu na naprawę błędu. Jeśli przyczyna usterki nie jest oczywista, użyj narzędzi debugujących, jakie są w pakiecie Unity. Działaj systematycznie. Sprawdzaj podejrzane funkcje pojedynczo i w maksymalnej izolacji od pozostałych. Szukaj błędu metodą eliminacji. ■ Nie bądź jedynym testerem. Tak jak autor nigdy nie będzie dobrym recenzentem swojej książki, tak i programista nie będzie najlepszym testerem swojego programu. Owszem, błędy składniowe wychwyci kompilator, a Ty zauważysz oczywiste i grube błędy logiczne. Natomiast cała masa błędów drobnych i pozornie nic nieznaczących najprawdopodobniej ujdzie Twojej uwadze. Tak mają wszyscy programiści, nie tylko twórcy gier. Dlatego proś i zachęcaj innych, aby testowali Twoją grę na różnych etapach jej rozwoju. Będziesz zaskoczony liczbą wykrytych w ten sposób błędów. Każdy błąd wykryty traktuj jak okazję do świętowania, a nie powód do rozpaczy. Każdy twórca oprogramowania chciałby, aby jego aplikacje były bezbłędne, ale żeby wyeliminować wszystkie błędy, trzeba je najpierw wykryć. ■ Numeruj kolejne wersje. Potraktuj poważnie numerowanie kolejnych wersji gry, zachowując ścisłe reguły nadawania numerów głównych i pobocznych, aby dla użytkownika było jasne, z czym ma do czynienia. Po wykryciu i naprawieniu błędów nie zapomnij o właściwym skorygowaniu również numeru wersji. Dzięki temu użytkownik — i Ty też — będzie wiedział, czy korzysta z wersji najnowszej, czy nie. Poza tym jeśli w grze pojawi się jakiś nowy błąd, łatwiej Ci będzie ustalić, w którym momencie (w której wersji) mógł być wprowadzony i jaka zmiana kodu może być za to odpowiedzialna.
39
40
Rozdział 1
Dziesięcioetapowy tok pracy
■ Rejestruj błędy. Notuj wszystkie błędy wraz z okolicznościami ich wykrycia i przyczynami. Nie zważaj na to, czy dany błąd został naprawiony, czy jeszcze nie. Nie usuwaj też żadnej pozycji z rejestru. Prześledzenie takiej listy może Cię naprowadzić na trop podobnych usterek, które jeszcze nie zostały wykryte. Tropienie błędów w kodzie jakiejkolwiek aplikacji przypomina trochę odkrywanie tajemnic, więc bądź tak dobrym detektywem, jak tylko możesz.
Etap 10. Budowanie Ostatni etap to budowanie pełnej wersji gry. Dotarcie do tego etapu może zająć miesiące, a nawet lata. Gra jest już stworzona i przetestowana, ale trzeba jeszcze uczynić z niej pełnoprawną aplikację, która będzie mogła funkcjonować na innych komputerach. Jeśli Twoja gra ma być dostępna poprzez sklepy takie jak App Store, Google Play czy Windows Store, musisz ją zbudować. Proces budowania musisz przeprowadzić także po to, aby gra dała się uruchomić na platformie Windows, Linux, Mac lub na jakiejś konsoli do gier. Wiele aspektów tego procesu można wykonać po prostu w Unity, ale mimo wszystko wymaga to uwagi i starannego przygotowania. Będziemy do tego wracać wielokrotnie w pozostałych rozdziałach. Tutaj wymienię tylko kilka najważniejszych kwestii. ■ Dostosuj proces budowy do platformy. Jedną z zalet pakietu Unity jest to, że umożliwia zbudowanie gry i dostosowanie jej do wielu systemów, takich jak Windows, Linux, Mac, Android, iOS, Windows Phone, BlackBerry, Web i konsole. Dobrze oddaje to często powtarzany slogan: „Buduj raz, instaluj wszędzie”. Koncepcja wydaje się bardzo prosta — tworzymy grę, a na koniec wciskamy guzik, który sprawia, że jest ona gotowa do użytku na każdej z wymienionych platform. I rzeczywiście, proces budowania może tak wyglądać, jeśli tylko właściwie skonfigurujemy narzędzia pakietu Unity, ale to jeszcze nie musi oznaczać, że gra zadziała prawidłowo w każdym docelowym systemie. Aby tak się stało, trzeba całemu procesowi poświęcić trochę uwagi i przeprowadzić tzw. optymalizację budowy gry pod kątem danej platformy. Oczywiście Unity ma odpowiednie do tego narzędzia, a o technikach ich stosowania będziemy mówić w kolejnych rozdziałach. ■ Poznaj docelową platformę. Prawie każda gra przeznaczona na określoną platformę ma jakieś minimalne wymagania. Po prostu gracz musi dysponować określonym sprzętem, żeby móc zagrać. Czasami twórcy gry określają także zalecane parametry systemu, czyli taką konfigurację sprzętowo-programową, przy której gra będzie mogła działać bez żadnych ograniczeń. Są to Twoje dwie „konfiguracje docelowe” — dwie konfiguracje sprzętowe, na których gra powinna zadziałać. Na czas testowania i budowania gry powinieneś mieć dostęp do obu tych konfiguracji. Ale to nie wszystko. O tym, jak mają wyglądać docelowe konfiguracje, powinieneś zdecydować przed rozpoczęciem tworzenia gry, a nie po jej ukończeniu. Przepro-
Zalecenia praktyczne
wadzanie testów w takich warunkach pozwoli Ci właściwie ocenić szybkość działania gry i jeśli zajdzie potrzeba, zdecydować o konieczności podjęcia prac nad poprawą jej wydajności. ■ Poznaj wymagania sklepu z aplikacjami. Jeśli chcesz, aby Twoja gra znalazła się w sklepie z aplikacjami, takim jak App Store czy Google Play, musisz poznać warunki, na jakich możesz tam swoją aplikację umieścić. Każdy taki sklep stawia określone wymagania co do zawartości gry, rozmiaru pliku i parametrów strukturalnych, takich jak wymiary ikon czy liczba i rozmiary plików z danymi. Poświęć trochę czasu na przestudiowanie tych wymagań i uwzględnij je podczas budowania gry. W razie wątpliwości staraj się uzyskać odpowiedź, korzystając z różnych kanałów pomocy technicznej konkretnego sklepu.
Zalecenia praktyczne Zaprezentowany wyżej dziesięcioetapowy tok pracy jest tylko ogólnym schematem tworzenia gier, od casualowych układanek po drogie MMORPG-i. Jeśli nawet nie spasuje Ci w całej rozciągłości, to i tak jego znajomość może Ci wiele rzeczy ułatwić. Pamiętaj, aby maksymalnie wykorzystać to wszystko, co będzie pasowało do Twoich potrzeb. Jeśli przystępujesz do tworzenia gry po raz pierwszy, mam dla Ciebie kilka dodatkowych wskazówek. ■ Mierz siły na zamiary. Postaw sobie cel możliwy do osiągnięcia i zmierzaj do niego z całych sił. Niemal każdy czuje podniecenie i zapał, gdy myśli o grze, którą zamierza utworzyć. W takim stanie bardzo łatwo dodaje się kolejne elementy i funkcje, aby gra była jeszcze lepsza i fantastyczniejsza. Ale też łatwo można sobie nałożyć na barki więcej niż się potrafi udźwignąć. Jeśli masz mały zespół i niewielki budżet, wyznacz sobie realistyczne granice, aby uniknąć potem rozczarowania. Jeśli nie potrafisz realistycznie oszacować całkowitych kosztów przedsięwzięcia ani terminu jego zakończenia, potraktuj to na równi z zagrożeniem wykonalności projektu i wprowadź stosowne ograniczenia. Postępuj tak dotąd, aż będziesz w stanie określić koszty i czas trwania jego realizacji. ■ Nie rozdrabniaj się. Jeśli jesteś przemęczony, zestresowany i masz wszystkiego dość, to znaczy, że starasz się robić zbyt wiele rzeczy naraz. Raczej skup się na jednym zadaniu i poświęć mu całą swoją energię. Następny krok rób dopiero po zakończeniu etapu poprzedniego. Gry są przedsięwzięciami z pogranicza wielu dyscyplin i wymagają od twórców szerokiego wachlarza umiejętności. Nie oznacza to jednak, że musisz pracować na wszystkich polach jednocześnie, przeskakując z programowania do grafiki, a stamtąd do muzyki i jeszcze do projektowania poziomów bez wyraźnie wytyczonego kierunku i podziału pracy. Bądź zorganizowany i koncentruj się na tym, co aktualnie robisz. Wykonuj zadania po jednym.
41
42
Rozdział 1
Dziesięcioetapowy tok pracy
■ Mniej graj, więcej pracuj. Pewnego razu grupa młodych ludzi studiujących tworzenie gier spytała mnie, jak mogliby poprawić swoją produktywność. Mieli oryginalny pomysł na grę i spore umiejętności, ale frustrowało ich zbyt wolne tempo prac nad jej realizacją. Gdy odwiedziłem ich studio w Chicago, aby zobaczyć, jak mógłbym im pomóc, szybko zorientowałem się, że sporą część czasu spędzają na graniu w rozmaite gry. Jak się później okazało, granie zajmowało im aż 65% czasu, który powinni przeznaczyć na tworzenie własnej gry. To prawda, że inne gry mogą być źródłem cennej inspiracji, ale takie myślenie może być istotnym zagrożeniem dla postępu prac nad własnym projektem. Bądź uczciwy wobec siebie, jeśli chodzi o podział czasu na pracę i rozrywkę. ■ Nie uskarżaj się na narzędzia. Podczas tworzenia gry napotkasz wiele przeszkód i problemów. I nie ma tu znaczenia, jakiego narzędzia będziesz akurat używał: Unity, Blendera, GIMP-a czy jeszcze innego. W takich sytuacjach łatwo zrzucić całą winę na niedoskonałość narzędzia, ale taka postawa wyjdzie tylko na złe zarówno Twojej pracy, jak i Twojej grze. Zamiast narzekać, postaraj się spojrzeć na problem z innej perspektywy i poszukać innego sposobu użycia narzędzia. Zrób sobie krótką przerwę i wróć do pracy z innym nastawieniem. W razie potrzeby poszukaj doradztwa w internecie. Zazwyczaj ten sam cel można osiągnąć na wiele sposobów i służące do tego narzędzia też pozwalają na znaczną elastyczność. Gdy tylko przestaniesz narzekać na narzędzia i zaczniesz odkrywać ich wszechstronność, zdołasz zrobić z ich pomocą więcej niż Ci się wydaje. Nie pytaj: „Kto zawinił?”, tylko mów do siebie: „Wiem, że za pomocą tego programu można to zrobić. Ale jak?”.
Podsumowanie Ten rozdział wyznacza punkt początkowy na naszej drodze do praktycznego wykorzystania połączonych możliwości Unity i Blendera oraz kilku innych programów wspomagających tworzenie gier. Celem ostatecznym jest ukształtowanie właściwego sposobu myślenia o grach komputerowych i ich tworzeniu. W dziesięcioetapowym toku pracy cały cykl produkcji gry dzielimy na dziesięć odrębnych faz. Kolejność i metody ich realizacji zależą od konkretnej gry, zespołu wykonawczego i dostępnych środków. Zawsze jednak wyznaczają one istotne punkty cyklu produkcyjnego, przez co zarządzanie nim staje się łatwiejsze. W pozostałych rozdziałach skoncentrujemy się na sześciu ostatnich etapach, czyli na tworzeniu elementów gry, programowaniu i ostatecznym budowaniu gry. Pominiemy zagadnienia związane z projektowaniem gry i tworzeniem dokumentu projektowego. Zrobimy tak, bo najczęściej po programy typu Unity i Blender sięga się dopiero wtedy, gdy projekt gry jest już gotowy. One mają służyć do tworzenia elementów gry i jej poziomów, a właśnie to ma być głównym tematem książki. Nie będę jednak opisywał tych zagadnień w układzie zgodnym z zaprezentowanym wyżej tokiem pracy, lecz w układzie tematycznym i z uwzględnieniem wzajemnych powiązań między poszczególnymi programami oraz ich współpracy z silnikiem Unity.
Podsumowanie
W tym pierwszym rozdziale podałem wiele wskazówek i rad dla tych, którzy po raz pierwszy próbują stworzyć grę, ale nie oznacza to, że inni z niego nie skorzystają. Wszystkie zawarte w nim rady można by podsumować następująco: mierz siły na zamiary, nie rób zbyt wielu rzeczy jednocześnie, twórz gry zamiast w nie grać i nie obwiniaj narzędzi za swoje niepowodzenia. Jeśli podczas lektury pozostałych rozdziałów będziesz postępował zgodnie z tymi czterema zasadami, to myślę, że tworzenie gier stanie się dla Ciebie wspaniałym zajęciem. Oczywiście mam na myśli tworzenie gier za pomocą Unity i Blendera.
43
44
Rozdział 1
Dziesięcioetapowy tok pracy
R OZ DZ I AŁ 2
O D B LENDERA DO U NITY
Prostota jest szczytem wyrafinowania. — Leonardo da Vinci Po przeczytaniu tego rozdziału powinieneś: ■ umieć przygotować Blendera do współpracy z Unity; ■ rozumieć zagadnienia związane z przechodzeniem od Blendera do Unity; ■ zdawać sobie sprawę z trudności w importowaniu i eksportowaniu siatek; ■ umieć obchodzić się z ostrymi krawędziami i modyfikatorem Edge Split (rozcinanie krawędzi); ■ umieć eksportować modele w formacie FBX. Niemal w każdej grze wideo mamy do czynienia z wyświetlaniem modeli złożonych z wielokątów i dlatego zwanych siatkami. Ich geometryczna forma jest definiowana za pomocą wierzchołków, krawędzi i ścianek. W ten właśnie sposób przedstawiane są w zasadzie wszystkie przedmioty widoczne w grze — od elementów uzbrojenia po ludzi i wszelkie potwory. Artyści tworzący na potrzeby gier mają do dyspozycji wiele rozmaitych programów ułatwiających generowanie takich siatek, ale my będziemy używać wyłącznie Blendera. Jest to darmowy i działający na wielu platformach program do modelowania obiektów trójwymiarowych; można go pobrać ze strony: www.blender.org/. Uwa ga
Jeśli chciałbyś zobaczyć konkretne przykłady tego, co potrafi Blender, obejrzyj krótki film pt. Sintel zamieszczony na stronie: www.youtube.com/watch?v=eRsGyueVLvQ.
45
46
Rozdział 2
Od Blendera do Unity
Nie będę opisywał Blendera od podstaw. Jest mnóstwo darmowych i komercyjnych poradników oraz kursów obsługi tego programu kierowanych również do początkującego użytkownika. Można tam nie tylko dowiedzieć się wszystkiego na temat interfejsu tego programu i stosowanych w nim skrótów klawiszowych, ale również poznać podstawy modelowania, teksturowania itp. W s k a z ów ka
Jeśli jeszcze nigdy nie używałeś Blendera albo potrzebujesz odświeżyć swoją wiedzę o nim, zanim przystąpisz do dalszej lektury tego rozdziału, to zajrzyj do dodatku A. W części zatytułowanej „Książki” znajdziesz kilka pozycji, które z pewnością ułatwią Ci wstępne poznanie tego programu. Głównym tematem tego rozdziału jest współpraca Blendera z Unity, ale będzie też okazja, żeby wspomnieć o modelowaniu i teksturowaniu. Jednak zgodnie z tym, co przed chwilą powiedziałem, skoncentruję się przede wszystkim na praktycznym aspekcie współdziałania tych dwóch programów. W szczególności pokażę, jak można skonfigurować interfejs Blendera, aby dało się w nim pracować szybciej i wydajniej. Następnie zobaczysz, na czym polega optymalny sposób eksportowania wymodelowanych siatek do programu Unity z zachowaniem odpowiedniego cieniowania, rozmiarów i struktury. Jeśli w tym momencie nie wiesz za bardzo, o czym mówię, nie przejmuj się. Wkrótce wszystko się wyjaśni.
Konfigurowanie interfejsu Blendera Gdy uruchomisz Blendera po raz pierwszy i nie zmienisz ustawień domyślnych, jego interfejs będzie wyglądał mniej więcej tak jak na rysunku 2.1. Mówię „mniej więcej”, a nie „dokładnie”, bo Blender jest dość często aktualizowany i jest niemal pewne, że wersja używana przez Ciebie będzie nowsza od mojej, a zatem możesz mieć przed sobą nieco inny widok. Wielu ludziom odpowiadają te domyślne ustawienia i używają oni Blendera bez zmieniania czegokolwiek. Jednak ja się do nich nie zaliczam. Wprowadzam zmiany, i to dość znaczące. Opiszę je wszystkie wraz z uzasadnieniem, abyś mógł w pełni ocenić, czy moje podejście jest godne naśladowania, czy nie. Oto te modyfikacje.
Ciemny motyw Kolorystyka interfejsu Blendera jest zdefiniowana w postaci zestawów barw zwanych motywami. Motyw domyślny zawiera średnie odcienie szarości pokazane na rysunku 2.1. Stwierdziłem, że przy dłuższej pracy taki interfejs działa jednak rozpraszająco — oko ludzkie w naturalny sposób kieruje się w stronę miejsc jaśniejszych. A skoro naszą uwagę przyciąga interfejs, to trudniej się nam skupić na opracowywanym modelu. Aby temu zaradzić, przyciemniam interfejs, włączając motyw Elsyiun pokazany na rysunku 2.2.
Konfigurowanie interfejsu Blendera
Rysunek 2.1. Blender w konfiguracji domyślnej (wersja 2.68a). Jeśli masz wersję późniejszą, jej interfejs może wyglądać nieco inaczej Źródło: Blender.
Rysunek 2.2. Motyw Elsyiun przyciemnia interfejs Blendera Źródło: Blender.
Aby włączyć ten motyw kolorystyczny, wykonaj następujące czynności: 1. Wybierz File/User Preferences (plik/preferencje użytkownika) lub wciśnij klawisze Ctrl+Alt+U. 2. W oknie dialogowym Blender User Preferences, które się otworzy, kliknij zakładkę Themes (motywy).
47
48
Rozdział 2
Od Blendera do Unity
3. Rozwiń listę Presets (ustawienia predefiniowane) i wybierz pozycję o nazwie Elsyiun, tak jak pokazano na rysunku 2.3. Kolory interfejsu natychmiast się zmienią.
Rysunek 2.3. W oknie dialogowym Blender User Preferences wybierz motyw o nazwie Elsyiun Źródło: Blender.
4. W celu zatwierdzenia wprowadzonej zmiany kliknij przycisk Save User Settings (zapisz ustawienia użytkownika) widoczny w dolnej części okna preferencji. Następnie zamknij to okno, aby wrócić do głównej przestrzeni roboczej.
Jeśli później uznasz, że jednak motyw domyślny był lepszy, możesz go przywrócić przez kliknięcie przycisku Reset to Default Theme (przywróć motyw domyślny) w oknie Blender User Preferences.
Etykietki bez pythonowych instrukcji Blendera używają zarówno artyści tworzący modele, jak i programiści piszący skrypty rozwijające możliwości programu. Dlatego w ustawieniach domyślnych starano się uwzględnić potrzeby jednych i drugich. Jednak my, twórcy gier, będziemy działać głównie jako artyści, więc niepotrzebne nam będą elementy przydatne programistom. Jednym z takich elementów są instrukcje w języku Python (język programowania dostępny w Blenderze jako język skryptowy) wyświetlane w etykietkach narzędziowych, które ukazują się, gdy przytrzymasz wskaźnik myszy na jakimś przycisku lub innym elemencie interfejsu (rysunek 2.4). Jako artyście informacje takie nie będą Ci potrzebne. Możesz je śmiało ukryć, aby dodatkowy tekst nie angażował bez potrzeby Twojej uwagi.
Konfigurowanie interfejsu Blendera
Rysunek 2.4. Przy domyślnych ustawieniach Blendera w etykietkach narzędziowych wyświetlane są informacje zarówno dla artystów, jak i programistów Źródło: Blender.
Aby wyłączyć wyświetlanie informacji pythonowych, wykonaj następujące czynności: 1. Otwórz okno dialogowe Blender User Preferences przez wybranie polecenia File/User Preferences. 2. Kliknij zakładkę Interface (interfejs). 3. Wyłącz opcję Show Python Tooltips (pokaż etykietki narzędziowe Pythona) — rysunek 2.5. 4. Kliknij przycisk Save User Settings (zapisz ustawienia użytkownika). (Jeśli tego nie zrobisz, dawne ustawienia powrócą po ponownym uruchomieniu programu).
Kontrolki z programu Maya Dla mnie największym usprawnieniem w pracy z Blenderem i Unity jest możliwość wykonywania wielu czynności w Blenderze w sposób podobny jak w programie Maya. Przy ustawieniach domyślnych Blender oferuje własny zestaw skrótów klawiszowych i kontrolek do nawigowania w oknach widokowych. Niestety, wszystko odbywa się tu inaczej niż w programie Unity, który pod tym względem nie różni się od programu Maya.
49
50
Rozdział 2
Od Blendera do Unity
Rysunek 2.5. Wyłączanie informacji pythonowych w oknie Blender User Preferences Źródło: Blender.
Ponieważ często będziemy musieli używać Blendera na przemian z Unity, byłoby dobrze, gdyby dało się uniknąć częstego przestawiania się z jednego systemu sterowania na inny. Z doświadczenia wiem, że bez ujednolicenia tych systemów łatwo o pomyłkę, a zastosowanie skrótu blenderowego w Unity (lub na odwrót) nie wróży nic dobrego. Rozwiązanie tego problemu tkwi w predefiniowanym ustawieniu o nazwie Maya, za pomocą którego można sprawić, że Blender będzie działał tak jak Unity. Po uaktywnieniu tego ustawienia otrzymujemy jeden spójny system sterowania obowiązujący w obu aplikacjach. Uwa ga
Ktoś może zapytać, dlaczego upodobniłem Blendera do Unity, a nie na odwrót. Powód jest wręcz banalny: po prostu Blender jest pod względem konfigurowalności bardziej elastyczny i nawet ma już wbudowane gotowe ustawienia, więc po co się męczyć, skoro można to zrobić szybko i skutecznie. Zmianę systemu sterowania Blenderem można przeprowadzić na dwa sposoby. Pierwszy polega na wybraniu odpowiedniej opcji na ekranie powitalnym, a drugi wymaga posłużenia się oknem dialogowym Blender User Preferences. W pierwszym przypadku należy: 1. Uruchomić Blendera. 2. Na ekranie powitalnym rozwinąć listę Interactions (interakcje) i wybrać z niej opcję Maya — tak jak na rysunku 2.6.
„Błąd” zamykania bez zapisu
Rysunek 2.6. Wybieranie systemu sterowania Blenderem na ekranie powitalnym Źródło: Blender.
W s k a z ów ka
Ekran powitalny można wyświetlić również w trakcie pracy z Blenderem. W tym celu należy kliknąć ikonkę Blendera widoczną w nagłówku okna informacyjnego, który domyślnie znajduje się przy górnej krawędzi interfejsu (na rysunku 2.6 ikonka ta jest zaznaczona kółkiem). Drugi sposób wymaga następujących czynności: 1. Wybierz polecenie File/User Preferences. 2. W oknie dialogowym Blender User Preferences, które się otworzy, kliknij zakładkę Input (wejście). 3. Rozwiń listę Presets i wybierz pozycję o nazwie Maya (rysunek 2.7)1. 4. W celu zatwierdzenia wprowadzonej zmiany kliknij przycisk Save User Settings.
„Błąd” zamykania bez zapisu Zanim przejdziemy do bardziej zasadniczych spraw, chciałbym omówić kwestię pozornego błędu, który stał się już sławny w społeczności blenderowej. Otóż istnieje niepisane prawo, które mówi, że każda aplikacja użytkowa powinna reagować na polecenie zamknięcia odpowiednim komunikatem, jeśli użytkownik nie zapisał aktualnego 1
Listy są dwie (na rysunku rozwinięta jest tylko jedna): jedna odnosi się do operacji wykonywanych za pomocą myszy, a druga do skrótów klawiszowych. Ekran powitalny ustawia obie listy w należyty sposób — przyp. tłum.
51
52
Rozdział 2
Od Blendera do Unity
Rysunek 2.7. Ustawianie systemu sterowania Blenderem w oknie dialogowym Blender User Preferences Źródło: Blender.
stanu swojej pracy. Niemal wszystkie programy, od pakietów Microsoft Office i LibreOffice aż po zaawansowane programy graficzne, takie jak Unity czy GIMP, w pełni respektują to prawo, a Blender nie! Gdy go zamykasz, po prostu znika z ekranu i robi tak niezależnie od tego, czy zapisałeś rezultat swojej pracy, czy nie. A gdy go uruchamiasz ponownie, jakby nigdy nic wyświetla nową, świeżą scenę i nadal nie daje żadnego znaku, że tamta niezapisana scena nadal istnieje i możesz do niej wrócić. Już niejeden użytkownik uznał, że wiele godzin pracy zostało bezpowrotnie stracone. W takich chwilach rzeczywiście można wpaść w furię, zwłaszcza jeśli sobie uświadomimy, że przecież można było wszystkiego uniknąć, gdyby tylko Blender „zachował się” w sposób konwencjonalny i wyświetlił stosowne ostrzeżenie. Uwa ga
Niektórzy próbowali uzasadniać „brak tej funkcji”, twierdząc, że użytkownik Blendera powinien być profesjonalistą i dbać o rezultaty swojej pracy. Uważam jednak takie podejście za co najmniej niewłaściwe, ponieważ ignoruje ono fakt, że każdemu (również profesjonaliście) może się zdarzyć przypadkowe zamknięcie aplikacji bez zapisania opracowywanego pliku. Na szczęście problem jest tylko pozorny! Czytaj dalej… Przekonanie, że Blender pozwala na utratę niezapisanych zmian, jest błędne. Jego twórcy przewidzieli takie sytuacje i zaimplementowali metodę odzyskiwania rezultatów poprzedniej sesji. Wystarczy po prostu wybrać polecenie File/Recover Last Session
Przenoszenie modeli z Blendera do Unity
(plik/odtwórz ostatnią sesję) — rysunek 2.8. Polecenie działa, ponieważ Blender tuż przed zamknięciem zapisuje bieżącą scenę w pliku quit.blend i wystarczy ten plik otworzyć. Wydaje się to całkiem proste (i tak jest — wszystko sprowadza się przecież tylko do kliknięcia odpowiedniego polecenia w menu), ale przeświadczenie o niedopracowaniu Blendera powoduje, że możliwość przywrócenia poprzedniej sesji często uchodzi uwadze użytkownika.
Rysunek 2.8. Przywracanie poprzedniej sesji w celu odzyskania niezapisanych danych Źródło: Blender.
Być może to, co napisałem, nie do końca Cię przekonuje i nadal uważasz, że wzorem innych aplikacji Blender powinien wyświetlać ostrzeżenie o niezapisanych zmianach. Proponuję jednak odłożyć te dywagacje na bok. Nie piszę książki o tym, jakie funkcje dany program powinien zawierać, a jakich nie. Piszę o tym, jaki program jest i co można za jego pomocą zrobić. Sądzę, że tylko przy takim nastawieniu da się w pełni wykorzystać te możliwości programu, które w nim tkwią.
Przenoszenie modeli z Blendera do Unity Jednym z zagadnień, które są najczęściej dyskutowane w rozmowach na temat toku pracy z użyciem Blendera i Unity, jest przenoszenie modeli wygenerowanych w Blenderze do silnika Unity. Popularną metodą jest zapisanie blenderowej sceny w pliku .blend
53
54
Rozdział 2
Od Blendera do Unity
(natywny format Blendera) za pomocą polecenia File/Save (plik/zapisz), a następnie otwarcie tego pliku w Unity. Resztą zajmuje się już Unity — przynajmniej w teorii! Czasami — zależnie od okoliczności — otrzymasz dokładnie to, o co Ci chodziło, i nie musisz niczego poprawiać, ale najczęściej rezultaty będą trudne do zaakceptowania. Może się również zdarzyć, że ta prosta metoda w ogóle nie zadziała. Wyjaśnię teraz, dlaczego tak się dzieje, a potem na konkretnym przykładzie przedstawię właściwe rozwiązanie. Uwa ga
Na razie zajmujemy się eksportowaniem blenderowych modeli do Unity w sensie ogólnym. Pomijamy szczegóły dotyczące modeli animowanych, takich jak rigowane siatki postaci człekokształtnych lub innych stworów. Wrócimy do nich w dalszej części książki.
Pliki .blend Blender domyślnie tworzy pliki w formacie .blend. Dlatego gdy wydasz mu polecenie File/Save (plik/zapisz), zapisze bieżącą scenę w pliku z rozszerzeniem .blend. Na szczęście Unity potrafi odczytywać takie pliki, więc możesz ich używać do przenoszenia danych z jednego programu do drugiego. Po prostu przeciągnij plik z Eksploratora lub Findera i upuść w obrębie interfejsu Unity. Metoda wydaje się interesująca, ale jest kilka powodów, dla których zdecydowanie odradzam jej stosowanie. ■ Unity do zaimportowania pliku .blend potrzebuje Blendera. Unity odczytuje pliki .blend, ale nie bezpośrednio. Tak naprawdę akceptuje tylko formaty: FBX, DAE, 3DS, DXF i OBJ. Gdy ma zaimportować dane z innego pliku, uruchamia w tle program macierzysty (Blendera dla pliku .blend) i za jego pomocą konwertuje ten plik na format FBX. A zatem każdy, kto chce importować pliki .blend do Unity, musi mieć zainstalowanego Blendera, nawet gdy nie ma zamiaru go używać. Unity potrzebuje go do przeprowadzenia konwersji. Wprawdzie Blender jest darmowy, ale już sama konieczność jego instalacji tylko po to, by otworzyć jakiś plik, może być irytująca — tym bardziej, że da się tego uniknąć (o tym za chwilę). ■ Pliki .blend ograniczają liczbę opcji eksportu i zwiększają ryzyko niekompatybilności. Gdy Unity przeprowadza konwersję, nie masz na ten proces żadnego wpływu. Stosowane są wtedy ustawienia domyślne, które przecież nie zawsze są optymalne, i w rezultacie otrzymujesz niekoniecznie prawidłową siatkę. Oczywiście może się zdarzyć, że wszystko pójdzie dobrze, ale nie mając na nic wpływu, możesz liczyć tylko na szczęśliwy przypadek. Poza tym dochodzi jeszcze ryzyko wynikające z częstych modyfikacji samego Blendera. Jeśli do konwersji pliku zostanie użyta inna wersja niż ta, za pomocą której go wygenerowano, rezultat może być trudny do przewidzenia.
Przenoszenie modeli z Blendera do Unity
■ Pliki .blend są zbyt duże. Nie jest to zarzut pod adresem tego formatu, lecz stwierdzenie, że przy ustawieniach domyślnych Unity z pomocą Blendera przeniesie do formatu FBX całą zawartość sceny, włącznie z obiektami, które nie powinny być importowane, np.: kamery, lampy, niewidoczne siatki, obiekty pozorne. W Blenderze są przydatne, ale ich import do Unity na ogół nie ma sensu. Uwa ga
Więcej informacji na temat formatów 3D i ich technicznych uwarunkowań znajdziesz w instrukcji obsługi programu Unity dostępnej pod adresem: http://docs.unity3d.com/Documentation/Manual/3D-formats.html.
Ćwiczenie: ręczny eksport do FBX Po przeczytaniu powyższych uwag raczej nie będziesz przenosił modeli z Blendera do Unity za pomocą plików .blend. O ile na wczesnym etapie prac, podczas testowania siatek, możesz się jeszcze nimi posługiwać, o tyle do importowania gotowych zasobów powinieneś używać wyłącznie plików w formacie FBX — tym bardziej, jeśli będą to zasoby przekazywane innym członkom zespołu. Jak już wspominałem, Unity posługuje się wewnętrznie formatem FBX nawet wtedy, gdy importuje pliki .blend. Ale jeśli sam wyeksportujesz z Blendera plik FBX, będziesz miał większą kontrolę nad jego zawartością niż podczas automatycznej konwersji pliku .blend. Ponadto uniezależnisz Unity od innych programów, bo z formatem FBX radzi sobie bez żadnej pomocy z zewnątrz. W ćwiczeniu tym pokażę na przykładzie zwykłego stożka, jak takie eksportowanie należy przeprowadzać. Naturalnie Twoje modele będą bardziej rozbudowane i skomplikowane, ale wszystko, co tutaj powiem, ma zastosowanie do wszystkich modeli: prostych i złożonych. Więc zaczynajmy!
Tworzenie modeli To ćwiczenie zaczniemy od zupełnie nowej sceny w Blenderze (zob. rysunek 2.1 lub 2.2). W codziennej pracy do eksportowania będziesz przystępował ze sceną zawierającą gotowe modele. Jednak tym razem usuwamy wszystko z wyjątkiem kamery. W pustej scenie umieść zwykły stożek — wybierz polecenie Add/Mesh/Cone (dodaj/siatkę/stożek). Będzie to model, który wyeksportujesz z przeznaczeniem umieszczenia go w Unity (rysunek 2.9). Radzenie sobie z wadami cieni Uwa ga
Ten punkt i następny zachowują ważność, pod warunkiem że używasz Blendera w wersji wcześniejszej niż 2.69. Właśnie w tym wydaniu aplikacji dodano funkcję dzielenia normalnych, która likwiduje omawiane tu usterki. Więcej informacji na ten temat znajdziesz pod adresem: http://wiki.blender.org/index.php/Dev:Ref/Release_ Notes/2.69. Jeśli używasz wersji 2.69 lub nowszej, możesz od razu przejść do punktu zatytułowanego „Środek obiektu”.
55
56
Rozdział 2
Od Blendera do Unity
Rysunek 2.9. W pustej scenie Blendera umieść stożek Źródło: Blender.
Idźmy dalej. Jeśli od razu przeniesiesz siatkę stożka z Blendera do Unity, czy to w pliku .blend, czy FBX (o którym więcej już za chwilę), najprawdopodobniej będziesz miał problem z wyrenderowaniem prawidłowych cieni na jej powierzchni. Zauważysz to, gdy tylko spojrzysz na siatkę w oknie widokowym Unity lub w polu podglądu na panelu Inspector (inspektor). Siatka zostanie wygładzona i będzie wyglądała dziwnie. Problem wynika z odmiennego podejścia obu programów do roli, jaką powinny odgrywać normalne. Rozbieżności w wyglądzie stożka wyświetlanego w Blenderze i w Unity są pokazane na rysunku 2.10.
Rysunek 2.10. Problem z cieniowaniem powierzchni siatki. Tutaj rozbieżności w cieniowaniu między Blenderem i Unity najlepiej widać przy krawędzi łączącej powierzchnię boczną stożka z jego podstawą Źródło: Blender.
Przenoszenie modeli z Blendera do Unity
Uwa ga
Dalszy ciąg tego punktu przedstawia sytuację hipotetyczną, więc nie musisz tego czytać. Chciałem Ci tylko pokazać, na jaki problem możesz natrafić, jeśli postąpisz zgodnie z podanym opisem. Innym efektem, jaki napotkasz, będą dużo mniejsze wymiary siatki w Unity aniżeli w Blenderze. Wynika to z domyślnie przyjmowanej przez Unity skali o wartości 0,01. Możesz się o tym przekonać, sprawdzając tę wartość w panelu Inspector, gdy siatka jest zaznaczona (rysunek 2.11). Aby to naprawić, po prostu zwiększ parametr Scale Factor (współczynnik skali) do poziomu 1. (Zagadnienie to będzie jeszcze omawiane).
Rysunek 2.11. Przez ponowne przeliczenie normalnych w Unity można szybko naprawić błędy cieniowania Źródło: Blender.
W przypadku modeli tak prostych jak sześciany, stożki czy kule problem cieniowania można szybko rozwiązać w Unity. Trzeba tylko zaznaczyć właściwą siatkę w panelu Project (projekt), po czym w panelu Inspector wybrać z listy rozwijanej Normals (normalne) opcję Calculate (oblicz). (Nie zapomnij kliknąć potem przycisku Apply, czyli „zastosuj”, aby zatwierdzić wprowadzone zmiany). Unity na podstawie kątów między ściankami ustali, które krawędzie mają być wygładzone, a które powinny pozostać ostre. W rezultacie otrzymasz stożek wyglądający niemal tak samo jak w Blenderze (zob. rysunek 2.11). Możesz zatem powiedzieć, że między aplikacjami występuje relacja podobieństwa 1:1. Powyższa metoda „naprawiania” błędów cieniowania ma jednak istotne ograniczenia. Jest to działanie automatyczne i nie masz żadnego wpływu na to, które krawędzie zostaną zacieniowane ostro, a które łagodnie. W przypadku stożka jest to wystarczające, ale przecież nie tylko takie siatki chciałbyś importować. Dlatego musimy wrócić do Blendera i tam odpowiednio przygotować model do eksportu. A zatem, jeśli wraz ze mną przenosiłeś stożek za pośrednictwem pliku .blend, usuń go teraz z Unity i ponownie otwórz Blendera.
57
58
Rozdział 2
Od Blendera do Unity
Ostrość krawędzi i modyfikator Edge Split W poprzednim punkcie pokazałem jeden sposób na rozwiązanie problemu złego cieniowania, a teraz zaprezentuję metodę pewniejszą i najczęściej stosowaną. Jeśli chcesz, żeby po zaimportowaniu do Unity siatka wyglądała prawidłowo i nie ufasz zbytnio mechanizmom przeliczania normalnych w tej aplikacji, musisz jeszcze w Blenderze wskazać dokładnie, które krawędzie mają pozostać ostre. Ostra krawędź to takie miejsce na powierzchni siatki, gdzie spotykają się dwie ścianki, a ich cieniowania pozostają całkowicie odrębne — nie są w żaden sposób uśredniane. Zazwyczaj są to kanty takich brył jak sześcian czy graniastosłup, ale mogą to być także nagłe załamania ścian pod kątem 90º. Natomiast większość obiektów o kształtach organicznych, np. ludzka twarz, ma powierzchnie gładkie i nie ma tam miejsca na ostre krawędzie. W przykładowym stożku powinieneś wskazać jako ostre krawędzie tworzące obwód jego podstawy, tam bowiem łączą się dwie zupełnie odrębne powierzchnie. Zaznacz więc cały obwód podstawy, po czym wybierz polecenie Mesh/Edges/Mark Sharp (siatka/ krawędzie/oznacz jako ostre) — tak jak na rysunku 2.12.
Rysunek 2.12. Krawędzie na obwodzie podstawy stożka oznacz jako ostre Źródło: Blender.
Wskazanie krawędzi, które powinny być ostre, to dopiero pierwszy krok. Teraz trzeba jeszcze poddać siatkę działaniu modyfikatora o nazwie Edge Split (rozcinanie krawędzi), który porozcina siatkę wzdłuż ostrych krawędzi i w ten sposób wymusi na Unity odrębne cieniowanie rozłącznych powierzchni. Nie bój się, że przez taki zabieg zniszczysz siatkę, bo wszystko dzieje się w sposób odwracalny — modyfikator działa w Blenderze i zawsze możesz go wyłączyć, a wtedy siatka powróci do stanu pierwotnego (z nierozciętymi krawędziami).
Przenoszenie modeli z Blendera do Unity
Aby zastosować modyfikator Edge Split, wykonaj następujące czynności: 1. Zaznacz siatkę stożka. 2. Otwórz zakładkę Object modifiers (modyfikatory obiektu) panelu Properties (właściwości) — rysunek 2.13.
Rysunek 2.13. Modyfikator Edge Split znajduje się na zakładce Object modifiers Źródło: Blender.
3. Na zakładce Object modifiers rozwiń listę Add Modifier (dodaj modyfikator) i wskaż opcję Edge Split. Modyfikator o takiej właśnie nazwie zostanie przypisany do obiektu stożka. 4. W właściwościach modyfikatora wyłącz opcję Edge Angle (kąt krawędzi), ale pozostaw włączoną funkcję Sharp Edges (ostre krawędzie) — tak jak na rysunku 2.14. W ten sposób nakażesz Blenderowi rozcięcie siatki tylko wzdłuż tych krawędzi, które oznaczyłeś jako ostre, a nie na podstawie kąta między przyległymi ściankami. Ostrzeżeni e
Nie klikaj przycisku Apply (zastosuj) w właściwościach modyfikatora (zob. rysunek 2.14), bo spowodujesz zatwierdzenie wprowadzonych zmian, a to będzie oznaczało, że staną się nieodwracalne. Po prostu zostaw modyfikator takim, jakim jest. Wrócisz do niego później, gdy będziesz eksportował stożek do pliku FBX.
59
60
Rozdział 2
Od Blendera do Unity
Rysunek 2.14. Dodaj do stożka modyfikator Edge Split, aby wyostrzyć oznaczone krawędzie Źródło: Blender.
Uwa ga
Zatwierdzenie (zastosowanie) modyfikatora przypisanego siatce wprowadza zmiany „nieodwracalne”, albo inaczej „stałe”, w sensie umownym. Należy to rozumieć jako niemożność odłączenia modyfikatora od obiektu i w ten sposób anulowania skutków jego działania. Jedynym sposobem na przywrócenie stanu pierwotnego jest ręczne zmodyfikowanie siatki — przez ponowne ustawienie wierzchołków, krawędzi i ścianek.
Środek obiektu Zarówno w Unity, jak i w Blenderze każdy obiekt ma określony punkt, zwany w Unity środkiem transformacji (pivot), a w Blenderze po prostu środkiem (origin). (Ja będę używał nazwy blenderowej). Jest to punkt odniesienia dla geometrii siatki. Jeśli ją przesuniesz do nowego położenia o określonych współrzędnych scenicznych, to właśnie tam znajdzie się jej środek. Jeśli ją będziesz obracał, to właśnie ten punkt będzie środkiem obrotu. Krótko mówiąc: jest to początek lokalnego układu współrzędnych siatki. Gdy ją eksportujesz z Blendera, zapewne chciałbyś, aby położenie tego punktu było takie jak należy. Właśnie teraz pokażę Ci, jak to osiągnąć. Na rysunku 2.15 zaznaczyłem zarówno środek stożka, jak i miejsce na zakładce Object (obiekt), gdzie możesz odczytać jego położenie we współrzędnych globalnych. (Gizmo transformacji jest umieszczone dokładnie w środku siatki).
Przenoszenie modeli z Blendera do Unity
Rysunek 2.15. Globalne (światowe, sceniczne) współrzędne położenia zaznaczonego obiektu można odczytać na zakładce Object panelu Properties. W oknie widokowym środek obiektu jest wskazywany przez gizmo transformacji Źródło: Blender.
Podczas modelowania w Blenderze często środek obiektu ląduje nie tam, gdzie byśmy chcieli. Czasami trzeba go umieścić w ściśle określonym położeniu względem siatki. Na przykład gdy obiektem jest postać człekokształtna, wskazane jest umieszczenie środka jej siatki na dolnej powierzchni stopy, żeby po zaimportowaniu do Unity właśnie tą częścią stykała się z podłożem. Na rysunku 2.15 środek stożka domyślnie pokrywa się z jego środkiem masy. (Wskazuje na to położenie gizma transformacji). Widać też, że pokrywa się on również z początkiem globalnego układu współrzędnych. Załóżmy, że chcesz go umieścić na podstawie stożka, aby przy współrzędnych globalnych równych (0,0,0) bryła spoczywała na podłożu, a nie pod nim. Żeby to uzyskać, wykonaj następujące czynności: 1. W oknie widokowym Blendera zaznacz stożek. 2. Włącz przyciąganie przyrostowe (Incremental Snapping) w sposób pokazany na rysunku 2.16. Jeśli teraz będziesz przesuwał stożek, będzie się on przemieszczał skokowo zgodnie z odległościami między liniami blenderowej siatki konstrukcyjnej, a nie płynnie jak przy przesuwaniu swobodnym. 3. Za pomocą gizma transformacji przesuń stożek w górę, aby jego podstawa znalazła się w siatce konstrukcyjnej. Zauważ, że w Blenderze oś skierowana do góry to oś Z i w trakcie przesuwania stożka w górę zmienia się wartość tej współrzędnej. Teraz położenie stożka powinno być określone wartościami (0,0,1). Jednak środek siatki przesunął się wraz z nią. Spójrz na rysunek 2.17. Stożek spoczywa na podłożu, tak
61
62
Rozdział 2
Od Blendera do Unity
Rysunek 2.16. Włącz przyciąganie przyrostowe, aby móc przesuwać obiekt skokowo zgodnie z siatką konstrukcyjną Źródło: Blender.
jak chcieliśmy, ale jego środek (wskazywany przez gizmo transformacji) nadal pokrywa się ze środkiem masy, a nie z podłożem. Musimy to tak skorygować, żeby bez przesuwania siatki jej środek znów znalazł się w punkcie o współrzędnych globalnych (0,0,0).
Rysunek 2.17. Przesunięcie stożka w górę spowodowało, że jego środek nie jest już w początku globalnego układu współrzędnych Źródło: Blender.
Przenoszenie modeli z Blendera do Unity
4. Aby z powrotem umieścić środek stożka w początku globalnego układu współrzędnych, użyj blenderowego kursora 3D (widoczny w oknie widokowym jako włosowaty krzyżyk celowniczy). Ustaw ten kursor w początku układu współrzędnych globalnych. W tym celu wybierz polecenie Object/Snap/Cursor to Center (obiekt/przyciągnij/ kursor do środka) — tak jak na rysunku 2.18.
Rysunek 2.18. W ramach przygotowań do przesunięcia środka stożka ustaw kursor 3D w początku globalnego układu współrzędnych Źródło: Blender.
5. Po ustawieniu kursora 3D w początku globalnego układu współrzędnych przyciągnij do niego środek stożka. W ten sposób początek lokalnego układu współrzędnych siatki pokryje się z początkiem układu globalnego. Aby to zrealizować, zaznacz siatkę stożka (jeśli jeszcze nie jest zaznaczona), a następnie wybierz Object/Transform/ Origin to 3D Cursor (obiekt/przekształć/środek do kursora 3D). Gizmo transformacji przesunie się do środka globalnego układu współrzędnych, a to będzie oznaczało, że tam też znajduje się środek stożka. Co więcej, współrzędna Z stożka będzie teraz miała wartość 0, a nie, jak dotychczas, 1. Rezultat jest pokazany na rysunku 2.19.
63
64
Rozdział 2
Od Blendera do Unity
Rysunek 2.19. Umieść środek siatki w środku sceny, a proces importu w Unity będzie bardziej przewidywalny Źródło: Blender.
Obracanie obiektów Niestety, to nie wszystko. Jest jeszcze kilka rzeczy do zrobienia, jeśli chcesz, aby przenoszenie siatki z Blendera do Unity przebiegło bezboleśnie i zakończyło się w sposób przewidywalny. Aby zilustrować kolejne zadanie, jakie zostało do wykonania, rozważmy następujący scenariusz. Jeśli teraz przeniesiesz stożek z Blendera do Unity — czy to za pomocą pliku .blend, czy FBX — jego wygląd w panelu Project na pierwszy rzut oka będzie prawidłowy. Lecz jeśli przyjrzysz się uważniej, zauważysz, że coś jest nie tak z orientacją stożka. Wygląda, jakby był obrócony o 90º lub 270º. I rzeczywiście tak jest (rysunek 2.20). Zaznaczenie siatki w panelu Project i sprawdzenie jej transformacji w panelu Inspector w pełni potwierdza taką diagnozę. Podczas importu w Unity siatka została obrócona o 270º wokół osi X, chociaż w Blenderze jej obroty wokół każdej osi były zerowe (u Ciebie oś obrotu może być inna). Istnieje tutaj wyraźna rozbieżność. Mamy więc do czynienia z niedopasowaniem domyślnych orientacji obiektu w Blenderze i Unity. Uwa ga
Jeśli w tym stanie przeciągniesz siatkę z panelu Project i umieścisz ją w scenie Unity, najprawdopodobniej jej orientacja zostanie skorygowana automatycznie, a nawet jeśli tak się nie stanie, możesz to zrobić ręcznie za pomocą stosownego manipulatora. Niektórych takie rozwiązanie zadowala, ale mnie nie. Ja zawsze dążę do tego, by importowane siatki miały ustawienia standardowe — położenie (0,0,0), obrót (0,0,0) i skalę (1,1,1). Dzięki temu unikam kłopotów związanych z niewłaściwymi wartościami początkowymi tych trzech transformacji.
Przenoszenie modeli z Blendera do Unity
Rysunek 2.20. Podczas przenoszenia z Blendera do Unity siatka jest obracana wokół jednej z osi o 90º lub 270º Źródło: Unity Technologies.
Problem z obrotem wynika z fundamentalnej rozbieżności między układami współrzędnych w Blenderze i w Unity. W Unity osią skierowaną do góry jest Y, a w Blenderze taką osią jest Z. Dlatego przy przejściu do przestrzeni o innej orientacji obiekt jest obracany o 90º lub 270º. Rozwiązanie polega na wykonaniu obrotu kompensacyjnego w programie macierzystym (Blenderze). Zabieg jest dwuetapowy. Pierwszy krok wykonamy teraz, a drugi dopiero po zapoznaniu się z blenderowym eksporterem FBX. A zatem, jeśli razem ze mną wykonywałeś poprzednie czynności, usuń siatkę stożka w Unity i wróć do Blendera. W oknie widokowym zaznacz stożek i za pomocą odpowiedniej kontrolki w panelu Properties obróć go o -90º wokół osi X. Niektóre siatki będziesz musiał także obrócić o 180º wokół osi Y, aby po zaimportowaniu do Unity były zwrócone do ekranu przodem, a nie tyłem. Spójrz na rysunek 2.21. Po wykonaniu kompensacyjnego obrotu musisz to przekształcenie zatwierdzić. Chodzi o takie „wypalenie” lub „zakodowanie” tego przekształcenia, żeby z blenderowego punktu widzenia nadal wszystkie obroty miały wartość zerową. Żeby coś takiego osiągnąć, upewnij się, że siatka jest zaznaczona, po czym wybierz polecenie Object/Apply/ Rotation & Scale (obiekt/zastosuj/obrót i skalowanie) — rysunek 2.22. I to na razie wszystko! Wrócimy do tej kwestii, gdy będziemy eksportować model do pliku FBX.
65
66
Rozdział 2
Od Blendera do Unity
Rysunek 2.21. Przed wyeksportowaniem do formatu FBX nadaj siatce odpowiednią orientację Źródło: Blender.
Rysunek 2.22. Zatwierdź transformację obiektu Źródło: Blender.
Przenoszenie modeli z Blendera do Unity
Nazywanie obiektów Ten etap procesu eksportowania jest opcjonalny. Przecież możesz z powodzeniem przenieść model do Unity bez nadawania mu specjalnej nazwy, a dla gracza nie będzie to miało żadnego znaczenia. Lecz jeśli gra ma zawierać dużo rozmaitych modeli, to warto poświęcić trochę czasu i ponadawać im jakieś sensowne nazwy, aby później można było łatwiej znaleźć to, czego się szuka. Nie lekceważ tej sprawy, bo bez dobrego systemu nazewnictwa szybko wkrada się bałagan i organizacja pracy zaczyna kuleć. Dlatego nadawaj nazwy modelom już w Blenderze. Blender automatycznie przypisuje stożkowi nazwę Cone (stożek). Jednak na ogół będziesz chciał zmienić taką domyślną nazwę na bardziej unikatową. Spróbujmy zatem nadać stożkowi nazwę MyCone (mój stożek). W tym celu zaznacz bryłę w oknie widokowym, dwukrotnie kliknij jej dotychczasową nazwę w panelu Outliner (organizator) i zastąp ją nową (rysunek 2.23). I to wszystko! Teraz możemy rozpocząć eksportowanie siatki do pliku FBX.
Rysunek 2.23. Nadaj obiektowi sensowną nazwę, aby później móc sprawniej pracować Źródło: Blender.
Eksportowanie do pliku FBX Wszystko, co robiliśmy do tej pory, było przygotowaniem do tego kroku. Teraz użyjemy blenderowego narzędzia eksportującego i za jego pomocą zapiszemy stożek w pliku FBX, aby stamtąd już bez żadnych niespodzianek mógł być zaimportowany w Unity. Wcześniej uzasadniałem, dlaczego lepiej jest importować siatki w Unity z plików FBX, a nie z plików .blend, a teraz, podtrzymując wszystkie tamte stwierdzenia, chcę powiedzieć, że pliki .blend również powinny odgrywać ważną rolę w Twojej pracy. Nigdy nie
67
68
Rozdział 2
Od Blendera do Unity
zapominaj o zapisywaniu swoich scen właśnie w tych plikach, bo tylko dzięki temu będziesz miał do nich pełny dostęp i w razie potrzeby będziesz mógł wprowadzać niezbędne zmiany. Jeśli zdecydujesz się na modyfikowanie siatki, nie wczytuj jej do Blendera z pliku FBX, lecz załaduj ją z pliku .blend. Plik FBX traktuj wyłącznie jako medium pośredniczące w przenoszeniu modeli z Blendera do Unity. Dlatego zanim wyeksportujesz siatkę do pliku FBX, zapisz ją w pliku .blend, wydając Blenderowi standardowe polecenie File/Save (plik/zapisz). Aby zaznaczoną siatkę wyeksportować do pliku FBX, wykonaj następujące czynności: 1. Zaznacz przeznaczoną do eksportu siatkę i wybierz File/Export/Autodesk FBX (.fbx) (plik/eksportuj/Autodesk FBX) — rysunek 2.24. Otworzy się okno narzędzia eksportującego z wieloma opcjami i parametrami eksportu. Dla siatek nieanimowanych, czyli takich jak stożek, najlepsze będą ustawienia pokazane na rysunku 2.25.
Rysunek 2.24. Przed rozpoczęciem eksportu zaznacz właściwą siatkę Źródło: Blender.
2. Nadaj plikowi sensowną nazwę. Ja wpisałem Cone.fbx, co widać na rysunku 2.25. Każda eksportowana siatka powinna mieć własną, unikatową i opisową nazwę. 3. Na rolecie Export FBX (eksport FBX) zaznacz opcję Selected Objects (zaznaczone obiekty). Spowoduje to, że wyeksportowane zostaną tylko obiekty zaznaczone, a nie cała scena. Jeśli nie włączysz tej opcji, wszystkie siatki zostaną skomasowane w jedną grupę i wyeksportowane.
Przenoszenie modeli z Blendera do Unity
Rysunek 2.25. Ustawienia eksportu dla siatek statycznych Źródło: Blender.
Uwa ga
Parametr Scale (skala) ma domyślną wartość 1.00 i w większości przypadków byłaby ona odpowiednia. Jednakże, jak się wkrótce przekonasz, Unity zupełnie pomija ten parametr bez względu na jego wartość. W zamian stosuje własną skalę o wartości 0.01, czy tego chcesz, czy nie. Później pokażę, jak sobie z tym problemem radzić. 4. Określ kierunki Forward (w przód) i Up (w górę). Jest to drugi etap procesu rozpoczętego w punkcie „Obracanie obiektów”. Ustawienia te w połączeniu z odpowiednią orientacją obiektu w scenie gwarantują mu właściwą orientację również po zaimportowaniu w Unity. Zazwyczaj kierunki te należy ustawiać zgodnie z kierunkami osi w Blenderze, czyli w polu Forward należy ustawić Y Forward, a w polu Up — Z Up. Czasami będziesz musiał trochę pokombinować z tymi ustawieniami, zanim siatka zostanie zaimportowana ze standardowymi wartościami transformacji, czyli z położeniem (0,0,0), obrotem (0,0,0) i skalą (1,1,1), a mimo to będzie miała właściwą orientację.
69
70
Rozdział 2
Od Blendera do Unity
5. Upewnij się, że zaznaczona jest opcja Apply Modifiers (zastosuj modyfikatory). Jest to konieczne, aby wszystkie modyfikatory przypisane siatce, włącznie z Edge Split (opisanym w punkcie „Ostrość krawędzi i modyfikator Edge Split”), zostały zakodowane w eksportowanej siatce. (Opcja ta nie spowoduje zatwierdzenia żadnego modyfikatora w pierwotnej blenderowej scenie; ma wpływ jedynie na siatkę, która jest generowana podczas eksportu i zapisywana w pliku FBX). 6. Dla siatek nieanimowanych (statycznych) wyłącz opcje Include Animation (dołącz animację) i Optimize Keyframes (optymalizuj klatki kluczowe). O eksportowaniu siatek animowanych będzie mowa w dalszej części książki. Uwa ga
Zamiast FBX możesz użyć formatu Collada (.dae). Użyj wtedy polecenia File/Export/Collada (plik/eksportuj/ Collada). Format ten obsługuje siatki zarówno statyczne, jak i animowane. Więcej informacji na jego temat znajdziesz pod adresem: https://collada.org/.
Zawartość pliku FBX Podczas eksportowania Blender tworzy plik FBX możliwy do odczytania w Unity. Przyjrzyjmy mu się nieco dokładniej. Warto to zrobić, bo plik zawiera czytelne dla człowieka (tekst ASCII) i edytowalne zapisy właściwości eksportowanej siatki. W razie potrzeby można tu jeszcze coś poprawić. Do przeglądania i edycji zawartości takiego pliku można użyć dowolnego edytora tekstowego, takiego jak Notepad, Notepad++ czy nawet MonoDevelop (rysunek 2.26).
Rysunek 2.26. Pliki FBX zawierają czytelny dla człowieka tekst ASCII, który można modyfikować w dowolnym edytorze tekstowym lub zintegrowanym środowisku programistycznym (IDE) Źródło: MonoDevelop.
Ćwiczenie: importowanie plików FBX w Unity
Uwa ga
Teoretycznie ręczne manipulowanie w prawidłowo wyeksportowanych plikach FBX nie powinno być potrzebne. Ale mimo wszystko warto wiedzieć, że taka możliwość istnieje. Warto też wiedzieć, że pliki te można analizować i przetwarzać również w trybie wsadowym.
Ćwiczenie: importowanie plików FBX w Unity Po wyeksportowaniu siatki FBX z Blendera zapewne będziesz chciał ją zaimportować w Unity, aby tam dołączyć ją do gry. W tym celu po prostu przeciągnij odpowiedni plik z Eksploratora (Windows) lub Findera (Mac OS) na panel Project w Unity (rysunek 2.27). Ale to jeszcze nie wszystko. Najprawdopodobniej pewne ustawienia będą wymagały dostrojenia. W tym ćwiczeniu zajmiemy się również takimi sprawami.
Rysunek 2.27. Importowanie plików FBX do panelu Projekt w Unity Źródło: Unity Technologies.
Współrzędne UV mapy światła Gdy będziesz importował statyczną siatkę terenu, budowli lub innego elementu środowiska gry, zapewne będziesz chciał ją oświetlić metodą mapowania światła. Do realizacji tego rodzaju oświetlenia możesz użyć narzędzia Beast Lightmapper dostępnego w Unity po wybraniu polecenia Window/Lightmapping (okno/mapowanie światła). Mapowanie światła polega na określaniu, jak dana siatka reaguje na panujące w jej otoczeniu warunki oświetleniowe — jakie rzuca cienie, jakie tworzy odblaski — i zapisywaniu takich informacji w formie specjalnej tekstury. Tekstura taka, zwana mapą światła, jest potem nakładana na powierzchnię siatki, co sprawia, że ta ostatnia wygląda, jakby rzeczywiście była oświetlona.
71
72
Rozdział 2
Od Blendera do Unity
Dzięki takim zabiegom ani procesor graficzny, ani główny nie muszą na bieżąco obliczać efektów oświetleniowych. Jednak żeby coś takiego się udało, zaimportowane siatki muszą mieć przypisane współrzędne mapy światła. Współrzędne te to specjalne dane matematyczne określające, w jaki sposób tekstura z mapą światła ma być odwzorowana na powierzchni siatki. Bez nich mapowanie światła może dać wynik daleki od oczekiwanego. Wartości tych współrzędnych możesz wyznaczyć ręcznie w Blenderze, ale możesz też zlecić to zadanie odpowiednim funkcjom Unity. Jeśli zdecydujesz się na ten drugi sposób, zacznij od zaznaczenia siatki w panelu Project, aby wyświetlić jej właściwości w panelu Inspector. Potem po prostu włącz opcję Generate Lightmap UVs (generuj współrzędne mapy światła) i kliknij przycisk Apply (zastosuj) — tak jak na rysunku 2.28. W większości przypadków otrzymasz rezultat prawidłowy, kłopot mogą sprawić jedynie siatki o kształtach okrągłych lub organicznych i wtedy będziesz musiał sam wyznaczyć te współrzędne za pomocą odpowiednich narzędzi Blendera. Więcej informacji na ten temat znajdziesz w następnym rozdziale.
Rysunek 2.28. Automatyczne generowanie współrzędnych mapy światła dla zaimportowanej siatki Źródło: Unity Technologies.
Ćwiczenie: importowanie plików FBX w Unity
Współczynnik skali Dla każdej importowanej siatki FBX Unity automatycznie ustala współczynnik skali o wartości 0,01 i nie ma tu znaczenia, jaka wartość jest zapisana w importowanym pliku. W rezultacie siatka przyjmuje rozmiary dużo mniejsze niż te, które powinna mieć zgodnie z projektem. Czasami będziesz musiał wykonać mocne zbliżenie, żeby ją w ogóle zobaczyć. Problem można rozwiązać przez zwiększenie wartości parametru Scale Factor (współczynnik skali) do poziomu 1 (rysunek 2.29). Jednakże przy większej liczbie importowanych siatek lub wielokrotnym importowaniu tej samej siatki (ze względu na częste modyfikacje) ciągłe powtarzanie tej samej czynności szybko staje się męczące.
Rysunek 2.29. Zmiana współczynnika skali na 1 (zamiast 0,01) Źródło: Unity Technologies.
Metoda alternatywna polega na napisaniu skryptu, który zmusi Unity do określonego zachowania podczas importowania siatek — w tym przypadku do ustawiania za każdym razem współczynnika skali o wartości 1. Skrypt będzie działał automatycznie i już nigdy nie będziesz musiał ręcznie poprawiać tego parametru. Aby to uzyskać, utwórz w panelu Project nowy skrypt w języku C# i nazwij go FBXFix.cs (poprawianie FBX). Następnie umieść w tym skrypcie następujący kod:
73
74
Rozdział 2
Od Blendera do Unity
using UnityEditor; public class FBXFix : AssetPostprocessor { public void OnPreprocessModel() { ModelImporter modelImporter = (ModelImporter) assetImporter;
modelImporter.globalScale = 1; } }
Gotowy skrypt umieść w folderze Editor (edytor). (Jeśli taki folder nie istnieje w bieżącym projekcie, utwórz go). Żeby skrypt działał, musi się znajdować właśnie w takim folderze (rysunek 2.30). Aby go przetestować, zaimportuj nowy plik FBX. Współczynnik skali powinien mieć teraz wartość 1!
Rysunek 2.30. Odpowiedni skrypt w folderze Editor ustawi współczynnik skali importowanej siatki na 1 Źródło: Unity Technologies.
A zatem umiesz już eksportować modele z Blendera i importować je do Unity z zachowaniem niemal pełnej wierności.
Podsumowanie Rozdział ten poświęciłem opisowi czynności związanych z przenoszeniem modeli między Blenderem a Unity. Mówiąc krótko: gdy dwa różne programy wymieniają między sobą dane, bardzo często część tych danych jest tracona lub zmieniana. W przypadku Blendera i Unity do przenoszenia danych można użyć plików .blend lub FBX. W tym rozdziale starałem się wykazać, że pliki FBX nadają się do tego celu znacznie lepiej. Ale jak miałeś okazję się przekonać, temat jest znacznie szerszy niż tylko wybór
Podsumowanie
formatu plików. Żeby siatka przeniesiona z Blendera do Unity wyglądała jak najlepiej, trzeba wykonać jeszcze kilka dodatkowych czynności. Niezbędne jest oznakowanie ostrych krawędzi siatki, nadanie jej odpowiedniej orientacji, właściwe umiejscowienie środka transformacji itp. Wszystko, co tutaj napisałem, odnosi się do każdej siatki, niezależnie od jej rodzaju. Siatki animowane mają dodatkowe wymagania, ale tym zagadnieniem zajmiemy się dokładniej w jednym z dalszych rozdziałów. Natomiast tematem następnego rozdziału będzie specyficzny rodzaj siatek statycznych, mianowicie modułowe siatki środowiskowe.
75
76
Rozdział 2
Od Blendera do Unity
R OZ DZ I AŁ 3
M ODUŁOWE ŚRODOWISKA I SIATKI STATYCZNE
Kreatywność to tylko umiejętność łączenia różnych rzeczy. — Steve Jobs Po przeczytaniu tego rozdziału powinieneś: ■ rozumieć, czym są siatki statyczne i modułowe środowiska; ■ umieć stosować modułowe metody budowania środowisk; ■ znać zagadnienia związane z modelowaniem i mapowaniem UV; ■ dobrze rozumieć pojęcia takie jak mirroring, duplikaty, n-kąty itp.; ■ umieć budować w Unity sceny z modułów i prefabrykatów. W wielu grach typu RPG czy FPS widzimy wspaniale rozbudowane środowiska w postaci lasów, jaskiń, stacji kosmicznych, biurowców, szpitali, tajnych laboratoriów itp. Jak każda „namacalna” rzecz w grze 3D środowiska są wykonane z siatek — misternych kompozycji wielokątów, krawędzi i wierzchołków połączonych w jedną całość za pomocą programu do modelowania w trzech wymiarach. Dla twórcy gier budowanie takich złożonych środowisk jest interesującym wyzwaniem technicznym. Z punktu widzenia gracza wygląda to jak kompleksowy, spójny i w pełni zintegrowany świat. Wnętrza pomieszczeń płynnie przechodzą w sąsiadujące z nimi korytarze, a krajobraz górzysty niepostrzeżenie zamienia się w teren zurbanizowany lub pustynny. Można odnieść wrażenie, że całe środowisko jest jedną wielką siatką i że w takiej postaci zostało zaimportowane do silnika gry. Jednak na ogół jest to tylko złudzenie. Zazwyczaj środowisko składa się z wielu małych kawałków, zwanych modułami, które połączone razem jak klocki Lego tworzą większą całość. Technika takiego składania środowisk z małych 77
78
Rozdział 3
Modułowe środowiska i siatki statyczne
kawałków nazywana jest metodą budowania modułowego lub po prostu metodą modułową. W zasadzie jest to trójwymiarowy odpowiednik metody kafelkowej stosowanej przy tworzeniu plansz dla gier 2D. Na rysunku 3.1 pokazany jest zestaw modułów dla środowiska fantastycznonaukowego. Sam w sobie może nie zachwyca, ale przez wielokrotne użycie poszczególnych modułów można zbudować całkiem spójne środowisko, chociażby takie jak na rysunku 3.2.
Rysunek 3.1. Kompletny zestaw modułów środowiska fantastycznonaukowego. Przez łączenie tych elementów jak klocków możesz ułożyć kompletne środowisko Źródło: Blender.
Rysunek 3.2. Przykład środowiska zbudowanego z modułów Źródło: Unity Technologies.
Zalety metody modułowej
Zalety metody modułowej Metoda modułowa ma kilka niewątpliwych zalet. Oto one: ■ Wielokrotność użycia. Przez rozbicie środowiska na elementarne kawałki, a nie konstruowanie go w całości, ujawnia się olbrzymi potencjał wynikający z możliwości wielokrotnego wykorzystywania poszczególnych elementów. Te same moduły połączone w różnych kombinacjach pozwalają uzyskać różne środowiska — przy założeniu, że moduły zostały należycie zaplanowane i przygotowane! (W dalszej części rozdziału zobaczysz, jak należy to robić). Możliwość wielokrotnego stosowania modułów zwalnia nas z konieczności modelowania wielu podobnie wyglądających środowisk, bo przecież można je poskładać z tych samych klocków. ■ Wydajność. Wielokrotność użycia niemal zawsze idzie w parze ze wzrostem wydajności i efektywności, zwłaszcza w renderowaniu. Jeśli Unity potrafi należycie zinterpretować środowisko złożone z modułów, to możesz znacząco poprawić efektywność gry. Korzyści wynikają szczególnie z wyświetlania wyłącznie zawartości bryły widzenia i tylko tego, co nie jest zasłonięte, a także z lepszego zarządzania pamięcią. Uwa ga
Więcej informacji na temat bryły widzenia (frustum) i pomijania tego, co jest zasłonięte, znajdziesz na stronach: http://docs.unity3d.com/Documentation/Manual/UnderstandingFrustum.html oraz http://docs. unity3d.com/Documentation/Manual/OcclusionCulling.html. ■ Optymalizacja tekstur. Środowiska modułowe są optymalne pod względem teksturowania, ponieważ teksturowanie małych modułów jest dużo łatwiejsze niż nakładanie złożonego obrazu na jedną, rozbudowaną siatkę. Do tego dochodzi jeszcze możliwość wielokrotnego stosowania tej samej tekstury, jako że każdy moduł ciągle korzysta z tych samych współrzędnych teksturowych i z tej samej przestrzeni teksturowej. A zatem do poteksturowania całego środowiska modułowego, choćby nie wiadomo jak dużego, nie potrzeba więcej tekseli (pikseli teksturowych) niż do poteksturowania samych modułów.
Rozpoczynanie prac nad środowiskiem modułowym Po podjęciu decyzji co do wyglądu środowiska gry i jego rozplanowaniu trzeba wymodelować poszczególne moduły w programie do grafiki 3D, np. w Blenderze. Od początku zwracaj baczną uwagę na rozmiary i proporcje modułów, żeby później nie było problemów z ich wzajemnym dopasowywaniem. Podczas modelowania musisz pamiętać, że nie tworzysz całego środowiska, a jedynie segmenty, z których później (po zaimportowaniu do Unity) takie czy inne środowisko będzie można złożyć. W tworzeniu takich pasujących do siebie modułów często pomaga wyobrażenie sobie gotowego środowiska pociętego na jednakowej wielkości kostki.
79
80
Rozdział 3
Modułowe środowiska i siatki statyczne
Potem trzeba jeszcze zbudować taką geometrię wewnątrz tych wyimaginowanych kostek, aby dało się je sensownie połączyć również w innej aranżacji. Nie oznacza to, że każdy moduł musi się łączyć z każdym innym; chodzi po prostu o to, aby dało się z nich ułożyć jak najwięcej środowisk o zbliżonym charakterze. Ja przeważnie zaczynam od ustalenia wymiarów kostki, czyli objętości podstawowego klocka (rysunek 3.3). Domyślny sześcian utworzony w Blenderze ma swój środek w początku globalnego układu współrzędnych (0,0,0), co na ogół jest niekorzystne. Zazwyczaj najwłaściwsze jest takie położenie, przy którym w punkcie (0,0,0) znajduje się środek dolnej ścianki bryły. Aby to osiągnąć, włącz funkcję przyciągania przyrostowego i przesuń sześcian w górę (wzdłuż osi Z) na tyle, żeby jego dolna ścianka znalazła się dokładnie na siatce konstrukcyjnej (rysunek 3.4).
Rysunek 3.3. Sześcian jako elementarny składnik środowiska modułowego Źródło: Blender.
Jednak samo przesunięcie bryły wraz z jej środkiem nie wystarczy. Przy przenoszeniu siatki z jednego programu do drugiego najlepiej sprawdza się usytuowanie jej środka w początku globalnego układu współrzędnych, przez który przechodzi siatka konstrukcyjna. Przy domyślnych ustawieniach Blendera w tym właśnie punkcie znajduje się kursor 3D i jeśli do niego przyciągniesz środek kostki, uzyskasz to, o co chodzi. Aby to zadanie wykonać, po prostu wybierz polecenie Object/Transform/Origin to 3D Cursor (obiekt/przekształć/środek do kursora 3D) — tak jak na rysunku 3.5. Jeśli kursor 3D znajduje się poza środkiem globalnego układu współrzędnych, umieść go tam, wybierając polecenie Object/Snap/Cursor to Center (obiekt/przyciągnij/kursor do środka).
Rozpoczynanie prac nad środowiskiem modułowym
Rysunek 3.4. Aby ułatwić sobie ustawienie sześcianu na siatce konstrukcyjnej, włącz przyciąganie przyrostowe Źródło: Blender.
Rysunek 3.5. Umieszczanie środka sześcianu w początku globalnego układu współrzędnych za pośrednictwem kursora 3D Źródło: Blender.
81
82
Rozdział 3
Modułowe środowiska i siatki statyczne
Tak przygotowana kostka stanowi najmniejszy blok, czyli moduł środowiska, i dlatego będę ją często nazywał klockiem podstawowym. Nie da się jednoznacznie powiedzieć, jakie powinny być jego wymiary, bo to w dużym stopniu zależy od konkretnej gry i przyjętego toku pracy. Ja zazwyczaj kieruję się tutaj wymiarami głównego bohatera, jakie posiada on w Unity. Dlatego w przypadku gier pierwszoosobowych i RPG zazwyczaj dotąd przenoszę kostkę między Blenderem i Unity, aż uzyskam podstawowy klocek obejmujący postać gracza z pewnym naddatkiem. Do zmieniania wymiarów kostki można użyć blenderowego narzędzia Scale (skala) — skrót klawiszowy R — ale ja wolę operować tutaj liczbami całkowitymi. Dlatego zawsze używam kontrolek Dimensions (wymiary) z panelu N1 i jako wartości X, Y oraz Z wpisuję okrągłe, pozbawione części ułamkowej liczby — tak jak na rysunku 3.6. Jak widać, tym razem nadałem kostce wymiary (w jednostkach globalnych) 2×2×3, przy czym wymiar największy odnosi się do wysokości. Przypominam: w Blenderze kierunek pionowy wyznacza oś Z.
Rysunek 3.6. Ustalanie wymiarów klocka podstawowego za pomocą panelu N Źródło: Blender.
Uwa ga
Pamiętaj o ustawieniu relacji 1:1 między jednostkami Blendera i Unity (była o tym mowa w rozdziale 2.).
1
Autor tak nazywa panel Properties (właściwości) wysuwany z prawej krawędzi okna widokowego po wciśnięciu klawisza N — przyp. tłum.
Używanie klocka podstawowego
Używanie klocka podstawowego Zdefiniowanie klocka podstawowego to chyba najważniejszy moment w całym procesie budowy środowiska modułowego. Element ten wytycza swego rodzaju jednostkę, według której będą ustalane wymiary wszystkich modułów. Oto kilka ogólnych wskazówek dotyczących postępowania z klockiem podstawowym, gdy już zostaną ustalone jego wymiary i położenie w blenderowej scenie. ■ Zachowaj kopię bezpieczeństwa. Nigdy nie modeluj, nie edytuj i nie usuwaj pierwotnego klocka podstawowego. Zawsze miej jego kopię w scenie jako wzorzec do ustalania wymiarów innych modułów środowiska. Po zdefiniowaniu wszystkich parametrów klocka powiel go i używaj kopii, a oryginał umieść na odrębnej, niezależnej warstwie. ■ Używaj go wyłącznie jako wzorca. Nie modeluj klocka podstawowego. Nie poprawiaj ani nie zmieniaj jego wierzchołków. On jest swego rodzaju szablonem, na podstawie którego tworzysz inne moduły środowiska. Wyznacza objętość i graniczne wymiary najmniejszego z tworzonych modułów. Traktuj go jak element pomocniczy przy modelowaniu modułów. ■ Używaj go do ustalania wymiarów większych elementów. Z faktu ustanowienia klocka podstawowego wzorcem dla modułów środowiska nie wynika wcale, że wszystkie moduły muszą mieć takie same jak on wymiary. Klocek podstawowy może reprezentować np. tylko część drzwiową jakiegoś pomieszczenia, włącznie z fragmentami ścian, sufitu i podłogi. Jeśli chcesz utworzyć większe moduły, jak chociażby korytarze, możesz to śmiało zrobić. Zadbaj tylko, żeby ich wymiary były całkowitymi wielokrotnościami wymiarów klocka podstawowego (2×, 3×, 4× itd.). Nie używaj wartości ułamkowych ani takich, które nie są podzielne przez żaden wymiar klocka podstawowego. Tylko wtedy wszystkie moduły (włącznie z klockiem podstawowym) będą do siebie pasowały i będziesz mógł z nich ułożyć większą całość. Jeśli nadasz im wymiary zbyt zróżnicowane, żadna układanka się nie uda i nie będziesz mógł wykorzystać zalet modułowości (rysunek 3.7).
Opracowywanie modułów w Blenderze Wypracowanie sobie nawyku modelowania w kategoriach segmentów, kostek i klocków podstawowych to jeszcze nie wszystko, jeśli chodzi o tworzenie środowisk w Blenderze. Powinieneś też wiedzieć, jak ustawić pewne parametry aplikacji, aby praca przebiegała sprawniej. Oto kilka wskazówek na ten temat.
83
84
Rozdział 3
Modułowe środowiska i siatki statyczne
Rysunek 3.7. Wymiary większych elementów powinny być całkowitymi wielokrotnościami wymiarów klocka podstawowego. Tutaj segment korytarzowy jest dwa razy dłuższy od segmentu narożnego Źródło: Blender.
Odwracanie normalnych Środowiska zamknięte, takie jak pokoje, jaskinie czy dziuple, są przez gracza oglądane raczej od wewnątrz niż z zewnątrz. A zatem gdy umieścisz w scenie nowy sześcian lub inną bryłę z zamiarem wymodelowania jakiegoś wnętrza, najprawdopodobniej będziesz chciał widzieć wszystko od środka. To z kolei oznacza, że wszystkie wielokąty składające się na podłogę, sufit i ściany powinny być zwrócone do wewnątrz bryły, a nie na zewnątrz. W tej sytuacji konieczne będzie odwrócenie normalnych przypisanych tym wielokątom. Zabieg ten możesz wykonać następująco: 1. Włącz tryb edycji ścianek. 2. Wciśnij klawisze Ctrl+A lub wybierz polecenie Select/Select All (zaznacz/zaznacz wszystko), aby zaznaczyć wszystkie ścianki obiektu. 3. Wybierz polecenie Mesh/Normals/Flip Normals (siatka/normalne/odwróć normalne). Blender odwróci normalne, co da efekt wizualny taki jak na rysunku 3.8. Uwa ga
Jeśli używasz domyślnych ustawień Blendera i masz włączony tryb wyświetlania Solid (bryłowy), to odwrócenie normalnych może nie dać żadnego efektu wizualnego. W takim przypadku koniecznie przeczytaj następny punkt!
Opracowywanie modułów w Blenderze
Rysunek 3.8. Sześcian przed odwróceniem normalnych i po takim zabiegu. Odwracanie normalnych powoduje coś w rodzaju wywracania siatki na lewą stronę Źródło: Blender.
Ukrywanie ścianek odwróconych tyłem Domyślnie Blender wyświetla wszystkie wielokąty jako dwustronne, mimo że w grach wideo stosuje się wielokąty jednostronne. W Unity, podobnie jak w grach wideo, oglądać można tylko jedną stronę wielokąta. Jeśli spojrzysz na wielokąt z drugiej strony, będzie on po prostu niewidoczny. Natomiast w Blenderze rysowane są obie strony, co oznacza, że nawet po odwróceniu normalnych wszystkie ścianki są nadal widoczne. Takie zachowanie aplikacji może utrudniać modelowanie modułów środowiska, bo przecież często będziesz chciał obejrzeć wnętrze obiektu, np. pokoju, a wtedy ścianki odwrócone tyłem będą tylko przeszkadzać. Na szczęście Blender oferuje funkcję o nazwie Backface Culling (odrzucanie ścianek odwróconych tyłem). Znajdziesz ją w sekcji Shading (cieniowanie) rolety Display (wyświetlanie) będącej częścią panelu N. Aby wymusić renderowanie tylko jednej strony wielokątów, po prostu włącz tę funkcję (rysunek 3.9).
Funkcja przyciągania Jeśli zamierzasz tworzyć dające się łatwo łączyć moduły środowiskowe, trudno Ci będzie obejść się bez korzystania z funkcji przyciągania. Na dobrą sprawę powinna ona stać się Twoją drugą naturą na czas pracy w Blenderze. Dzięki niej precyzyjne wyrównywanie wierzchołków czy całych modeli staje się niezwykle proste. Funkcja przyciągania ma kilka form, ale teraz interesować nas będą tylko przyciąganie do wierzchołków i przyciąganie przyrostowe, które umożliwia ustawianie obiektów zgodnie z siatką konstrukcyjną. Wszystko to ma niebagatelne znaczenie przy modelowaniu elementów, które później będą się miały ładnie łączyć w jedną całość, gdy po
85
86
Rozdział 3
Modułowe środowiska i siatki statyczne
Rysunek 3.9. Aby zmusić Blendera do renderowania tylko jednej strony ścianek, zaznacz opcję Backface Culling Źródło: Blender.
przeniesieniu do Unity będziesz z nich układał środowisko gry. Jeśli choćby jeden element będzie niedopasowany do reszty, może powstać wyraźne zaburzenie w konstrukcji środowiska i cała Twoja praca pójdzie na marne. Jeśli ustawisz taki segment obok innego, może powstać między nimi luka, ponieważ ich wierzchołki nie będą się pokrywały. Czasami luka może być tak duża, że gracz, trafiwszy na nią, wypadnie poza środowisko gry! Oczywiście można łatać takie miejsca przez wstawianie specjalnych, kolidujących prymitywów, które zapobiegną wypadaniu gracza, ale takie rozwiązanie jest po prostu powierzchowne i z daleka pachnie tandetą. Tak naprawdę nie sięga do źródła problemu, który tkwi w samej strukturze siatki. Dlatego zawsze stosuj funkcję przyciągania i modeluj wszystko z należytą precyzją. Aby zastosować przyciąganie przyrostowe, włącz ogólną funkcję przyciągania (służy do tego przycisk z ikoną w kształcie magnesu) i ustaw tryb Increment (przyrost) — tak jak na rysunku 3.4. Gdy ten tryb przyciągania jest aktywny, wszystkie transformacje mogą być wykonywane jedynie skokowo o ściśle określoną wartość. Jest to szczególnie przydatne, gdy w grę wchodzą transformacje na poziomie obiektowym (a nie wierzchołkowym, krawędziowym czy wielokątowym), bo pozwala na bardzo dokładne rozmieszczanie całych obiektów. Poza tym, jak już opisywałem w podrozdziale „Rozpoczynanie prac nad środowiskiem modułowym”, przyciąganie przyrostowe może być pomocne również przy umieszczaniu środka obiektu dokładnie w początku globalnego układu współrzędnych.
Opracowywanie modułów w Blenderze
W przeciwieństwie do przyciągania przyrostowego, które dopuszcza transformacje skokowe, przyciąganie do wierzchołków powoduje automatyczne wyrównywanie jednego lub kilku wierzchołków siatki z jakimś innym wierzchołkiem. Jeśli chcesz wyrównać dwa lub więcej wierzchołków względem którejkolwiek (lub wszystkich) osi, to właśnie przyciąganie do wierzchołków jest tym, czego potrzebujesz! Dla przykładu rozważmy sytuację z rysunku 3.10. Podczas modelowania ściany w module środowiskowym przypadkowo przesunąłem górne wierzchołki w prawo, co spowodowało lekkie pochylenie pionowych wcześniej krawędzi.
Rysunek 3.10. Brak właściwego wyrównania wierzchołków ściany Źródło: Blender.
Aby na powrót ustawić te wierzchołki w linii prostej z wierzchołkami leżącymi niżej, mogłem je po prostu przeciągnąć za pomocą narzędzia Transform (przekształć) do położenia, przy którym krawędzie ściany byłyby mniej więcej proste. Lecz z pomocą funkcji przyciągania mogłem sprawić, że na nowo stały się dokładnie proste. Aby to uzyskać, wykonałem następujące czynności: 1. Zaznaczyłem wszystkie wierzchołki wymagające przesunięcia. 2. Włączyłem funkcję przyciągania do wierzchołków (zob. rysunek 3.4). 3. Ograniczyłem możliwość przesuwania tylko wzdłuż osi X (można to zrobić za pomocą gizma translacji lub przez wciśnięcie kolejno klawiszy W i X). 4. Trzymając wciśnięty przycisk myszy, przesunąłem zaznaczone wierzchołki w pobliże właściwie ustawionych wierzchołków leżących niżej. Wtedy zadziałała funkcja przyciągania i umieściła przeciągane wierzchołki dokładnie tam, gdzie powinny być. W rezultacie uzyskałem perfekcyjne wyrównanie wierzchołków przesuwanych z docelowymi (rysunek 3.11).
87
88
Rozdział 3
Modułowe środowiska i siatki statyczne
Rysunek 3.11. Funkcja przyciągania do wierzchołków wyrównuje wierzchołki przesuwane z docelowymi Źródło: Blender.
Uwa ga
Więcej informacji o wszystkich opcjach funkcji przyciągania znajdziesz w dokumentacji Blendera dostępnej pod adresem: http://wiki.blender.org/index.php/Doc:2.6/Manual/3D_interaction/Transform_ Control/Snap_to_Mesh.
N-kąty Siatki składają się z wielokątów — dlatego często mówi się o nich: siatki wielokątne. Najprostszym wielokątem jest trójkąt, który ma trzy boki. Potem są czworokąty o czterech bokach. Teoretycznie można wymieniać dalej: pięciokąty, sześciokąty, ośmiokąty, dziesięciokąty, dziewiętnastokąty itd. do nieskończoności. Każdy z tych kształtów charakteryzuje się określoną liczbą boków stanowiących jego obwód. Co ciekawe, każdy z tych wielokątów można zbudować z odpowiedniej liczby właściwie dobranych trójkątów.
Opracowywanie modułów w Blenderze
W modelowaniu 3D oraz w grach wideo wszystkie wielokąty mające więcej niż cztery boki nazywane są n-kątami (n-gons). Powiem krótko: to są Twoi wrogowie. Nigdy nie powinny się znaleźć w żadnej wymodelowanej przez Ciebie siatce. Dlatego jeszcze na etapie pracy w Blenderze powinieneś podjąć odpowiednie kroki, aby się ich pozbyć. To znaczy: jeśli znajdziesz jakiś n-kąt, musisz go koniecznie porozcinać na trójkąty lub czworokąty. Te drugie nie zawsze dadzą się utworzyć (to już zależy od natury siatki), ale trójkąty można wyznaczyć zawsze (rysunek 3.12).
Rysunek 3.12. Cylindryczny obiekt z n-kątową podstawą. N-kąt ma tu postać czternastokąta foremnego. Taki obiekt będzie sprawiał problemy w grze 3D Źródło: Blender.
Blender oferuje kilka narzędzi do retopologizacji modeli, czyli do takiego przebudowywania ich siatek, aby składały się wyłącznie z trójkątów i czworokątów. Jednym z tych narzędzi jest Triangulate Faces (triangulacja ścianek) dostępne w trybie edycyjnym jako polecenie Mesh/Faces/Triangulate Faces (siatka/ścianki/triangulacja ścianek) — rysunek 3.13. Działa ono tylko na zaznaczone ścianki, ale błyskawicznie zamienia je na zbiór trójkątów. Może być szybkim sposobem na pozbycie się n-kątów przed wyeksportowaniem siatki do Unity, chociaż jego wadą jest to, że nie pozwala na żadną ingerencję w przeprowadzany podział. Drugim, znacznie elastyczniejszym narzędziem do retopologizacji modeli przez eliminowanie n-kątów jest Knife (nóż). Podobnie jak poprzednie jest dostępne w trybie edycyjnym i umożliwia interaktywne dzielenie oraz reorganizowanie siatki przez rozcinanie jej
89
90
Rozdział 3
Modułowe środowiska i siatki statyczne
Rysunek 3.13. Za pomocą narzędzia Triangulate Faces błyskawicznie podzielisz każdy czworokąt i każdy n-kąt na trójkąty Źródło: Blender.
wzdłuż rysowanych w czasie rzeczywistym linii (rysunek 3.14). W gruncie rzeczy jest to „ręczne” korygowanie siatki, które trwa dłużej niż przy stosowaniu narzędzia Triangulate Faces, ale za to mamy nieograniczoną swobodę w decydowaniu, jak dana ścianka ma być podzielona.
Rysunek 3.14. Krojenie ścianek modelu na trójkąty i czworokąty za pomocą narzędzia Knife Źródło: Blender.
Opracowywanie modułów w Blenderze
Wyszukiwanie n-kątów Na pozór wydaje się, że wyszukanie n-kąta nie powinno być trudne. W końcu wystarczy policzyć, ile boków ma dana ścianka, aby stwierdzić, czy jest n-kątem, czy nie. Lecz w praktyce, gdy siatka składa się z setek czy nawet tysięcy ścianek, wskazanie wszystkich n-kątów (jeśli są jakieś) wcale nie musi być rzeczą łatwą. Na szczęście Blender znów przychodzi z pomocą i umożliwia zaznaczenie wszystkich n-kątów w siatce przez kliknięcie jednego przycisku (no, może kilku). Procedura taka wygląda następująco: 1. Włącz tryb edycji ścianek. 2. Jeśli istnieje jakieś zaznaczenie, usuń je. 3. Wybierz polecenie Select/Select Faces by Sides (zaznacz/zaznacz ścianki według boków) — tak jak na rysunku 3.15.
Rysunek 3.15. Narzędzie Select Faces by Sides umożliwia zaznaczenie wszystkich ścianek o zadanej liczbie boków Źródło: Blender.
4. Aby wyszukać wszystkie n-kąty w siatce, wpisz w polu Number of Verices (liczba wierzchołków), które widnieje teraz w przyborniku, cyfrę 4.
91
92
Rozdział 3
Modułowe środowiska i siatki statyczne
5. Z listy rozwijanej Type (typ) wybierz opcję Greater Than (więcej niż), bo przecież n-kąty mają więcej niż cztery wierzchołki. 6. Wyłącz opcję Extend (poszerz). Przy takiej kombinacji ustawień zostaną zaznaczone wszystkie ścianki, które mają więcej niż cztery wierzchołki, czyli wszystkie n-kąty (jeśli jakieś w ogóle są). Jeśli nie widzisz, czy jakieś ścianki zostały zaznaczone, czy nie, sprawdź liczbę zaznaczonych ścianek wyświetlaną na nagłówku ekranu informacyjnego w prawym górnym rogu interfejsu (rysunek 3.16).
Rysunek 3.16. Zaznaczanie wszystkich n-kątów w siatce Źródło: Blender.
Cofanie operacji i usuwanie duplikatów Gdy pracujesz w Blenderze, z rozwagą używaj polecenia Undo (cofnij). Niektóre polecenia (w niektórych wersjach), np. Extrude (wytłocz), są w rzeczywistości operacjami wieloetapowymi, mimo że użytkownik uruchamia ich wykonanie jednym kliknięciem. Dlatego też aby ich skutki zostały cofnięte całkowicie, potrzebne jest wielokrotne użycie polecenia Undo. Nie jest to zbyt intuicyjne i często powoduje (nie licząc innych przyczyn), że w siatce pojawiają się zdublowane wierzchołki (tzw. duplikaty). Powstają podczas modelowania i zajmują dokładnie te same miejsca co inne wierzchołki. Zatem niektóre wierzchołki siatki stają się podwójne i w oknie widokowym nie da się ich jednoznacznie zlokalizować, bo z wyglądu nie różnią się od pojedynczych. Najlepszym sposobem na pozbycie się takich niepożądanych duplikatów jest zaznaczenie wszystkich wierzchołków (wciśnij Ctrl+A) i wybranie polecenia Mesh/Vertices/Remove Doubles (siatka/wierzchołki/usuń duplikaty) — rysunek 3.17.
Opracowywanie modułów w Blenderze
Rysunek 3.17. Aby rozwiązać problem zdublowanych wierzchołków, użyj polecenia Remove Doubles Źródło: Blender.
Mirroring Podczas modelowania obiektów symetrycznych, np. korytarzy, tuneli, często wygodnie jest wymodelować tylko jedną połowę takiego obiektu (rysunek 3.18), a drugą utworzyć jako lustrzane odbicie tamtej. Zabieg taki nazywany jest mirroringiem.
Rysunek 3.18. W technice mirroringu wystarczy wymodelowanie połowy obiektu Źródło: Blender.
93
94
Rozdział 3
Modułowe środowiska i siatki statyczne
Po wymodelowaniu połowy modułu możesz wygenerować jej symetryczny duplikat, przypisując jej modyfikator Mirror (lustro). Dostęp do tego modyfikatora uzyskasz na zakładce Modifiers (modyfikatory) panelu Properties (właściwości). Po prostu kliknij listę rozwijaną Add Modifier (dodaj modyfikator) i wybierz z niej pozycję o nazwie Mirror (rysunek 3.19). Stosowanie tego modyfikatora może zaoszczędzić dużo czasu, ale nie należy z tym przesadzać. Gracz szybko zauważy symetryczność, powtarzalność i schematyczność środowiska, w którym się porusza.
Rysunek 3.19. Generowanie drugiej strony korytarza za pomocą modyfikatora Mirror Źródło: Blender.
Uwa ga
Modyfikator Mirror automatycznie ustawia płaszczyznę (lub oś) symetrii w taki sposób, aby przechodziła przez środek (środek transformacji) obiektu. Jeśli więc chcesz, żeby zastosowanie modyfikatora przyniosło spodziewany rezultat, ustaw wcześniej środek obiektu we właściwym miejscu. Wskazówki, jak to zrobić, znajdziesz w podrozdziale „Rozpoczynanie prac nad środowiskiem modułowym”. Pamiętaj, że wygenerowane lustrzane odbicie nie stanowi pełnej jedności z oryginałem tak długo, jak długo modyfikator Mirror pozostaje niezatwierdzony. Innymi słowy: oryginał i jego lustrzane odbicie staną się jedną całością dopiero wtedy, gdy klikniesz przycisk Apply (zastosuj) odpowiadający temu modyfikatorowi.
Grupowanie wierzchołków Tworzenie siatek w programach do grafiki 3D nieuchronnie wiąże się z manipulowaniem dużymi liczbami wierzchołków, krawędzi i ścianek, które w sumie stanowią podstawowy materiał modelarski. Szczególnie liczne są wierzchołki, a właśnie one są
Opracowywanie modułów w Blenderze
najczęściej zaznaczane, gdy trzeba wskazać jakiś obszar siatki w celu przeprowadzenia operacji modelarskich. Jednak zaznaczanie każdego wierzchołka oddzielnie może być uciążliwe, a już na pewno byłoby frustrujące, gdyby trzeba było te same wierzchołki zaznaczać wielokrotnie (może się przecież zdarzyć, że po pewnym czasie od wykonania jednej operacji na zaznaczonych wierzchołkach będziesz musiał wykonać inną na dokładnie tych samych wierzchołkach). Na szczęście Blender oferuje możliwość zapisania zestawu raz zaznaczonych wierzchołków i potem przywoływania go, gdy te same wierzchołki mają być użyte. Zestawy te, zwane w Blenderze grupami, można tworzyć, przywoływać i usuwać za pośrednictwem zakładki Data (dane) panelu Properties (rysunek 3.20).
Rysunek 3.20. Do zarządzania zestawami zaznaczonych wierzchołków służy sekcja Vertex Groups (grupy wierzchołków) zakładki Data panelu Properties Źródło: Blender.
Aby utworzyć nową grupę, kliknij przycisk oznaczony znakiem plus (+) i wpisz stosowną nazwę (rysunek 3.21). Nazwa powinna być unikatowa i najlepiej, jeśli będzie opisowa. Po utworzeniu grupy i zaznaczeniu jej nazwy na zakładce Data możesz jej przypisać zestaw zaznaczonych wierzchołków przez zwykłe kliknięcie przycisku Assign (przypisz) widocznego pod listą dostępnych grup (rysunek 3.22). Aby później ponownie zaznaczyć te same wierzchołki, musisz tylko wskazać właściwą grupę i kliknąć przycisk Select (zaznacz), który również widać na rysunku 3.22.
95
96
Rozdział 3
Modułowe środowiska i siatki statyczne
Rysunek 3.21. Tworzenie grupy w celu zapisania zestawu zaznaczonych wierzchołków Źródło: Blender.
Rysunek 3.22. Przypisywanie wierzchołków do grupy i ponowne ich zaznaczanie Źródło: Blender.
Opracowywanie modułów w Blenderze
Parametry wyświetlania siatki Możliwe, że to tylko moja ułomność, ale stwierdziłem, że przy domyślnych ustawieniach Blendera wierzchołki są słabo widoczne i przez to praca z nimi jest utrudniona. (Może masz takie same odczucia?) Żeby móc pracować efektywniej, niemal zawsze zmieniam sposób, w jaki wierzchołki są renderowane, w oknie widokowym. Oto zmiany, które wprowadzam: 1. Z menu File (plik) wybieram polecenie User Preferences (preferencje użytkownika), tak jak na rysunku 3.23, aby otworzyć okno dialogowe Blender User Preferences.
Rysunek 3.23. Przywoływanie okna dialogowego Blender User Preferences Źródło: Blender.
2. W oknie Blender User Preferences klikam zakładkę Themes (motywy). 3. Z listy po lewej stronie wybieram pozycję 3D View (widok 3D), aby wyświetlić opcje interfejsu dla okna widokowego. 4. Dla opcji Vertex (wierzchołek) i Vertex Select (zaznaczenie wierzchołka) wybieram jaśniejsze kolory, co spowoduje, że wierzchołki zarówno niezaznaczone, jak i zaznaczone będą jaśniejsze. 5. Zwiększam wartość parametru Vertex Size (rozmiar wierzchołka) do 8 lub nawet 9, przez co wszystkie wierzchołki siatki stają się większe i lepiej widoczne. Dla mnie takie zmiany są wystarczające i dzięki nim nie tylko lepiej widzę wierzchołki, ale i łatwiej jest mi je zaznaczać podczas modelowania (rysunek 3.24).
97
98
Rozdział 3
Modułowe środowiska i siatki statyczne
Rysunek 3.24. Ustawianie kolorów i rozmiarów wierzchołków w oknie Blender User Preferences Źródło: Blender.
Mapowanie UV i tworzenie tekstur Modelowanie w kategoriach wierzchołków, krawędzi i ścianek to dopiero pierwszy krok w kierunku utworzenia kompletnego zestawu środowiskowego. Następny to mapowanie UV i pokrywanie modeli teksturami. Nadal pracujemy w Blenderze, ale do pomocy przy malowaniu i edycji tekstur angażujemy dodatkowo programy takie jak GIMP lub Photoshop. Do głosu dojdzie również Unity ze swymi wymaganiami i dobrze zrobisz, jeśli będziesz miał te wymagania przez cały czas na myśli. Właśnie takimi sprawami będziemy się zajmować w tym podrozdziale.
Wyznaczanie szwów, mapowanie UV i modelowanie Mapowanie UV to nic innego jak rozwijanie trójwymiarowej geometrii modelu na płaskiej powierzchni. Celem takiego zabiegu jest uzyskanie dwukierunkowej relacji między trójwymiarowym modelem a płaskim obrazem. Jeśli taką relację uda się utworzyć, piksele dwuwymiarowej tekstury będą mogły być rzutowane na powierzchnię modelu i nadawać mu określony wygląd. Dwukierunkowość relacji oznacza, że szczegóły namalowane bezpośrednio na powierzchni modelu w trzech wymiarach mogą być zrzutowane na płaską teksturę! Jednak żeby dokonać takiego rozwinięcia modelu, trzeba wcześniej wyznaczyć w jego siatce tzw. szwy (seams), czyli linie, wzdłuż których będzie można model rozciąć, aby w ogóle dało się go otworzyć i rozpłaszczyć. Proces ten należy przeprowadzać z wielkim namysłem, ponieważ Unity wymaga, aby, ze względu na szybkość działania, szwów było jak najmniej. Tak czy inaczej jakieś szwy są potrzebne, bo bez nich w ogóle nie da
Mapowanie UV i tworzenie tekstur
się modelu rozwinąć. Dla każdego wierzchołka na szwie Unity wstawi duplikat, a to oznacza, że szwy pośrednio przyczyniają się do wzrostu liczby wierzchołków modelu. Aby wyznaczyć szew w siatce modelu, zaznacz krawędzie, wzdłuż których ten szew powinien przebiegać, po czym kliknij w przyborniku przycisk Mark Seam (oznacz szew) lub z menu Mesh (siatka) wybierz polecenie Edges/Mark Seam (krawędzie/oznacz szew) — tak jak na rysunku 3.25.
Rysunek 3.25. Przed rozwinięciem siatki należy wyznaczyć szwy. Można to zrobić za pomocą przycisku Mark Seam w przyborniku lub polecenia Edges/Mark Seam Źródło: Blender.
Krawędź oznaczoną jako szew Blender wyświetla w kolorze czerwonym (pod warunkiem, że nie zmieniłeś ustawienia domyślnego). Gdyby się okazało, że szwy w ogóle nie są wyróżniane, sprawdź, czy przypadkiem nie wyłączyłeś funkcji odpowiedzialnej za wyświetlanie szwów. Aby ją włączyć, otwórz panel N i w sekcji Mesh Display (wyświetlanie siatki) zaznacz opcję Seams (szwy) — tak jak na rysunku 3.26. Po wyznaczeniu szwów możesz nakazać Blenderowi rozwinięcie siatki. Odpowiednim do tego poleceniem jest Unwrap (rozwiń). Dokładny opis procedury rozwijania siatek znajdziesz w dokumentacji Blendera (http://wiki.blender.org/index.php/Doc:2.6/Manual/ Textures/Mapping/UV/Unwrapping).
99
100
Rozdział 3
Modułowe środowiska i siatki statyczne
Rysunek 3.26. Żeby szwy były widoczne, trzeba włączyć ich wyświetlanie Źródło: Blender.
Wyznaczanie szwów często jest traktowane jako odrębny etap następujący po fazie modelowania, ale moim zdaniem jest to prosta droga do marnowania czasu. Uważam, że najlepszym rozwiązaniem jest wyznaczanie szwów podczas modelowania. Dlaczego? Ponieważ w trakcie modelowania często stosujemy powielanie dużych fragmentów siatki (np. przy stosowaniu mirroringu) i jeśli zawczasu wyznaczymy szwy, zostaną one skopiowane wraz z innymi elementami siatki — w ten sposób odpadnie nam połowa pracy.
Atlas tekstur i pokrywanie się współrzędnych UV Obecność materiału w scenie wiąże się z dodatkowym obciążeniem dla procesora i przyczynia się do obniżenia wydajności gry. Każdy materiał zwiększa liczbę wywołań rysujących. Im więcej materiałów, tym więcej takich wywołań. Jeśli zależy Ci na wydajności, musisz redukować ich liczbę do niezbędnego minimum. A zatem musisz po prostu ograniczyć liczbę użytych materiałów. Uwa ga
W Unity wywołanie rysujące to wewnętrzny proces, który aplikacja wykonuje automatycznie, aby wyświetlić trójwymiarową grafikę na ekranie monitora. W każdej klatce (a w ciągu sekundy może być wyświetlanych nawet 100 klatek) Unity wykonuje na ogół co najmniej jedno wywołanie rysujące, a każde z nich angażuje CPU i GPU. Im mniej takich wywołań musi wykonać Unity, aby wyrenderować scenę, tym większa będzie wydajność gry. Czynniki takie jak liczba materiałów w scenie mają bezpośredni wpływ na liczbę wywołań rysujących. Każdy unikatowy materiał wymaga przynajmniej jednego takiego wywołania.
Mapowanie UV i tworzenie tekstur
Jednym ze sposobów realizacji tego zalecenia jest używanie jak najmniejszej liczby tekstur rozpraszających światło. Od samego początku procesu mapowania UV siatek środowiskowych dąż do tego, by wszystkim modułom przypisać jedną dużą teksturę. Ten rodzaj tekstur, na które mapowanych jest wiele siatek, określa się mianem atlasu tekstur. Żeby zmapować wszystkie siatki środowiskowe na jedną teksturę, trzeba je rozwinąć w tej samej przestrzeni UV. W Blenderze należy w tym celu złączyć wszystkie siatki w jedną całość i dopiero wtedy można przeprowadzić mapowanie. Gdy mapowanie zostanie zakończone, należy siatki z powrotem rozdzielić, jeśli mają być eksportowane do Unity. Do łączenia siatek służy polecenie Join (złącz). Po prostu zaznacz wszystkie siatki, które mają być złączone, i wybierz Object/Join (obiekt/złącz), kliknij przycisk Join w przyborniku lub wciśnij klawisze Ctrl+J (rysunek 3.27).
Rysunek 3.27. Aby rozwinąć wszystkie siatki we wspólnej przestrzeni UV, należy je najpierw połączyć Źródło: Blender.
Gdy siatki utworzą jedną całość, można je rozwinąć jak zwykłą, pojedynczą siatkę. Warto przy tym pamiętać o możliwości pokrywania się współrzędnych UV. Jeśli siatka zawiera kilka obszarów o podobnej topologii, które powinny wyglądać tak samo — np. dwa fragmenty ściany lub dwoje podobnych drzwi — można im przypisać te same współrzędne UV i w ten sposób wielokrotnie wykorzystać ten sam fragment tekstury. To tak, jakby ich współrzędne UV ułożyć warstwowo jedne na drugich. Każdy z tych
101
102
Rozdział 3
Modułowe środowiska i siatki statyczne
obszarów siatki będzie więc miał przypisaną tę samą część tekstury. Jeśli zastosujesz tę technikę w każdym możliwym przypadku, oszczędności liczone w kategoriach rozmiarów tekstury mogą być znaczące. Są nawet przykłady pokazujące, jak przez umiejętne korzystanie z tych możliwości zdołano całe środowisko zmapować na jedną teksturę liczącą zaledwie 1024×1024 pikseli. Na rysunku 3.28 widać rozwinięcie UV przykładowego zestawu modułów środowiska, który można pobrać ze strony internetowej: ftp://ftp.helion.pl/przyklady/unible.zip zawierającej materiały uzupełniające do książki (Rozdział03/MateriałyDoPobrania). Podany folder zawiera zestaw siatek rozmaitych elementów środowiska (takich jak narożniki ścian, proste fragmenty korytarza, skrzyżowania typu T itp.), które można łączyć ze sobą jak bloki budowlane i tworzyć kompletne środowiska. Jest tam również tekstura. Zostawiłem w niej trochę wolnego miejsca dla innych elementów gry, które być może spróbujesz dodać. Uwa ga
Jeśli chcesz zobaczyć, co można osiągnąć przy użyciu małych tekstur w połączeniu z techniką pokrywania się współrzędnych UV, obejrzyj filmik zrealizowany przy użyciu UDK i zamieszczony pod adresem: http://vimeo.com/35470093.
Rysunek 3.28. Aby wielokrotnie wykorzystać przestrzeń tekstury, zastosuj pokrywanie się współrzędnych UV Źródło: Blender.
Ustalanie gęstości tekselowej Tekstury jako prostokątne obrazy mają szerokość i wysokość wyrażane w pikselach. Gdy rozwinięcie UV jest nakładane na teksturę, każda wyspa UV obejmuje pewien obszar tekstury i odwzorowuje go na siatkę. Liczba pikseli tekstury przypisana do wyspy UV jest nazywana gęstością tekselową.
Mapowanie UV i tworzenie tekstur
W ocenie gęstości tekselowej pomaga zastosowanie w edytorze UV specjalnej mapy szachownicowej (UV Grid). Podstawą tej oceny są wtedy kształty i rozmiary poszczególnych pól szachownicy. W szczególności chodzi o to, by: ■ wszystkie pola szachownicy po zrzutowaniu na siatkę były kwadratowe (miały szerokość równą wysokości); ■ wszystkie pola na wszystkich siatkach środowiska były tej samej wielkości. Spełnienie tych warunków gwarantuje właściwą jakość teksturowania, ponieważ każda siatka otrzyma piksele w ilości proporcjonalnej do jej fizycznych wymiarów. Przyjrzyjmy się temu dokładniej. Aby wygenerować w Blenderze mapę tekstury szachownicowej, wykonaj następujące czynności: 1. W oknie UV/Image Editor (edytor współrzędnych UV i obrazów) wybierz polecenie Image/New Image (obraz/nowy obraz). 2. Parametry nowego obrazu ustaw zgodnie z tym, co widać na rysunku 3.29. Upewnij się też, że wygenerowana tekstura zostanie nałożona na siatki i będzie widoczna w oknie widokowym. Możliwość obejrzenia jej na modelach ma zasadnicze znaczenie.
Rysunek 3.29. Aby wykryć ewentualne problemy mapowania, nałóż na modele specjalną teksturę w formie regularnej kratki UV Źródło: Blender.
Kratka UV nałożona na modele pozwala dość dobrze ocenić, jak piksele tekstury układają się na poszczególnych siatkach. Istotne są dwa szczegóły: ■ Jeśli pola szachownicy nie są kwadratami, lecz przyjmują kształt wydłużonych prostokątów lub przekrzywionych równoległoboków, to mapowanie wymaga korekty.
103
104
Rozdział 3
Modułowe środowiska i siatki statyczne
Wszystkie pola powinny mieć kształt kwadratu. W przeciwnym razie tekstura zostanie zdeformowana. ■ Upewnij się, że pola szachownicy na wszystkich modelach mają takie same wymiary. Jeśli tak nie jest, to znaczy, że rozkład pikseli teksturowych między poszczególne modele będzie nieproporcjonalny. A zatem może się okazać, że mały obiekt pochłania znaczną część tekstury, a duży — o wiele mniejszą. W takiej sytuacji mały obiekt będzie nadmiernie szczegółowy, a duży będzie rozmazany i mało wyraźny. Zachowanie stałych wymiarów pól szachownicy spowoduje, że rozkład gęstości tekselowej będzie równomierny (rysunek 3.30).
Rysunek 3.30. Skoryguj mapowanie UV, aby rozkład gęstości tekselowej był w miarę równomierny Źródło: Blender.
Importowanie i konfigurowanie środowisk w Unity Zestaw środowiska modułowego to zebrane razem wszystkie jego elementy, czyli moduły. Przez układanie, powielanie i łączenie tych elementów w Unity tworzysz rozmaite warianty środowiska dla swojej gry. Zazwyczaj każdy moduł jest eksportowany z Blendera jako odrębny i niezależny plik FBX. Proces eksportowania takich plików opisałem szczegółowo w rozdziale 2. Na rysunku 3.31 pokazałem przykładowy zestaw środowiskowy zaimportowany do projektu w Unity. Po zaimportowaniu zestawu musisz poświęcić trochę czasu na staranne jego skonfigurowanie. Szczególnie powinieneś się skoncentrować na mapowaniu światła i wykrywaniu kolizji. Jeśli siatki będą miały tylko jeden kanał (zestaw) UV, to Unity automatycznie użyje go do przypisania zarówno standardowych tekstur, jak i map światła.
Importowanie i konfigurowanie środowisk w Unity
Rysunek 3.31. Każdy moduł środowiska jest importowany do Unity jako odrębny plik FBX Źródło: Unity Technologies.
Pozostawienie takiego stanu rzeczy bez zmian najprawdopodobniej spowoduje nieprawidłowy wygląd modeli z powodu złego mapowania światła. Pewną poprawę możesz uzyskać przez włączenie opcji Generate Lightmap UVs (generuj współrzędne UV mapy światła) we właściwościach siatki (rysunek 3.32). Musisz się jednak liczyć z tym, że niektóre problemy oświetleniowe zostaną nadal nierozwiązane, zwłaszcza te na powierzchniach kulistych i organicznych. Wtedy będziesz musiał wrócić do Blendera i wygenerować odrębny kanał UV dla mapy światła. W Unity możesz łatwo wygenerować dane kolizyjne dla zaimportowanej siatki, bo wystarczy, że w panelu Inspector (inspektor) zaznaczysz wśród właściwości siatki opcję Generate Colliders (generuj zderzacze) — zob. rysunek 3.32. Spowoduje to, że każda siatka w scenie zostanie wyposażona w zderzacz siatkowy, który nie dopuści, by inny obiekt, np. gracz, przez nią przeniknął. Jednak włączenie tej opcji — i w ogóle zderzacze siatkowe — powoduje zwiększone zapotrzebowanie na moc obliczeniową, a zatem przy dużej liczbie siatek i zderzaczy wydajność gry może spaść poniżej dopuszczalnego poziomu, a na urządzeniach mobilnych gra może wcale nie zadziałać. Dlatego zderzaczy siatkowych należy używać tylko wtedy, gdy jest to konieczne ze względu na precyzję wykrywania kolizji. W większości przypadków wystarczają do tego metody przybliżone. Pozostaw więc opcję Generate Colliders wyłączoną i ręcznie dopasuj zwykłe zderzacze prostopadłościenne i kuliste do przybliżonych obrysów elementów środowiska. Zderzacze prostopadłościenne i kuliste nie wymagają takich mocy obliczeniowych jak siatkowe (rysunek 3.33).
105
106
Rozdział 3
Modułowe środowiska i siatki statyczne
Rysunek 3.32. Generowanie współrzędnych UV mapy światła dla siatki Źródło: Unity Technologies.
Rysunek 3.33. Składniki kolizyjne można dodawać do siatek ręcznie i mogą to być zderzacze w formie zwykłych prymitywów Źródło: Unity Technologies.
Stosowanie prefabrykatów
Stosowanie prefabrykatów Moduły wchodzące w skład zestawu środowiskowego są elementami podstawowymi wielokrotnego użytku. Są podstawowe w tym sensie, że dopiero w wyniku ich łączenia powstają rzeczy skomplikowane, a wielokrotność użytku oznacza, że każdy moduł może być użyty wiele razy i w różnych miejscach. W związku z tym często okazuje się (przekonasz się, gdy zaczniesz tworzyć sceny w Unity), że poziom zawiera wiele powtarzających się kombinacji tych samych modułów. Mogą to być np.: całe korytarze, narożne fragmenty pomieszczeń, hole, tunele czy pokoje, które pomimo że same składają się z kilku połączonych modułów, powtarzają się w określonych konfiguracjach w wielu miejscach tego samego poziomu. Niektóre środowiska mają przykładowo wiele jednakowych pokoi lub tak samo przebiegających korytarzy. Jest to wyższy poziom modułowości. Można oczywiście za każdym razem od nowa składać każdą taką kombinację z elementów podstawowych, ale dużo łatwiejsze jest stosowanie tzw. prefabrykatów, czyli pogrupowanych już modułów. Prefabrykaty, podobnie jak moduły, nadają się do wielokrotnego użytku, zupełnie jakby były pojedynczymi siatkami (rysunek 3.34).
Rysunek 3.34. Kolekcja prefabrykatów, które można zastosować w wielu aranżacjach Źródło: Unity Technologies.
Aby w Unity utworzyć prefabrykat, po prostu kliknij prawym przyciskiem myszy puste miejsce w panelu Project i z podręcznego menu wybierz polecenie Create/Prefab (utwórz/prefabrykat) — rysunek 3.35. Następnie nadaj prefabrykatowi sensowną nazwę, która będzie opisywała jego zawartość.
107
108
Rozdział 3
Modułowe środowiska i siatki statyczne
Rysunek 3.35. Tworzenie prefabrykatu Źródło: Unity Technologies.
Po utworzeniu prefabrykatu wskaż wszystkie siatki, które powinny się w nim znaleźć. Najpierw wskaż tę, która będzie nadrzędna, a potem, korzystając z panelu Hierarchy (hierarchia), ustal, które mają być podrzędne. Gdy już relacja nadrzędności zostanie ustalona, zaznacz obiekt nadrzędny i przeciągnij go do prefabrykatu w panelu Project. Wraz z obiektem nadrzędnym znajdą się tam również obiekty podrzędne (rysunek 3.36).
Rysunek 3.36. Tworzenie prefabrykatów z ustalonej konfiguracji siatek Źródło: Unity Technologies.
Wsad statyczny
Wsad statyczny Większość elementów środowiska, takich jak ściany, podłogi czy sufity, prawdopodobnie w ogóle nie będzie się przemieszczać ani zmieniać w trakcie gry. Po prostu takie obiekty zawsze będą tkwić na swoich miejscach jako elementy scenografii. A skoro są niezmienne, nazwiemy je obiektami statycznymi. W przeciwieństwie do nich obiekty takie jak postać gracza, wrogowie i wszelkie modyfikowalne rekwizyty będziemy nazywać dynamicznymi. Jeśli środowisko zawiera elementy statyczne, powinieneś je oznaczyć w edytorze Unity, aby mogły być renderowane w trybie wsadowym, co może znacznie poprawić wydajność gry na wszystkich platformach, zarówno stacjonarnych, jak i przenośnych. W tym celu zaznacz wszystkie statyczne elementy środowiska, a następnie włącz opcję Static (statyczne) w panelu Inspector — tak jak na rysunku 3.37.
Rysunek 3.37. Wszystkie nieruchome obiekty oznacz jako wsad statyczny Źródło: Unity Technologies.
Podsumowanie W tym rozdziale skupiliśmy się na modelowaniu środowiska z uwzględnieniem jego modułowości. Opisany tok pracy wymagał użycia dwóch aplikacji: Blendera i Unity. W pierwszej chwili tworzenie środowiska modułowego wydaje się łatwe, bo przypomina zabawę klockami. Ogólnie rzecz biorąc, to prawda, ale po głębszej analizie całego procesu widać, że jest to zajęcie dość pracochłonne i wymagające znajomości wielu tajników. Ważne jest zachowanie odpowiednich proporcji i rozmiarów poszczególnych modułów, aby później dały się łatwo łączyć w jedną większą całość. Dużo uwagi trzeba też poświęcić kwestiom mapowania UV i tworzenia prefabrykatów. Krótko mówiąc: tworzenie środowiska techniką modułową wymaga trochę czasu i nie łudź się, że uda
109
110
Rozdział 3
Modułowe środowiska i siatki statyczne
Ci się to zrobić w pośpiechu. Dokładnie zaplanuj wszystkie moduły, ustal ich wymiary i dobrze opracuj mapowanie UV, a w ogólnym rozrachunku zaoszczędzisz niemało czasu. Podczas lektury tego rozdziału możesz używać plików ćwiczeniowych zawierających kompletny zestaw środowiska modułowego. Pliki te możesz pobrać ze strony: ftp://ftp. helion.pl/przyklady/unible.zip. Jeśli zaciekawiła Cię problematyka modelowania środowisk i chciałbyś się dowiedzieć czegoś więcej, zapraszam do obejrzenia mojego poradnika filmowego pt. Modular Asset Building dostępnego pod adresem: www.3dmotive.com.
R OZ DZ I AŁ 4
T EREN
Sztuka prostoty jest tajemnicą złożoności. — Douglas Horton Po przeczytaniu tego rozdziału powinieneś: ■ znać zalety i wady systemu generowania terenów w Unity; ■ znać alternatywne metody tworzenia terenów dostępne w Blenderze; ■ umieć rzeźbić tereny i malować je teksturami; ■ umieć posługiwać się blenderowymi narzędziami rzeźbiarskimi; ■ umieć tworzyć geometrie dróg pasujące do terenu. Niemal każda gra, której akcja toczy się w środowisku plenerowym, a więc w lesie, na pustyni, na nizinie, w górach czy na ulicy, wymaga odpowiednio wymodelowanego terenu — terenu w znaczeniu technicznym, a nie potocznym. Dla nas pojęcie teren będzie oznaczało dużą i zazwyczaj gęstą siatkę, reprezentującą podłoże albo inaczej stały grunt danego poziomu. Siatki tego typu można tworzyć na wiele sposobów. Unity oferuje łatwy, ale wydajny system generowania terenów, który pokrótce omówię w następnym podrozdziale. Ma on jednak kilka istotnych ograniczeń i twórcy gier często zmuszeni są do szukania innych rozwiązań. Jednym z nich jest ręczne modelowanie terenu w odpowiedniej do tego aplikacji, np. w Blenderze. I właśnie tym zajmiemy się w pozostałej części rozdziału.
Tworzenie terenu w Unity Unity zawiera mnóstwo narzędzi przeznaczonych do rzeźbienia i malowania rozległych siatek mogących stanowić podłoże dla sceny. W tym podrozdziale omówię je pokrótce i wskażę ich wady oraz zalety. Pełną dokumentację systemu tworzenia terenu w Unity 111
112
Rozdział 4
Teren
znajdziesz w oficjalnej witrynie tej aplikacji (http://docs.unity3d.com/Documentation/ Manual/Terrains.html). Na rysunku 4.1 jest pokazana scena z gry Boot Camp, do której teren został wykonany za pomocą narzędzi dostępnych w Unity.
Rysunek 4.1. Teren wygenerowany w systemie Unity dla gry Boot Camp. Jeśli chcesz, możesz pobrać ten teren za darmo z Unity Asset Store Źródło: Unity Technologies.
Prześledźmy typowy tok pracy konieczny do wygenerowania prostego terenu za pomocą narzędzi dostępnych w Unity. Zaprezentowany tu opis odnosi się do Unity 4.3. Wersje wcześniejsze wymagały trochę innego podejścia. Aby rozpocząć tworzenie terenu, po prostu wybierz polecenie GameObject/Create Other/Terrain (obiekt gry/utwórz inny/ teren), tak jak na rysunku 4.2. Na środku sceny pojawi się domyślna siatka terenu.
Rysunek 4.2. Generowanie terenu w Unity Źródło: Unity Technologies.
Tworzenie terenu w Unity
Uwa ga
Jeśli interesuje Cię tworzenie terenów w starszych wersjach Unity, obejrzyj nagrany przeze mnie kurs wideo pt. Unity Assets & Terrain dostępny pod adresem: www.3dmotive.com.
Parametry terenu Przed przystąpieniem do rzeźbienia gór, przepaści, brzegów rzeki i innych szczegółów krajobrazowych musisz skonfigurować ustawienia siatki terenu. Są one dostępne w panelu Inspector (inspektor), gdy siatka terenu jest zaznaczona. Za ich pośrednictwem możesz ustalić wymiary terenu, jego powierzchnię i ogólną topologię. Wygenerowana przez Unity siatka terenu ma wymiary 2000×2000 jednostek globalnych, co w praktyce oznacza powierzchnię 4 000 000 m2! Nie zawsze tak duży obszar jest potrzebny, więc jeśli wystarczy Ci teren o mniejszej powierzchni, zmniejsz te wymiary. Dla potrzeb prezentowanego przykładu ustawiłem 512×512 jednostek globalnych, a pozostałe parametry pozostawiłem bez zmian (rysunek 4.3).
Rysunek 4.3. Aby przygotować teren do rzeźbienia, skonfiguruj jego ogólną topologię Źródło: Unity Technologies.
Uwa ga
Przy tworzeniu terenów lubię umieszczać w scenie źródło światła kierunkowego (zob. rysunek 4.3), które ułatwia mi obserwowanie form i kształtów powstających w trakcie rzeźbienia. Nie jest to bezwzględnie konieczne, ale w oczywisty sposób się przydaje.
113
114
Rozdział 4
Teren
Ważnym parametrem terenu jest Heightmap Resolution (rozdzielczość mapy wysokości). Ustawienie to określa pikselowe wymiary kwadratowej tekstury, której Unity używa do deformowania powierzchni terenu. Jej piksele są rzutowane na siatkę terenu i w zależności od jasności powodują przesuwanie wierzchołków w górę (piksele jasne) lub w dół (piksele ciemne). Im większa będzie rozdzielczość tej tekstury, tym więcej szczegółów krajobrazowych będzie można w niej zawrzeć, ale z drugiej strony wzrośnie zapotrzebowanie na pamięć i obniży się wydajność gry. Trzeba więc zachować umiar i postępować zgodnie z zasadą: zastosuj wartość najniższą spośród odpowiednich dla danego projektu.
Rzeźbienie terenu Za pomocą narzędzi służących do modelowania terenu możesz rzeźbić siatkę w oknie widokowym w sposób interaktywny, czyli z możliwością obserwowania na bieżąco skutków każdego posunięcia. W istocie rzeczy wszystko sprowadza się do modyfikowania wewnętrznej tekstury wysokościowej, o której wspomniałem w poprzednim punkcie. Przeciąganie narzędziem po siatce terenu powoduje malowanie pikseli tekstury, a te z kolei deformują siatkę. Gdy w scenie zaznaczona jest siatka terenu, w panelu Inspector dostępne są rozmaite pędzle (Brushes), co widać na rysunku 4.4. Po uaktywnieniu takiego pędzla po prostu kliknij teren, aby uzyskać wzniesienie, lub kliknij przy wciśniętym klawiszu Shift, aby uzyskać obniżenie istniejącego wzniesienia. Za pomocą parametrów Brush Size (rozmiar pędzla) i Opacity (krycie) możesz regulować zakres i siłę działania używanego narzędzia. Górzysty krajobraz pokazany na rysunku 4.4 wymodelowałem za pomocą podstawowego pędzla w kształcie koła. Do łagodzenia ostrych nierówności możesz używać pędzli wygładzających.
Rysunek 4.4. Teren możesz rzeźbić metodą malowania Źródło: Unity Technologies.
Tworzenie terenu w Unity
Uwa ga
Więcej informacji na temat rzeźbienia terenu znajdziesz w moim kanale YouTube: www.youtube.com/ watch?v=IhhGWK5VYUs.
Malowanie terenu teksturami Po nadaniu siatce terenu właściwej formy czas na pomalowanie jej teksturami terenowymi. Dopiero wtedy zacznie wyglądać, jakby była wykonana z rzeczywistych materiałów, takich jak ziemia, trawa, skały, woda itp. Malowanie teksturami przypomina rzeźbienie, bo też wykonuje się je za pomocą pędzli. Tekstury do pokrywania siatek terenu będziesz tworzył najczęściej sam, ale do przeprowadzania rozmaitych testów doskonale nadają się te, które oferuje Unity. Aby skorzystać z tych tekstur, musisz je zaimportować do projektu jako pakiet elementów terenu — po prostu wybierz polecenie Assets/Import Package/Terrain Assets (elementy/importuj pakiet/ elementy terenu), tak jak na rysunku 4.5.
Rysunek 4.5. Jeśli chcesz szybko poćwiczyć malowanie terenu teksturami, zaimportuj pakiet gotowych tekstur Źródło: Unity Technologies.
115
116
Rozdział 4
Teren
Jednak żeby zacząć malowanie, musisz najpierw zbudować paletę tekstur. W tym celu wykonaj następujące czynności: 1. Zaznacz w scenie teren. 2. W panelu Inspector kliknij zakładkę Paint Texture (malowanie teksturą) — rysunek 4.6.
Rysunek 4.6. Dodawanie tekstury do palety Źródło: Unity Technologies.
3. Kliknij przycisk Edit Textures (edytuj tekstury) i z menu, które się otworzy, wybierz Add Texture (dodaj teksturę). 4. Gdy otworzy się okno dialogowe Add Terrain Texture (dodaj teksturę terenu), wybierz teksturę (i mapę normalnych, jeśli to konieczne), która ma być dodana do palety, ustaw jej wymiary i kliknij przycisk Add (dodaj). Jeśli dodana tekstura była pierwszą na Twojej palecie, to zostanie automatycznie nałożona na całą siatkę terenu jako tekstura bazowa lub domyślna. W prezentowanym przykładzie wybrałem teksturę trawiastą i ustawiłem jej wymiary X oraz Y na 100 jednostek (rysunek 4.7). Rozmiary ustalane na palecie tekstur są wymiarami scenicznymi i decydują o krotności powtórzeń tekstury na powierzchni terenu. Przy większych wartościach liczba powtórzeń tekstury będzie mniejsza i na odwrót. 5. Teren zostanie pokryty w całości dodaną właśnie teksturą. Jeśli uznasz, że rozłożenie jej kafelków jest niewłaściwe i trzeba zmienić jej wymiary, jeszcze raz kliknij przycisk Edit Textures i wybierz tym razem polecenie Edit Texture (edytuj teksturę) — rysunek 4.8.
Tworzenie terenu w Unity
Rysunek 4.7. Dodawanie tekstury do palety Źródło: Unity Technologies.
Rysunek 4.8. Aby zmienić parametry tekstury, wybierz Edit Textures/Edit Texture Źródło: Unity Technologies.
6. Po dodaniu tekstury bazowej możesz dodawać kolejne, klikając za każdym razem przycisk Edit Textures i wybierając polecenie Add Texture. Jednak te tekstury, w przeciwieństwie do bazowej, nie będą już automatycznie nakładane i będziesz to musiał robić ręcznie, używając odpowiednich pędzli. Tam, gdzie namalujesz nową teksturę, tekstura bazowa zostanie zakryta. 7. Aby namalować nową teksturę, zaznacz ją na palecie w panelu Inspector i wybierz pędzel. Następnie za pomocą myszy „zamaluj” odpowiedni fragment terenu. Jak widać na rysunku 4.9, technika ta pozwala tworzyć złożone i bogate w szczegóły tereny.
117
118
Rozdział 4
Teren
Rysunek 4.9. Malowanie terenu teksturami dodanymi do palety Źródło: Unity Technologies.
Ocena terenów generowanych przez Unity Unity oferuje kilka naprawdę znakomitych narzędzi do modelowania terenów. W poprzednim podrozdziale poznałeś przykład tego, jak za pomocą zaledwie paru kliknięć i niewielkiej modyfikacji kilku parametrów można wygenerować teren kompletnie poteksturowany i w pełni dostosowany do naszych potrzeb. Jednak system ten ma kilka wad, i to tak poważnych, że wielu twórców gier postanowiło szukać innych rozwiązań. Wady te dotyczą następujących zagadnień: ■ Wydajność. Tradycyjnie już twórcy gier unikają stosowania terenów wygenerowanych w Unity, jeśli gra ma działać na urządzeniach przenośnych z systemami takimi jak Android czy iOS. Wynika to głównie z kafelkowania, dużej liczby ścianek i ogólnie wielkiej zasobożerności takich terenów. Wprawdzie najnowsze modele urządzeń mobilnych są szybkie i mają spore możliwości, co nawet do pewnego stopnia wpłynęło na zmianę powyższego nastawienia, ale nadal tereny generowane przez Unity pozostawiają wiele do życzenia pod względem optymalizacji. Często zawierają więcej wierzchołków niż to jest konieczne i zajmują zbyt dużo miejsca w pamięci. Dla komputerów stacjonarnych, konsoli czy najbardziej zaawansowanych urządzeń mobilnych może nie jest to wielki problem, ale dla przeciętnego sprzętu może stanowić barierę nie do pokonania, zwłaszcza przy braku optymalizacji również innych aspektów gry. Krótko mówiąc: żeby poprawić wydajność, warto poszukać innego rozwiązania. ■ Optymalność topologii. Topologia siatki, w tym również siatki terenu, wiąże się nie tylko z liczbą jej składników w postaci wierzchołków, krawędzi i ścianek, ale również z ich ułożeniem. Unity generuje tereny w sposób proceduralny, czyli buduje je według określonego przepisu, wprowadzając jedynie takie modyfikacje, które wynikają z wartości parametrów ustawionych na zakładce Terrain Settings (ustawienia
Modelowanie terenu w Blenderze
terenu) panelu Inspector. Parametry te omawiałem w poprzednim podrozdziale, w punkcie „Parametry terenu”, a należą do nich m.in.: Width (szerokość), Length (długość) i Height (wysokość). Poza tymi parametrami nie mamy na nic wpływu. Nie możemy np. decydować, gdzie siatka powinna być bardziej zagęszczona, a gdzie teselacja może być mniejsza. W rezultacie otrzymujemy siatkę uśrednioną, w której rozkład wierzchołków, krawędzi i ścianek jest niemal stały na całej powierzchni. Coś takiego na ogół nie sprzyja optymalizacji, bo przecież najczęściej spora część terenu nigdy nie będzie oglądana z bliska i w związku z tym nie musi być tak szczegółowo wymodelowana jak rejony, w których gra rzeczywiście się toczy. Aby zoptymalizować siatkę terenu pod tym kątem, trzeba znaleźć taki sposób generowania, który umożliwi nam większy wpływ na rozkład wierzchołków i ścianek. ■ Ograniczenia mapy wysokości. Wzniesienia i obniżenia terenu generowanego przez Unity są określone przez płaską, pikselową mapę wysokości, co ma dość poważne konsekwencje. Teoretycznie mapa ta znajduje się nad siatką terenu i jest rzutowana w dół, powodując wznoszenie siatki tam, gdzie piksele są jasne, i obniżanie jej w miejscach, gdzie piksele są ciemne. Można w ten sposób wyrzeźbić nawet bardzo skomplikowane krajobrazy, ale pewnych formacji nie da się uzyskać. Nie da się tą metodą wymodelować np. nor, jaskiń, dziupli i innych tego typu miejsc, w których między warstwami terenu występuje pusta przestrzeń. Jest to poważne ograniczenie metody generowania terenów na podstawie mapy wysokości. Do pewnego stopnia można to ograniczenie obejść, stosując dodatkowe obiekty siatkowe lub dodatkowe tereny, ale żeby problem w pełni rozwiązać, trzeba siatkę terenu wymodelować ręcznie w edytorze grafiki 3D. W sumie te trzy powody stanowią solidną podstawę do tego, by poważnie myśleć o szukaniu innych sposobów modelowania terenów. Chociaż narzędzia oferowane przez Unity są niezwykle użyteczne, na pewno nie raz będziesz zmuszony do ręcznego wymodelowania terenu, aby mieć nad tym procesem większą kontrolę i przeprowadzić go zgodnie z regułami optymalizacji. W następnych podrozdziałach sprawdzimy, czy Blender może być w tym pomocny.
Modelowanie terenu w Blenderze W Blenderze modelowanie terenu rozpoczynamy od utworzenia obiektu płaszczyzny i przeprowadzenia stosownej teselacji. Płaszczyznę można utworzyć za pomocą polecenia Add/Mesh/Plane (dodaj/siatkę/płaszczyznę) — tak jak na rysunku 4.10. Domyślnie płaszczyzna taka składa się tylko z jednej ścianki. W celu przeprowadzenia teselacji, która umożliwi wymodelowanie szczegółów terenu, użyj narzędzia Subdivide (podziel). Uważaj przy tym, żeby nie tworzyć więcej ścianek niż to jest konieczne, ale też nie przesadzaj w drugą stronę. Zbędne szczegóły zawsze można usunąć, bądź to ręcznie, bądź proceduralnie.
119
120
Rozdział 4
Teren
Rysunek 4.10. Modelowanie terenu w Blenderze rozpocznij od utworzenia płaszczyzny za pomocą odpowiedniego polecenia z menu Add. Nie zapomnij ustawić w panelu N długości i szerokości terenu (1 jednostka = 1 metr) Źródło: Blender.
Narzędzie Subdivide można uaktywnić, gdy obiekt jest zaznaczony i w trybie edycji. W tym celu wykonaj jedną z poniższych czynności: ■ Wybierz z menu polecenie Mesh/Edges/Subdivide (siatka/krawędzie/podziel). ■ W przyborniku kliknij przycisk Subdivide. ■ Jeśli włączyłeś system sterowania zgodny z programem Maya, kliknij siatkę prawym przyciskiem myszy przy wciśniętym klawiszu Ctrl (Windows) lub Command (Mac OS) i z menu Specials (specjalne) wybierz polecenie Subdivide — tak jak na rysunku 4.11. Prawdopodobnie będziesz musiał powtórzyć tę operację wielokrotnie, żeby uzyskać właściwą gęstość siatki. Uwa ga
Alternatywną i odwracalną metodą zagęszczania siatki jest stosowanie modyfikatora Multiresolution (wielorozdzielczość) z opcją Simple (prosty). Więcej informacji na temat tego modyfikatora znajdziesz w dokumentacji Blendera (http://wiki.blender.org/index.php/Doc:2.6/Manual/Modifiers/Generate/ Multiresolution). Gdy mamy już przygotowaną płaszczyznę, możemy przystąpić do wyboru jednej z trzech metod dalszego postępowania. O żadnej z nich nie da się jednoznacznie powiedzieć, że jest lepsza lub gorsza od pozostałych. Przy wyborze musisz się kierować swoimi upodobaniami i rodzajem terenu, który masz wymodelować. Oto te trzy metody:
Modelowanie terenu w Blenderze
Rysunek 4.11. Dzielenie siatki na mniejsze ścianki Źródło: Blender.
■ metoda edycji proporcjonalnej, ■ metoda tekstury przemieszczeń, ■ metoda rzeźbienia.
Metoda edycji proporcjonalnej Metoda ta wzięła swoją nazwę od funkcji Proportional Editing (edycja proporcjonalna), która w innych programach bywa też nazywana Soft Selection (miękkie zaznaczanie). Wszelkie wzniesienia i obniżenia terenu wykonuje się tutaj przez przeciąganie w górę bądź w dół wierzchołków siatki, a funkcja Proportional Editing przenosi te ruchy na sąsiednie obszary, powodując, że deformacje siatki stają się mniej lub bardziej łagodne. Zobaczmy, jak to wygląda w praktyce. Wykonaj następujące czynności: 1. Na pasku narzędziowym okna 3D View (widok 3D) kliknij przycisk Proportional Editing i wybierz opcję Connected (połączone) — tak jak na rysunku 4.12. W ten sposób włączysz edycję proporcjonalną w trybie Connected, co spowoduje, że transformacje zaznaczonych wierzchołków będą się rozprzestrzeniały na kolejne wierzchołki z sąsiedztwa, tyle że z coraz mniejszą siłą. W rezultacie nastąpi wygładzenie, albo inaczej stopniowe zanikanie, wprowadzonej deformacji. 2. Zanim przesuniesz jakikolwiek wierzchołek, wybierz krzywą zanikania. W tym celu kliknij przycisk Proportional Editing Falloff (zanikanie proporcjonalnej edycji) i wybierz opcję Smooth (gładkie) — tak jak na rysunku 4.13.
121
122
Rozdział 4
Teren
Rysunek 4.12. W ramach przygotowań do modelowania terenu włącz edycję proporcjonalną w trybie Connected Źródło: Blender.
Rysunek 4.13. Wybierz krzywą zanikania dla edycji proporcjonalnej Źródło: Blender.
3. Teraz możesz rozpocząć modelowanie. Po prostu zaznacz jeden lub kilka wierzchołków (niekoniecznie połączonych ze sobą) i za pomocą gizma transformacji przeciągnij je w górę lub w dół. Edycja proporcjonalna automatycznie wygładzi wprowadzoną deformację (rysunek 4.14). Uwa ga
Podczas przeciągania gizma transformacji kursor będzie otoczony białym okręgiem o promieniu pokazującym zasięg działania edycji proporcjonalnej. Jeśli masz włączony system sterowania zgodny z programem Maya, możesz ten okrąg zmniejszać i powiększać przez obracanie rolką myszy. 4. Postępuj tak, aż uformujesz wszystkie wzniesienia i zagłębienia terenu. Jeśli chcesz mocniej zróżnicować jego rzeźbę, stosuj różne wartości promienia wyznaczającego zasięg edycji proporcjonalnej. Wypróbuj też inne krzywe zaniku, np. Sharp (ostre) i Random (losowe). Na rysunku 4.15 pokazany jest teren, który wymodelowałem w taki właśnie sposób.
Modelowanie terenu w Blenderze
Rysunek 4.14. Rzeźbienie terenu z użyciem funkcji Proportional Editing. Zobacz, jak podnoszenie tylko jednej ścianki spowodowało uniesienie również sąsiednich, dając w rezultacie wzniesienie o łagodnych zboczach Źródło: Blender.
Rysunek 4.15. Teren wymodelowany metodą edycji proporcjonalnej Źródło: Blender.
123
124
Rozdział 4
Teren
Metoda tekstury przemieszczeń W metodzie tekstury przemieszczeń siatka płaszczyzny jest deformowana automatycznie zgodnie z wartościami pikseli tworzącymi specjalną teksturę (mapę wysokości). Jest to więc metoda podobna do tej, jaką oferuje Unity. Mapa wysokości to nic innego jak obraz w skali szarości, w którym obszary jasne oznaczają wzniesienia, a obszary ciemne — zagłębienia. Zasadnicza różnica między stosowaniem tej metody w Blenderze a stosowaniem jej w Unity polega na tym, że Blender pozwala na dalsze rzeźbienie siatki już po przemieszczeniu wierzchołków. W zdeformowanej siatce można nadal edytować wierzchołki, krawędzie i ścianki przy użyciu typowo modelarskich narzędzi. Silnik Unity zainstalowany bez żadnych dodatków nie oferuje tego typu narzędzi. Zobaczmy, jak w praktyce spisuje się druga metoda modelowania terenu. Wykonaj następujące czynności: 1. W panelu Texture (tekstura) wczytaj mapę wysokości bezpośrednio z pliku albo wygeneruj ją w sposób automatyczny. W tym celu kliknij przycisk New (nowy), tak jak pokazano na rysunku 4.16.
Rysunek 4.16. Generowanie nowej tekstury Źródło: Blender.
2. Rozwiń listę Type (typ) i wybierz z niej pozycję Voronoi (komórki Woronoja) lub Clouds (chmury). Tekstura typu Clouds jest generowanym losowo układem pikseli w rozmaitych odcieniach szarości, który przypomina wyglądem kłęby chmur. Użycie tych pikseli do przemieszczenia wierzchołków siatki może dać w rezultacie gładki, ale naturalnie wyglądający teren. Podgląd tekstury jest wyświetlany w okienku Preview (podgląd). To właśnie jej użyjesz jako tekstury przemieszczenia (rysunek 4.17).
Modelowanie terenu w Blenderze
Rysunek 4.17. Generowanie sztucznego obrazu chmur jako tekstury przemieszczenia Źródło: Blender.
3. Nie zapomnij o wyposażeniu siatki terenu we współrzędne mapowania UV. Aby to zrobić, zaznacz wszystkie ścianki siatki i wybierz polecenie Mesh/UV Unwrap/Smart UV Project (siatka/rozwinięcie UV/inteligentne rzutowanie UV) — tak jak na rysunku 4.18. Zaakceptuj domyślne wartości parametrów. Nie musisz stosować rzutowania inteligentnego; możesz użyć jakiejkolwiek innej metody rozwijania siatki, aby tylko rozłożyć ją na całej przestrzeni współrzędnych UV od 0 do 1. 4. Po przypisaniu siatce terenu współrzędnych UV czas zastosować modyfikator Displace (przemieszczenie). Ustanowi to połączenie między siatką terenu a mapą wysokości. Otwórz zakładkę Modifiers (modyfikatory) i przypisz siatce wspomniany modyfikator (rysunek 4.19). Uwa ga
Więcej informacji na temat modyfikatora Displace znajdziesz w dokumentacji Blendera (http://wiki.blender. org/index.php/Doc:2.6/Manual/Modifiers/Deform/Displace).
125
126
Rozdział 4
Teren
Rysunek 4.18. Rzutowanie terenu na przestrzeń tekstury Źródło: Blender.
Rysunek 4.19. Modyfikator Displace jest dostępny na zakładce Modifiers Źródło: Blender.
5. W polu Texture (tekstura) wstaw mapę wysokości (rysunek 4.20). Powinno to automatycznie zdeformować siatkę terenu i będziesz mógł od razu ocenić topologiczne działanie mapy wysokości. Za pomocą parametru Strength (siła) możesz regulować
Modelowanie terenu w Blenderze
Rysunek 4.20. Siatka terenu zdeformowana zgodnie z mapą wysokości Źródło: Blender.
intensywność jej działania. Wartości ujemne (takie jak –1 czy –2) odwracają działanie mapy, powodując, że piksele jasne przyjmują rolę pikseli ciemnych i na odwrót. Pamiętaj, że aby siatka została na trwale zdeformowana, musisz zatwierdzić modyfikator.
Metoda rzeźbienia Ostatnią z wymienionych metod modelowania terenu jest rzeźbienie. W metodzie tej, podobnie jak w Unity, operujemy narzędziami przypominającymi pędzle i za ich pomocą kształtujemy siatkę terenu zupełnie tak, jakby była wykonana z gliny. Jednak system pędzli w Blenderze działa bez żadnej mapy wysokości, a to oznacza, że tu można rzeźbić szczegóły rzeczywiście w trzech wymiarach. Teraz tylko pokrótce opiszę, jak wyrzeźbić teren przy użyciu narzędzi blenderowych, chociaż o nich samych warto by powiedzieć znacznie więcej. Tak naprawdę to należałoby im poświęcić całą książkę! Jeśli interesuje Cię ten temat i chciałbyś dokładniej poznać narzędzia rzeźbiarskie Blendera, zachęcam do lektury dokumentacji tego programu, a szczególnie artykułu zamieszczonego na stronie: http://wiki.blender.org/index.php/ Doc:2.6/Manual/Modeling/Meshes/Editing/Sculpt_Mode. Aby wyrzeźbić teren płaskiej siatki, wykonaj następujące czynności: 1. Zaznacz płaszczyznę i włącz tryb rzeźbienia (Sculpt Mode) — tak jak na rysunku 4.21. W przyborniku po lewej stronie pojawią się narzędzia rzeźbiarskie i ich ustawienia. Pamiętaj, że narzędzia te współpracują z urządzeniami reagującymi na nacisk, więc możesz nimi sterować za pomocą tabletów graficznych, takich jak Wacom Bamboo, Intuos czy Huion.
127
128
Rozdział 4
Teren
Rysunek 4.21. Włączanie trybu rzeźbienia Źródło: Blender.
2. Wybierz typ pędzla. Do wyboru jest wiele pędzli, ale przy rzeźbieniu terenów najlepiej sprawdzają się Brush (pędzel) i SculptDraw (rzeźbiące wyciąganie). Aby wybrać żądany pędzel, kliknij okienko z podglądem aktywnego pędzla i na palecie, która się otworzy, wskaż właściwą miniaturę (rysunek 4.22).
Rysunek 4.22. Wybieranie typu pędzla Źródło: Blender.
Modelowanie terenu w Blenderze
3. Za pomocą suwaka Radius (promień) ustal zasięg oddziaływania pędzla, a suwakiem Strength (siła) ustaw intensywność tego oddziaływania (rysunek 4.23).
Rysunek 4.23. Ustawianie zasięgu i siły oddziaływania pędzla, a także ustalanie, czy powinien działać w trybie dodawania, czy odejmowania Źródło: Blender.
4. Jak widać na rysunku 4.23, oba pędzle (Brush i SculptDraw) mają dwa tryby działania: Add (dodawanie) i Subtract (odejmowanie). Domyślnie włączony jest tryb Add, co oznacza, że jeśli przy aktywnym pędzlu klikniesz siatkę terenu (myszą lub piórkiem tabletu), obszar podlegający działaniu pędzla zostanie wyciągnięty w górę. Tryb Subtract, włączany przez wciśnięcie dodatkowo klawisza Ctrl (Windows) lub Command (Mac OS), odwraca działanie pędzla i powoduje wtłaczanie terenu w dół. 5. Jeśli podczas malowania przytrzymasz wciśnięty klawisz Shift, uaktywnisz tymczasowo pędzel Smooth (wygładzanie). Oczywiście możesz go uaktywnić również za pośrednictwem wspomnianej wcześniej palety. Pędzel ten rozpoznaje ułożenia ścianek na podstawie ustawienia wektorów normalnych, a następnie wprowadza uśrednianie, co w rezultacie daje płynniejsze przejścia między sąsiednimi ściankami. Takie działanie jest szczególnie przydatne przy opracowywaniu terenów o stosunkowo niewielkiej rozdzielczości. Przykład wyrzeźbionego w ten sposób terenu jest pokazany na rysunku 4.24. Uwa ga
Czasami będziesz chciał ograniczyć deformowanie terenu tylko do jednego kierunku, aby móc kontrolować wysokość, na jaką dany obszar jest podnoszony lub obniżany. Wtedy deformowanie powinno się odbywać wyłącznie wzdłuż osi Z, a ograniczenie takie uzyskasz, jeśli z listy rozwijanej Sculpt Plane (płaszczyzna rzeźbienia), zakreślonej na rysunku 4.24, wybierzesz opcję Z Plane (płaszczyzna Z).
129
130
Rozdział 4
Teren
Rysunek 4.24. Teren wyrzeźbiony i wygładzony Źródło: Blender.
Rozdzielczość terenu Niekiedy po wyrzeźbieniu terenu okazuje się, że jest on zbyt szczegółowy jak na potrzeby tworzonej gry. Liczba ścianek jest po prostu za duża, aby kolejne sceny mogły być renderowane w czasie rzeczywistym. Zdarza się, że liczebność ścianek osiąga poziom setek tysięcy, a nawet milionów, i wtedy konieczne staje się zmniejszenie rozdzielczości jeśli nie całej siatki, to przynajmniej jej części. Teoretycznie możesz usuwać ścianki i krawędzie, posługując się takimi narzędziami jak Cut (wycinanie), Delete (usuwanie) i Merge (łączenie), ale jest to wysoce niepraktyczne, bo wszystko trzeba robić ręcznie. Na szczęście istnieje możliwość zautomatyzowania tego procesu (przynajmniej w pewnym stopniu) przez zastosowanie modyfikatora Decimate (dziesiątkowanie), który proceduralnie zmniejsza liczbę ścianek w siatce. Jak wszystkie modyfikatory jest dostępny na zakładce Modifiers pokazanej na rysunku 4.25. Ostrzeżeni e
Zmniejszenie liczby ścianek w siatce prowadzi do zmiany jej topologii. Im bardziej zmniejszasz tę liczbę, tym bardziej zmniejsza się szczegółowość siatki i tym mocniej różni się ona od swojej pierwotnej postaci. Do sterowania modyfikatorem Decimate służy głównie parametr Ratio (stosunek), którego wartość domyślna wynosi 1 i jeśli nie wprowadzisz innej, siatka pozostanie bez zmian i zachowa wszystkie ścianki. Przy wartości 0,5 siatka utraci mniej więcej połowę ze swej szczegółowości i liczba jej ścianek zmniejszy się o ok. 50%. Na rysunku 4.26 pokazany jest rezultat zdziesiątkowania siatki z rysunku 4.25 do ok. 20% jej pierwotnej rozdzielczości.
Malowanie terenu teksturą
Rysunek 4.25. Modyfikator Decimate zmniejsza liczbę ścianek w siatce Źródło: Blender.
Rysunek 4.26. Obniżenie rozdzielczości terenu za pomocą modyfikatora Decimate może dać rezultat w postaci siatki zoptymalizowanej, ale może ją też zniszczyć. Zachowaj ostrożność! Źródło: Blender.
Malowanie terenu teksturą Po wymodelowaniu powierzchni terenu przy użyciu opisanych wyżej (lub innych) technik trzeba go pomalować teksturą, czyli nałożyć na siatkę obraz, który sprawi, że całość będzie wyglądała jak prawdziwy teren ze skałami, z trawą, śniegiem itp. Można to zrobić na kilka sposobów, np. utworzyć odpowiednią teksturę w oddzielnym programie graficznym, takim jak GIMP czy Photoshop, i potem nałożyć ją na siatkę terenu zgodnie ze współrzędnymi UV — jest to klasyczna metoda teksturowania obiektów
131
132
Rozdział 4
Teren
siatkowych. Lecz Blender oferuje narzędzia umożliwiające wykonanie tego zadania w sposób bardziej intuicyjny i bezpośredni, bo polegający na nanoszeniu tekstury metodą malowania. W kolejnych punktach pokażę, jak to się robi.
Wyznaczanie współrzędnych UV terenu Przed rozpoczęciem teksturowania, niezależnie od wybranej metody, trzeba przypisać siatce właściwe współrzędne UV. Zagadnienie to opisałem dokładnie w rozdziale 3., w części zatytułowanej „Mapowanie UV i tworzenie tekstur”. Mówiąc krótko: współrzędne UV wyznaczają dwustronną relację, albo inaczej odwzorowanie, między dwuwymiarową teksturą a trójwymiarową siatką. W blenderowym edytorze współrzędnych UV i obrazów (UV/Image Editor) można z łatwością wygenerować specjalną teksturę obrazującą rozkład współrzędnych UV w postaci biało-szarej szachownicy. Dzięki tej teksturze można szybko ocenić jakość mapowania UV. Gdy mapowanie UV jest prawidłowe, kratki szachownicy widoczne na powierzchni siatki są kwadratami (mają wysokość równą szerokości) i wszędzie mają takie same rozmiary. Taki wygląd szachownicy świadczy o tym, że gęstość tekseli jest jednakowa na całej powierzchni siatki i nie występują zniekształcenia. Oczywiście taki obrazek rzadko otrzymuje się od razu po wygenerowaniu współrzędnych UV i trzeba poświęcić trochę czasu, żeby za pomocą narzędzi dostępnych w edytorze uzyskać stan przynajmniej zbliżony do idealnego. W przypadku skomplikowanych modeli pracochłonność tego procesu może być bardzo duża. Lecz bez dobrej jakości mapowania UV nawet najlepszej tekstury nie da się prawidłowo nałożyć, a zatem nie warto żałować czasu na ten przygotowawczy etap. Na rysunku 4.27 pokazany jest wyrzeźbiony teren z nałożoną szachownicą UV, której wygląd wskazuje, że można przystąpić do teksturowania. Uwa ga
Gęstość tekselowa ma znaczenie w grach czasu rzeczywistego. Każda tekstura ma określoną liczbę pikseli wyrażoną przez szerokość i wysokość obrazu, lecz na ekranie rzadko jest wyświetlana w swojej naturalnej wielkości i perspektywie. Zazwyczaj jest ściskana lub rozciągana (przez ponowne próbkowanie) na powierzchniach trójwymiarowych siatek o rozmaitych rozmiarach, jak papier, w który owijane są podarunki. Gdy duża tekstura jest upychana na małej powierzchni, gęstość tekselowa staje się większa. Gdy mała tekstura rozkłada się na dużej powierzchni, jej gęstość tekselowa maleje.
Generowanie tekstury Mamy wyrzeźbiony teren z odpowiednim mapowaniem UV, a zatem możemy rozpocząć malowanie tekstury. Na początek otwórz w Blenderze okno UV/Image Editor (edytor współrzędnych UV i obrazów), aby utworzyć nową, pustą teksturę, która będzie zawierała wszystkie piksele zdatne do malowania. Na dolnym pasku edytora kliknij przycisk New (nowy) — tak jak na rysunku 4.28.
Malowanie terenu teksturą
Rysunek 4.27. Przed malowaniem terenu teksturą należy sprawdzić prawidłowość mapowania UV Źródło: Blender.
Rysunek 4.28. Tworzenie nowej tekstury w oknie UV/Image Editor Źródło: Blender.
Ja nadałem tej teksturze nazwę TerrainTexture (tekstura terenu) i pozostawiłem jej domyślne wymiary 1024×1024 pikseli. W zależności od potrzeb możesz odpowiednio zwiększyć te wymiary lub je zmniejszyć. Zazwyczaj zmieniam też kolor tła z czarnego na średnio szary (0,5, 0,5, 0,5). Na koniec wyłączam opcję Alpha (alfa), bo tekstury terenów na ogół nie mają obszarów przezroczystych. Zauważ, że wszystko to są preferencje,
133
134
Rozdział 4
Teren
a nie wymagania. Zawsze trzeba wiedzieć, czego się potrzebuje, i w zależności od tego dobierać stosowne ustawienia. Warto też pamiętać, co na temat tworzenia tekstur napisałem w poprzednim rozdziale. Po ustawieniu parametrów nowej tekstury kliknij OK (rysunek 4.29).
Rysunek 4.29. Ustawianie parametrów tworzonej tekstury Źródło: Blender.
Malowanie w oknie UV/Image Editor Istnieją dwie zasadnicze metody malowania tekstur: ■ w oknie UV/Image Editor; ■ w standardowym oknie widokowym 3D View. Nie są to metody wykluczające się; można używać obu. Na razie przyjrzymy się pierwszej z nich. 1. Na pasku narzędziowym okna UV/Image Editor włącz tryb Paint (malowanie) — tak jak na rysunku 4.30. Spowoduje to zmianę narzędzi i opcji dostępnych w przyborniku po lewej stronie edytora (zob. rysunek 4.31). Są tam pędzle i inne narzędzia związane z malowaniem tekstur. Działają one podobnie jak narzędzia służące do rzeźbienia terenu, z tym że zamiast deformować siatkę, malują piksele tekstury. 2. Wybierz pędzel i ustaw jego kolor, rozmiar oraz intensywność. Zacznij malować! Podobnie jak w przypadku narzędzi rzeźbiarskich teraz też możesz posłużyć się tabletem graficznym.
Podczas malowania w oknie UV/Image Editor lubię mieć też otwarte okno 3D View, bo pozwala mi to na bieżąco obserwować, jak pod wpływem mojego malowania zmienia się wygląd siatki. Oczywiście w oknie 3D View musi być wtedy włączone cieniowanie typu Texture (teksturowe), a nie Solid (bryłowe) czy Wireframe (szkieletowe), bo tylko wtedy tekstura będzie renderowana na siatce (rysunek 4.31).
Malowanie terenu teksturą
Rysunek 4.30. Włączanie trybu malowania Źródło: Blender.
Rysunek 4.31. Malowanie tekstury na siatce za pośrednictwem okna UV/Image Editor Źródło: Blender.
Trzeba też dodać, że wszystkie dostępne tu pędzle są wyposażone w rozmaite tryby mieszania. Co więcej, tryby te można zmieniać przed każdym pociągnięciem pędzla, a do wyboru są:
135
136
Rozdział 4
Teren
■ Add (dodawanie); ■ Subtract (odejmowanie); ■ Multiply (mnożenie); ■ Lighten (rozjaśnianie); ■ Darken (przyciemnianie); ■ Mix (mieszanie). Wyboru odpowiedniego trybu dokonujemy w przyborniku, zanim przeciągniemy pędzlem po teksturze (rysunek 4.32).
Rysunek 4.32. Pędzli do malowania tekstur można używać z różnymi trybami mieszania Źródło: Blender.
Jak na razie (w chwili, gdy piszę ten tekst) Blender nie oferuje własnego systemu warstw obrazowych, który można by wykorzystać przy malowaniu tekstur. Wszystkie pociągnięcia pędzli są więc „wtapiane” w jedną i tę samą warstwę, a to oznacza, że żadne z tych pociągnięć nie może być odseparowane od innych i niezależnie od nich modyfikowane. Jest jednak dodatek o nazwie Texture Paint Layer Manager, który
Malowanie terenu teksturą
umożliwia korzystanie z warstw również w Blenderze. Można go łatwo zainstalować za pośrednictwem okna Blender User Preferences (preferencje użytkownika Blendera). Oto szczegóły postępowania: 1. Wybierz polecenie File/User Preferences (plik/preferencje użytkownika). 2. W oknie dialogowym Blender User Preferences, które się otworzy, kliknij zakładkę Addons (dodatki). 3. Na liście Categories (kategorie) zaznacz pozycję Paint (malowanie). 4. Kliknij pole wyboru przy pozycji Paint: Texture Paint Layer Manager (malowanie: menedżer warstw malowania tekstury) — tak jak na rysunku 4.33.
Rysunek 4.33. Instalowanie dodatku Texture Paint Layer Manager pozwalającego na korzystanie z warstw przy malowaniu tekstur Źródło: Blender.
Nie będę opisywał szczegółów posługiwania się tym dodatkiem, bo nie jest on integralną częścią Blendera. Jeśli jesteś zainteresowany jego używaniem, instrukcję obsługi znajdziesz pod adresem: http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/3D_interaction/ Texture_paint_layers.
137
138
Rozdział 4
Teren
Malowanie w oknie 3D View Alternatywna metoda malowania tekstury polega na malowaniu jej wprost na siatce w oknie widokowym, tak jakby malarz malował na prawdziwej rzeźbie lub żywym modelu. Stosując tę metodę, nakładasz piksele za pomocą myszy lub tabletu wprost na siatkę. Pociągnięcia pędzlem są rzutowane z okna widokowego na siatkę modelu i wypalane na powiązanej z nią teksturze. Duże znaczenie dla finalnego efektu ma kąt, pod jakim widzisz siatkę w oknie widokowym; zawsze dbaj o to, aby miejsce malowane było dobrze widoczne. 1. Żeby włączyć malowanie tekstury w oknie 3D View, na dolnym pasku narzędziowym z listy Mode (tryb) wybierz opcję Texture Paint (malowanie tekstury) — rysunek 4.34.
Rysunek 4.34. Włączanie trybu Texture Paint w oknie 3D View Źródło: Blender.
2. Po włączeniu trybu Texture Paint w przyborniku po lewej stronie pojawią się znane już narzędzia malarskie. Ich działanie jest dokładnie takie samo jak w oknie UV/Image Editor. Wybierz pędzel, ustaw jego kolor, rozmiar oraz intensywność i zacznij malować (rysunek 4.35)!
W omawianej metodzie istotne znaczenie ma kwestia rzutowania. Jak należy pomalować ściankę, która mieści się w zasięgu pędzla, ale jest odwrócona tyłem? Jak należy pomalować ściankę, która jest w zasięgu pędzla i zwrócona przodem, ale zasłania ją inna ścianka leżąca bliżej kamery? Na takie właśnie pytania musisz odpowiedzieć Blenderowi. Jego domyślnym zachowaniem w obu przypadkach jest niemalowanie takich
Malowanie terenu teksturą
Rysunek 4.35. Malowanie bezpośrednio w widoku 3D Źródło: Blender.
ścianek, ale możesz to zmienić przez zmodyfikowanie ustawień Project Paint (rzutowanie malowania) w przyborniku (rysunek 4.36). Wyłączenie opcji Occlude (okluzja) zezwala na malowanie ścianek zasłoniętych (przez inne ścianki leżące bliżej kamery). Wyłączenie opcji Cull (wykluczenie) dopuszcza malowanie ścianek odwróconych tyłem do kamery. A wyłączenie opcji Normal (normalna) powoduje, że wszystkie ścianki — niezależnie od ustawienia względem kamery i tego, czy są zasłonięte, czy nie — są malowane z jednakową intensywnością pędzla.
Rysunek 4.36. Za pomocą opcji z sekcji Project Paint możesz ustalić traktowanie ścianek w zależności od ich ustawienia względem pędzla i kamery Źródło: Blender.
139
140
Rozdział 4
Teren
Malowanie teksturami Jak dotąd malowaliśmy, czy to w oknie UV/Image Editor, czy w widoku 3D, wyłącznie określonymi kolorami wybieranymi z próbnika. W niektórych przypadkach może to wystarczyć (np. tereny do filmów rysunkowych), lecz zazwyczaj chodzi o nakładanie pędzlem przygotowanej wcześniej tekstury odzwierciedlającej z fotograficzną dokładnością elementy prawdziwego krajobrazu, takie jak trawa, skały itp. W Blenderze umożliwiają to narzędzia i opcje z sekcji Texture (tekstura) przybornika. Sekcja ta jest dostępna zarówno w oknie edytora UV/Image Editor, jak i w oknie widoku 3D. Możesz w niej wybrać w charakterze pędzla dowolną z załadowanych wcześniej tekstur. 1. Aby załadować do Blendera nową teksturę, otwórz panel Properties (właściwości) i kliknij zakładkę Texture. 2. Kliknij przycisk New (nowy), aby wygenerować nową, właściwą teksturę — rysunek 4.37.
Rysunek 4.37. Generowanie nowej tekstury Źródło: Blender.
Malowanie terenu teksturą
3. Po utworzeniu nowej tekstury musisz ją jeszcze związać z przygotowanym wcześniej i zapisanym na dysku obrazem. Domyślnie nowa tekstura jest typu Clouds (chmury) lub jakiegoś innego spośród proceduralnych. Żeby to zmienić, rozwiń listę Type (typ) i wybierz pozycję Image or Movie (obraz lub film). Wtedy będziesz mógł wskazać, który obraz lub film ma być użyty w roli tekstury — rysunek 4.38.
Rysunek 4.38. Zmiana typu tekstury na obrazową lub filmową Źródło: Blender.
4. Przewiń panel w dół i w sekcji Image (obraz) kliknij przycisk Open (otwórz) — rysunek 4.39. Następnie wybierz plik z obrazem, który ma być użyty jako tekstura. 5. Teraz możesz przystąpić do malowania załadowaną teksturą. W trybie Texture Paint wróć do przybornika i w sekcji Texture wybierz załadowaną wcześniej teksturę (rysunek 4.40). Wskazana tekstura zostanie przypisana do aktywnego pędzla i odtąd każde jego pociągnięcie będzie pozostawiało ślad w postaci pikseli z tej tekstury.
141
142
Rozdział 4
Teren
Rysunek 4.39. Wybór pliku z obrazem jako teksturą Źródło: Blender.
Rysunek 4.40. Malowanie wybraną teksturą Źródło: Blender.
Uwa ga
Blender miesza (lub łączy) teksturę z aktualnie wybranym kolorem pędzla. Przykładowo: jeśli wybrałeś dla pędzla kolor czerwony, to nakładana tekstura zostanie zabarwiona na czerwono. To samo dotyczy wszystkich innych kolorów. Jeśli chcesz, aby tekstura zachowała swoją pierwotną kolorystykę, wybierz dla pędzla kolor biały (1.0, 1.0, 1.0).
Tworzenie dróg i ścieżek
6. Jeśli po skończeniu malowania wybraną teksturą będziesz chciał ją odłączyć od pędzla, kliknij przycisk Unlink datablock (odłącz blok danych) znajdujący się pod miniaturą pędzla w przyborniku (rysunek 4.41).
Rysunek 4.41. Odłączanie tekstury od pędzla Źródło: Blender.
Tworzenie dróg i ścieżek Jedno z najczęściej zadawanych pytań, jakie słyszę w związku z modelowaniem terenów, dotyczy tworzenia dróg, ścieżek i innych elementów, które powinny być dopasowane do topologii terenu. Droga np. nie spoczywa na podłożu w taki sposób jak samochód, człowiek, budynek czy inny tego typu przedmiot o dokładnie określonym położeniu środka ciężkości. Droga rozciąga się na dużych obszarach, wijąc się i kręcąc, ale zawsze dopasowuje się ściśle do konturów terenu. Nie wystarczy, że ją położysz na siatce terenu; ona musi się do tej siatki dopasować. Z tego wynika, że budowanie dróg i ścieżek musi być nierozerwalnie związane z konkretnym terenem. Jedno z rozwiązań polega na modelowaniu tych elementów wraz z terenem, tak jakby były jego częścią. Nie jest to jednak takie proste. Jeśli droga ma być długa i kręta, wymodelowanie jej z wielokątów mogłoby Cię przyprawić o ból głowy. Lepiej jest oddzielić modelowanie drogi od modelowania terenu i połączyć te części dopiero w fazie końcowej. W tym podrozdziale omówię narzędzia, jakie mogą się przydać przy realizacji takiego zadania w Blenderze.
143
144
Rozdział 4
Teren
Modelowanie dróg W poprzednich podrozdziałach przedstawiłem trzy metody tworzenia siatek terenów: edycji proporcjonalnej, tekstury przemieszczeń i rzeźbienia. Wszystkie razem i każda z osobna pozwalają zbudować złożony teren dla niemal każdego rodzaju gry. Teraz skupimy się na takim modelowaniu dróg i ścieżek, aby zawsze pasowały do zbudowanego już terenu. Oto kolejność czynności, jakie trzeba w tym celu wykonać: 1. Włącz widok z góry (Top), aby spojrzeć na teren z perspektywy ptasiej (topograficznej). Spójrz na rysunek 4.42. (Zakładam, że masz już wymodelowany teren!)
Rysunek 4.42. Do planowania i modelowania dróg najlepszy jest widok z góry Źródło: Blender.
2. Sposobów na utworzenie drogi jest wiele. Jeden polega na zastosowaniu krzywych. Wystarczy narysować tylko krawędzie drogi biegnącej przez wymodelowany teren. Na razie nie musisz się przejmować jej przebiegiem pionowym, czyli wznoszeniem się i opadaniem na wszelkich nierównościach. Po prostu skoncentruj się na jej przebiegu poziomym, który najlepiej widać z góry. Aby dodać do sceny linię krzywą, wybierz polecenie Add/Curve/Bezier (dodaj/krzywą/Beziera) — tak jak na rysunku 4.43. 3. Widziana z góry pierwsza linia reprezentuje jedną krawędź drogi, lewą lub prawą (na ogół nie ma to większego znaczenia). Wkrótce utworzysz drugą krawędź przez powielenie tamtej. Jednak wcześniej uaktywnij narzędzie Extrude (wytłocz) — np. przez wciśnięcie klawiszy Alt+X w trybie edycji — i dodaj kolejne segmenty krzywej, układając je jeden za drugim i formując zgodnie z planowanym przebiegiem drogi. Pamiętaj: przebieg krzywej w kierunku pionowym (wzdłuż osi Z) jest na razie nieistotny. Dopasowaniem do ukształtowania terenu zajmiemy się później.
Tworzenie dróg i ścieżek
Rysunek 4.43. Tworzenie krzywej Beziera Źródło: Blender.
4. Po dodaniu ostatniego segmentu połącz końce krzywej, aby utworzyć krzywą zamkniętą. Coś takiego jest konieczne, jeśli końce nie mogą być przesuwane niezależnie jeden od drugiego. A zatem zaznacz krzywą w trybie obiektowym, włącz tryb edycji i z menu okna widokowego wybierz Curve/Toggle Cyclic (krzywa/przełącz cykliczność). Przydaje się również ustawienie (w trybie obiektowym) środka transformacji krzywej w centralnym punkcie jej geometrii, co można szybko zrobić za pomocą polecenia Object/Transform/Origin to Geometry (obiekt/przekształć/środek do geometrii) — rysunek 4.44. 5. Skoro jedna krawędź drogi jest już gotowa, możesz wykonać drugą. W tym celu utwórz kopię pierwszej krzywej i przeskaluj ją w górę lub w dół. Przykład kompletnej drogi jest pokazany na rysunku 4.45. Jeśli masz już obie krawędzie, złącz je w jeden obiekt — zaznacz je, a następnie wybierz z menu okna widokowego polecenie Object/Join (obiekt/złącz). 6. Jako etap ostatni wykonaj osadzenie drogi w siatce terenu, aby uzyskać jej dopasowanie do powierzchni siatki zarówno w poziomie, jak i w pionie. Możesz to zrobić za pomocą narzędzia Knife Project (rzutowanie tnące), które zrzutuje obrys drogi na powierzchnię terenu zgodnie z kierunkiem bieżącego widoku. Wzdłuż konturów rzutu powstaną dodatkowe krawędzie siatki. Krótko mówiąc: narzędzie to automatycznie przeniesie Twoją drogę na powierzchnię terenu. Aby wszystko poszło jak należy, musisz włączyć widok z góry — kamera powinna być ustawiona centralnie nad terenem i skierowana pionowo w dół. Następnie ustaw drogę w takim położeniu, jakie powinna zajmować po zrzutowaniu pionowo w dół (zob. rysunek 4.45). Gdy już
145
146
Rozdział 4
Teren
Rysunek 4.44. Ustawianie środka transformacji w geometrycznym środku krzywej Źródło: Blender.
Rysunek 4.45. Kompletna droga utworzona z dwóch złączonych ze sobą krzywych. Żeby poprawić ich widoczność na zrzucie ekranu, dodałem efekt fazowania (w panelu Properties) — dla procesu tworzenia drogi nie ma to znaczenia Źródło: Blender.
Tworzenie dróg i ścieżek
to wszystko przygotujesz, sięgnij po narzędzie Knife Project. Aby uzyskać do niego dostęp, najpierw usuń istniejące zaznaczenie, a potem zaznacz kolejno obiekt rzutowany (drogę) i obiekt rozcinany (teren) — w takiej, a nie innej kolejności. Na koniec włącz tryb edycji (wciśnij klawisz Tab) i w przyborniku po lewej stronie okna widokowego kliknij przycisk Knife Project (rysunek 4.46).
Rysunek 4.46. Narzędzie Knife Project zrzutowało obrys drogi na siatkę terenu Źródło: Blender.
Uaktywnienie narzędzia Knife Project spowoduje nacięcie siatki terenu wzdłuż konturów drogi, a zatem nacięcia te wyznaczą przebieg drogi na powierzchni terenu! Jest to naprawdę łatwy sposób na dodawanie dróg do siatki terenu (rysunek 4.47). Oczywiście trochę pracy zostało jeszcze do wykonania. Trzeba by np. tu i ówdzie wyrównać nawierzchnię drogi. Ale dzięki zastosowaniu narzędzia Knife Project zasadnicze zadanie zostało wykonane szybko i skutecznie. Gratuluję! Teraz już możesz sam tworzyć rozmaite tereny razem z drogami i ścieżkami.
147
148
Rozdział 4
Teren
Rysunek 4.47. Ręcznie rzeźbiony teren z systemem drogowym wykonanym przy użyciu narzędzia Knife Project Źródło: Blender.
Podsumowanie Tematem tego rozdziału był teren jako element środowiska gry. Najpierw przedstawiłem system generowania terenów funkcjonujący w Unity. Jest to system, w którym rzeźba terenu stanowi odwzorowanie specjalnej tekstury zwanej mapą wysokości. Za pośrednictwem tej tekstury można wpływać jedynie na pionowe położenia poszczególnych punktów terenu i właśnie to ograniczenie skłoniło nas do sprawdzenia, co w kwestii tworzenia terenów ma do zaoferowania Blender. Wygenerowany za jego pomocą teren można by przenieść jako siatkę statyczną do Unity i tam osadzić ją w miejscu terenu natywnego. Blender oferuje trzy metody modelowania terenu, malowanie teksturami, a także wygodne narzędzia do nanoszenia dróg i ścieżek. W następnym rozdziale przeanalizujemy animacyjny tok pracy w Blenderze ze szczególnym uwzględnieniem zagadnień związanych z eksportowaniem animacji do Unity.
R OZ DZ I AŁ 5
T OK PRACY ANIMACYJNEJ
Animację ograniczają tylko dwie rzeczy. Możliwości wyobraźni i zdolność do odtworzenia tego, co sobie wyobrażamy. — Eric Larson Po przeczytaniu tego rozdziału powinieneś: ■ umieć wykonać animację w Blenderze i przenieść ją do Unity; ■ umieć pracować z klatkami kluczowymi; ■ wiedzieć, na czym polega miksowanie kształtów i co to są klucze kształtu; ■ umieć wykonywać rigowanie w Blenderze z myślą o przeniesieniu szkieletu do Unity i wykorzystaniu w systemie Mecanim; ■ umieć przenosić animowane modele z Blendera do Unity. Animacja w grach komputerowych polega zasadniczo na opisywaniu zmian zachodzących w miarę upływu czasu, przy czym mogą to być zmiany położenia obiektu lub jego budowy wewnętrznej. W sensie ogólnym animacja oznacza swoistą relację między czasem a wartością, albo inaczej: między czasem, w którym dochodzi do zmiany, a zmianą, która się pojawia, gdy nadchodzi właściwy dla niej czas. Dlatego ilekroć będziesz chciał przesunąć w grze jakiś obiekt lub zmienić jego wygląd, będziesz musiał zastosować animację przez określenie relacji między czasem a modyfikowaną wartością. Realizacja takiego odwzorowania jest zazwyczaj oparta na koncepcji klatek kluczowych, a więc zanim przejdziemy do omawiania zagadnień związanych z tworzeniem animacji w Blenderze i Unity, przyjrzymy się dokładniej, na czym ta koncepcja polega.
149
150
Rozdział 5
Tok pracy animacyjnej
Klatka kluczowa jako jednostka animacji Jak już wspomniałem, animacja oznacza zmiany zachodzące w czasie. Definicja ta opiera się na pojęciach czasu i zmiany. Zarówno Blender, jak i Unity ujmują te pojęcia w jedną strukturę, zwaną klatką kluczową. Kompletna sekwencja animacyjna może zawierać jedną lub więcej klatek kluczowych. Klatka kluczowa (keyframe) określa właściwości obiektu (np. postaci) w konkretnym punkcie na osi czasu. Właściwościami tymi mogą być: położenie, kąt obrotu, skala i inne tego typu parametry (zwane tutaj kanałami). Żeby wykonać sensowną animację, potrzeba co najmniej dwóch klatek kluczowych, które określą stan obiektu na końcach pewnego przedziału czasowego. W Blenderze, tak jak i w Unity, płynność odtwarzania animacji uzyskuje się przez generowanie klatek pośrednich (tweens). Są to klatki animacji generowane automatycznie i wstawiane między klatki kluczowe w celu uzyskania płynnych zmian wartości kluczowanych parametrów. Proces obliczania wartości pośrednich nosi nazwę interpolacji i jest sterowany przez matematyczną strukturę o nazwie krzywa funkcyjna (F-curve). Oś pozioma oznacza czas, a pionowa — zmieniający się parametr (rysunek 5.1).
Rysunek 5.1. Po lewej stronie u dołu widoczny jest edytor krzywych funkcyjnych będący częścią edytora wykresów Źródło: Blender.
Szybkość, z jaką animacje są odtwarzane, zazwyczaj określa się przez podanie liczby klatek wyświetlanych w ciągu jednej sekundy (fps). Lecz jak się wkrótce przekonasz, Unity oferuje możliwość skalowania czasu trwania animacji w celu jej przyspieszenia lub spowolnienia w zależności od potrzeb. Można to robić nawet w czasie rzeczywistym. Jednak warto ustalić właściwą szybkość animacji już w Blenderze, i to we wczesnej
Przygotowanie Blendera do tworzenia animacji
fazie jej tworzenia, aby w trakcie opracowywania mieć możliwość sprawdzania, jak będzie wyglądała w grze. W tym celu należy otworzyć zakładkę Render (renderowanie) panelu Properties (właściwości) i w polu Frame Rate (szybkość odtwarzania) wpisać odpowiednią liczbę — tak jak na rysunku 5.2. Najczęściej stosowane są wartości 25 i 30 fps.
Rysunek 5.2. Określanie szybkości odtwarzania animacji w Blenderze Źródło: Blender.
Przygotowanie Blendera do tworzenia animacji Ogólnie tok pracy nad animacją w Blenderze i Unity wygląda następująco: 1. Wykonujesz animację w Blenderze. 2. Eksportujesz ją z Blendera. 3. Importujesz do Unity. 4. Testujesz i udoskonalasz, a w razie potrzeby wracasz do Blendera i tam poprawiasz, a potem ponownie importujesz.
Najpierw trzeba więc zaplanować i wykonać animację w Blenderze. Jednak zanim się za to zabierzesz, powinieneś trochę pomajstrować przy ustawieniach programu, aby go do takiej pracy przygotować. W kolejnych punktach podpowiem Ci, jak do tego podejść od strony praktycznej.
151
152
Rozdział 5
Tok pracy animacyjnej
Wykorzystaj specjalny, animacyjny układ interfejsu Blendera Animacja nie jest jakąś tam dodatkową procedurą wykonywaną po fazie modelowania czy mapowania UV. Jest to całkowicie odrębny proces, który wymaga starannego przygotowania, sporego nakładu pracy i zazwyczaj niemałych ilości czasu. Dlatego warto tak skonfigurować interfejs Blendera, aby maksymalnie pomagał w realizacji tego zadania. W Blenderze możesz tworzyć własne układy interfejsu, ale do prac animacyjnych lepiej wykorzystać gotowy układ o nazwie Animation (animacja). Aby go uaktywnić, po prostu wybierz jego nazwę z listy rozwijanej Screen layout (układ ekranu) widocznej w górnej części interfejsu (rysunek 5.3).
Rysunek 5.3. Włączanie animacyjnego układu interfejsu Źródło: Blender.
Uważaj na automatyczne kluczowanie Funkcja Auto-Key (automatyczne kluczowanie) jest domyślnie wyłączona, ale jeśli ją włączysz — przez kliknięcie przycisku Auto-Key na pasku narzędziowym okna Timeline (oś czasu), tak jak na rysunku 5.4 — Blender będzie rejestrował wszystkie transformacje i modyfikacje siatek, jakie wykonasz. Gdy tylko wykryje jakąś zmianę, automatycznie utworzy stosowne klatki kluczowe i umieści je na osi czasu. W miarę jak będziesz edytował poszczególne obiekty, Blender będzie na tej podstawie konstruował animację. Wykona za Ciebie całą robotę związaną z tworzeniem klatek kluczowych i będzie się przy tym starał odgadnąć, które właściwości chciałbyś animować i jak powinny być interpolowane klatki kluczowe. Automatyczne kluczowanie ma przyczyniać się do oszczędzenia Twojego czasu, jednakże generuje większą liczbę klatek kluczowych niż jest to konieczne, co zazwyczaj prowadzi do niepotrzebnego skomplikowania danych animacyjnych. Cierpi na tym wydajność animacji i za bardzo rozrasta się plik, w którym jest zapisywana. Ma to
Przygotowanie Blendera do tworzenia animacji
Rysunek 5.4. Funkcję Auto-Key wspomagającą tworzenie animacji można włączać i wyłączać Źródło: Blender.
również negatywny wpływ na szybkość działania całej gry, gdy taka animacja zostanie zaimportowana do Unity, ponieważ silnik musi wtedy przetworzyć więcej klatek. Dlatego proponuję, abyś w większości przypadków pozostawiał tę funkcję wyłączoną. Jeśli sam wykonasz klatki kluczowe, animacja będzie zawierała tylko te dane, które są rzeczywiście niezbędne. Oczywiście będą sytuacje, chociażby przy pracy z wieloma obiektami lub rigowanymi postaciami, kiedy automatyczne kluczowanie okaże się bardzo przydatne. Wtedy warto włączyć opcję Only Insert Available (wstawiaj tylko dostępne) znajdującą się na zakładce Editing (edytowanie) okna dialogowego Blender User Preferences (rysunek 5.5). Opcja ta spowoduje, że Blender będzie tworzył klatki kluczowe tylko w tych kanałach, w których Ty sam utworzysz pierwsze takie klatki. W ten sposób uchronisz się przed zalewem klatek kluczowych w kanałach, które w danej animacji nie są do niczego potrzebne.
Wstawiaj pojedyncze klatki kluczowe Często ręczne wstawianie klatek kluczowych okazuje się najefektywniejsze. Masz wtedy pełną kontrolę nad tym, które klatki zostaną utworzone w danej sekwencji animacyjnej, a przez to możesz łatwiej ograniczyć ich liczbę do niezbędnego minimum. Aby ręcznie wstawić klatkę kluczową dla parametru, który może być animowany, kliknij prawym przyciskiem myszy pole z jego wartością i z podręcznego menu wybierz polecenie Insert Single Keyframe (wstaw jedną klatkę kluczową) — rysunek 5.6.
153
154
Rozdział 5
Tok pracy animacyjnej
Rysunek 5.5. Nakładanie ograniczeń na funkcję Auto-Key Źródło: Blender.
Rysunek 5.6. Ręczne wstawianie klatek kluczowych do animacji Źródło: Blender.
Przygotowanie Blendera do tworzenia animacji
Długość animacji Każda animacja ma określoną długość wyrażoną liczbą klatek. Blender domyślnie ustawia tę liczbę na 250, ale w każdej chwili możesz to łatwo zmienić. Jednak w przypadku animacji, która ma być eksportowana do Unity, nie jest obojętne, jak to zrobisz. Jeśli wybierzesz niewłaściwą metodę, może się okazać, że po zaimportowaniu do Unity animacja ma inną liczbę klatek niż ta, którą ustawiłeś. To z kolei może spowodować przedwczesne lub zbyt późne zakończenie animacji. Jeden ze sposobów ustalania długości animacji polega na określeniu w oknie Timeline numeru ostatniej klatki — tak jak na rysunku 5.7. Jednakże po uaktywnieniu opcji Use an alternative start/end frame (użyj alternatywnych klatek jako początkowej i końcowej) animacja widoczna w oknie Timeline może mieć inną długość niż animacja sceniczna, którą wyeksportujesz do Unity. Okazuje się bowiem, że w oknie Timeline nie musi być wcale widoczna cała animacja, a jedynie jej fragment. Często jest widoczna w całości, ale nie zawsze. Ustawiony w tym oknie zakres klatek nie musi się pokrywać z całkowitą długością animacji. Dlatego żeby tę długość ustawić prawidłowo, należy się posłużyć metodą, którą właśnie teraz przedstawię.
Rysunek 5.7. Długość animacji ustawiona w oknie Timeline może się różnić od tej, jaką będzie miała animacja wyeksportowana do Unity Źródło: Blender.
Druga (zalecana) metoda ustalania długości animacji nie ma już takich wad, jakie miała poprzednia. Tutaj już nie grozi rozbieżność między ustawioną a rzeczywistą liczbą klatek. Czynności, jakie trzeba wykonać, są następujące: 1. W panelu Properties kliknij zakładkę Render. 2. W rolecie Dimensions (wymiary) kliknij pole End Frame (klatka końcowa) i wpisz numer klatki kończącej animację (rysunek 5.8).
155
156
Rozdział 5
Tok pracy animacyjnej
Rysunek 5.8. Ustalanie długości animacji przez określenie liczby wszystkich klatek Źródło: Blender.
Eksportowanie animacji do formatu FBX W rozdziale 2. opisałem szczegółowo, jak należy eksportować siatki z Blendera do formatu FBX lub Collada, aby potem można je było łatwo zaimportować do Unity. Jak zapewne pamiętasz, żeby wyeksportować siatkę do FBX, należy użyć narzędzia eksportującego dostępnego po wybraniu polecenia File/Export/Autodesk FBX (plik/ eksportuj/Autodesk FBX). W tamtym rozdziale rozważaliśmy jedynie eksport siatek statycznych. Na szczęście w przypadku siatek animowanych obowiązują podobne zasady postępowania. Trzeba jedynie pilnować, aby wszystkie opcje związane z animacjami były włączone. Szczególnie ważne są: Include Animation (dołącz animację), All Actions (wszystkie akcje), Include Default Take (dołącz ujęcie domyślne) i Optimize Keyframes (optymalizuj klatki kluczowe) — rysunek 5.9. Uwa ga
Żeby przenieść siatkę i animację z Blendera do Unity, nie musisz używać formatu FBX. Alternatywą dla niego jest format Collada (DAE), a właściwe dla niego narzędzie eksportujące jest dostępne po wybraniu polecenia File/Export/Collada (plik/eksportuj/Collada).
Praca z wieloma animacjami Często będziesz chciał przypisać jednemu obiektowi kilka animacji — np. gdy będziesz animował postać wykonującą rozmaite ruchy, powiedzmy: chód, bieg i skoki. Są dwie metody wykonania tego zadania w Blenderze, które dobrze współgrają z Unity. Przeanalizujmy je na przykładzie eksportu modelu postaci z kilkoma różnymi animacjami.
Przygotowanie Blendera do tworzenia animacji
Rysunek 5.9. Ustawienia niezbędne do prawidłowego wyeksportowania siatki w formacie FBX Źródło: Blender.
Eksportowanie oddzielnie animacji i postaci Pierwsza metoda polega na wyeksportowaniu samej postaci jako pojedynczej, zrigowanej, ale nieanimowanej siatki FBX, a następnie wyeksportowaniu poszczególnych animacji do odrębnych plików FBX. Powstaje więc jeden plik z siatką i kilka dodatkowych z danymi animacyjnymi. Każdy z tych dodatkowych plików zawiera kompletną animację chodu, biegu, skoków itp. W tej metodzie siatki i animacje są porozkładane w odrębnych plikach. Jej podstawową wadą jest to, że wraz ze wzrostem liczby obiektów i animacji rośnie również liczba plików. Eksportowanie animacji i siatek do jednego pliku W metodzie drugiej zarówno siatka, jak i jej animacje (ruchy) są eksportowane do jednego wspólnego pliku. Zamiast do odrębnych plików animacje trafiają na jedną oś czasu, gdzie są ustawiane jedna po drugiej. Przedział czasowy jest powiększany wystarczająco, by wszystkie animacje się w nim zmieściły. Każda z nich zajmuje tylko fragment osi czasu. Eksportowanie siatki wraz z animacjami jest wygodne, ponieważ w Unity wystarczy wtedy zaimportować tylko jeden plik. Później zobaczysz, że tak zaimportowane animacje można nadal łączyć z siatkami z innych plików. Niestety, metoda ta również nie jest wolna od wad: przy dużej liczbie siatek i animacji plik przybiera gigantyczne rozmiary i staje się nieporęczny.
157
158
Rozdział 5
Tok pracy animacyjnej
Uwa ga
Podczas pracy z animacjami będziesz musiał sam decydować, która metoda eksportowania jest najlepsza dla Twojej gry.
Prosta animacja kluczowana — od Blendera do Unity W tym podrozdziale prześledzimy proces tworzenia prostej animacji z klatkami kluczowymi i przenoszenia jej z Blendera do Unity. Do wykonania opisywanych tu czynności możesz użyć własnoręcznie wymodelowanej siatki albo skorzystać z gotowego modelu zapisanego w pliku Rozdział05/KeyFrameStart.Blend dostępnym na stronie z materiałami uzupełniającymi (ftp://ftp.helion.pl/przyklady/unible.zip). Aby przygotować Blendera do pracy, wykonaj następujące czynności: 1. Włącz interfejs w układzie Animation. Następnie otwórz plik z obiektem. Może to być dowolny obiekt — zwykły sześcian lub cokolwiek innego. Ponownie zachęcam do skorzystania z pliku Rozdział05/KeyFrameStart.Blend. 3. Ustaw długość animacji równą 100 klatek. 4. Zgodnie z tym, co napisałem w punkcie „Długość animacji”, ustaw parametr End Frame w oknie Timeline i w panelu Properties na 99. 5. Parametr Start Frame (klatka początkowa) ustaw w obu miejscach na 0. Domyślnie Blender ustawia tę wartość na 1, ale Unity przyjmuje zawsze, że klatka początkowa importowanej animacji ma numer 0, więc lepiej od razu zadbaj, żeby w tej kwestii nie było rozbieżności między aplikacjami (rysunek 5.10).
Rysunek 5.10. Ustalanie numeracji klatek animacyjnych Źródło: Blender.
Prosta animacja kluczowana — od Blendera do Unity
W naszej prostej animacji, począwszy od klatki 0. aż do 99., obiekt będzie się przesuwał do przodu, do tyłu i znów do przodu. Aby utworzyć taką animację, wykonaj następujące czynności: 1. Ustaw suwak czasu w klatce 0. (Jeśli używasz systemu sterowania z programu Maya, kliknij tę klatkę prawym przyciskiem myszy). 2. Utwórz pierwszą klatkę kluczową z siatką w położeniu początkowym. W tym celu zaznacz siatkę, a następnie na zakładce Object (obiekt) panelu Properties kliknij prawym przyciskiem myszy pole Y (lub X w zależności od kierunku, w którym chcesz przesuwać obiekt) i z podręcznego menu wybierz Insert Single Keyframe. W ten sposób zapiszesz położenie, jakie obiekt będzie zajmował na początku animacji. W oknach Graph Editor (edytor wykresów) i Dopesheet (raport operatorski) rozpocznie się tworzenie ścieżki animacji, a w oknie Timeline pojawi się znacznik klatki kluczowej (rysunek 5.11).
Rysunek 5.11. Ustanawianie pierwszej klatki kluczowej Źródło: Blender.
3. Wstaw następne klatki kluczowe na osi czasu. W przykładowej animacji przesunąłem siatkę do położenia –1.96168 na osi Y i wstawiłem klucz w klatce 20. Następnie cofnąłem ją do położenia Y = 1.24381 i wstawiłem klucz w klatce 55. W klatce 80. wstawiłem klucz dla położenia Y = –1.29317, a w klatce 99. ustawiłem siatkę z powrotem w położeniu początkowym. Nie zapomnij po każdym przemieszczeniu obiektu ustanowić klatki kluczowej lub po prostu włącz funkcję Auto-Key — rysunek 5.12. (Jeśli zdecydujesz się na drugie rozwiązanie, zapoznaj się z tym, co napisałem w punkcie „Uważaj na automatyczne kluczowanie”).
159
160
Rozdział 5
Tok pracy animacyjnej
Rysunek 5.12. Tworzenie sekwencji animacyjnej Źródło: Blender.
4. Gdy animacja będzie już gotowa, zaznacz siatkę i za pomocą polecenia File/Export/ Autodesk FBX wyeksportuj ją, stosując ustawienia pokazane na rysunku 5.9. Powstanie jeden plik FBX zawierający zarówno siatkę, jak i dane animacyjne (jako rezultat zastosowania drugiej metody spośród opisanych w punkcie „Praca z wieloma animacjami”). Na koniec zaimportuj ten plik do Unity (rysunek 5.13).
Rysunek 5.13. Importowanie pliku FBX do Unity Źródło: Unity Technologies.
Prosta animacja kluczowana — od Blendera do Unity
W Unity, gdy zaznaczysz zaimportowaną siatkę w panelu Project (projekt), jej dane wraz z konfiguracją zostaną wyświetlone na trzech zakładkach panelu Inspector (inspektor): ■ Model (model) — na tej zakładce są wyświetlane wszystkie ustawienia związane ze strukturą siatki, z jej wierzchołkami i współrzędnymi UV. ■ Rig (szkielet) — tutaj jest opisana struktura szkieletu, za pomocą którego realizowana jest animacja siatki. ■ Animations (animacje) — ta zakładka zawiera wszystkie dane animacyjne związane z zaimportowaną siatką. Zauważ, że dane dotyczące siatki, szkieletu i animacji są w Unity wzajemnie odseparowane. Podział ten ma niebagatelne znaczenie, ponieważ z jego istnienia wynika, że animacje osadzone w jednym pliku niekoniecznie muszą być stosowane z zapisaną w nim siatką. Równie dobrze można je zastosować z siatkami pochodzącymi z innych plików. Mówimy wtedy o retargetowaniu animacji (rysunek 5.14).
Rysunek 5.14. Zaimportowane dane są dzielone na trzy grupy: siatkowe, szkieletowe i animacyjne Źródło: Unity Technologies.
Teraz przejdziemy do skonfigurowania zaimportowanej siatki, aby nadawała się do użycia w grze. Będzie to polegało głównie na odpowiednim skonfigurowaniu armatury odpowiedzialnej za deformowanie siatki i utworzeniu klipu animacyjnego na podstawie danych zapisanych w zaimportowanej osi czasu.
161
162
Rozdział 5
Tok pracy animacyjnej
1. Otwórz zakładkę Rig panelu Inspector i upewnij się, że z listy rozwijanej Animation Type (typ animacji) wybrana jest opcja Generic (ogólny). Oznacza to, że siatka nie jest humanoidem i jest zgodna z systemem Mecanim. Uwa ga
System Mecanim został wprowadzony do Unity 4. Jest to zbiór funkcji animacyjnych, które ułatwiają importowanie, konfigurowanie i obsługę animowanych modeli siatkowych utworzonych za pomocą programów do modelowania takich jak Blender. Będziemy ich używać dość często, nie tylko w tym rozdziale, ale również w pozostałych. Nieco szerszy opis systemu Mecanim zamieściłem w książce Unity 4 Fundamentals (ISBN: 978-0415823838). Uwa ga
Postać z rysunku 5.13 wygląda wprawdzie jak humanoid, ale dla Unity nim nie jest. Unity uznaje siatkę za humanoidalną, jeśli ta ma określony rodzaj szkieletu z odpowiednią liczbą kości i odpowiednio ze sobą powiązanych. Model z rysunku 5.13 nie ma wcale szkieletu i dlatego został zakwalifikowany jako typ ogólny. 2. Otwórz zakładkę Animations. Zauważ, że prezentuje ona klip o nawie Default Take (ujęcie domyślne), który zawiera całą animację z pliku, począwszy od klatki 0. aż do 99. Później pokażę, jak można tę animację podzielić na mniejsze części. Na razie jednak pozostaniemy przy jednym klipie. Zmień tylko jego nazwę na BasicAnimation (animacja podstawowa) i jeśli to konieczne, ustaw za pomocą pól Start (początek) oraz End (koniec) właściwe numery klatek dla początku i końca animacji. Cała oś czasu reprezentuje pełną animację, jaka została wyeksportowana z Blendera. 3. Włącz opcję Loop Time (zapętlenie czasu), aby zmusić Unity do automatycznego powtarzania animacji. 4. Jeśli chcesz zobaczyć, jak wygląda zaimportowana animacja, kliknij przycisk Play (odtwarzanie) w oknie podglądu (rysunek 5.15). 5. Po wprowadzeniu modyfikacji w ustawieniach klipu kliknij przycisk Apply (zastosuj), aby wszystko zatwierdzić. Dane animacyjne zostaną zaktualizowane.
Jeśli siatkę dodasz do sceny jako obiekt gry (GameObject), a potem klikniesz przycisk Play na górnym pasku aplikacji, żeby uruchomić grę, okaże się, że siatka jest statyczna, czyli że żadna animacja nie została jej automatycznie przypisana. Żeby animacja siatki była uruchamiana automatycznie i przebiegała zgodnie z ustawieniami klipu animacyjnego, musisz to zaaranżować za pomocą elementu o nazwie Animator Controller (sterownik animatora). Niemal wszystkie typy siatek importowane do Unity są umieszczane w scenie z dołączonym składnikiem Animator, którego jednym z elementów jest Controller, i to do niego uzyskujesz dostęp za pośrednictwem elementu Animator Controller. To właśnie on decyduje, kiedy i jak animacja siatki ma być uruchomiona. Domyślnie element ten nie jest przypisany albo ma ustawioną wartość None (nic) i dlatego siatka nie jest animowana w sposób automatyczny (rysunek 5.16).
Prosta animacja kluczowana — od Blendera do Unity
Rysunek 5.15. Konfigurowanie klipu animacyjnego Źródło: Unity Technologies.
Rysunek 5.16. Siatka umieszczana w scenie otrzymuje składnik o nazwie Animator. Pamiętaj, że współczynnik skali ma być ustawiony na 1, aby siatka miała właściwe wymiary (zob. punkt „Współczynnik skali” w rozdziale 2.) Źródło: Unity Technologies.
163
164
Rozdział 5
Tok pracy animacyjnej
Aby utworzyć element gry typu sterownik animatora, kliknij prawym przyciskiem myszy w panelu Project i z podręcznego menu wybierz Create/Animator Controller (utwórz/sterownik animatora). Parametry sterownika można ustawiać za pośrednictwem okna Animator (animator) — nie mylić z oknem Animation (animacja). Aby je otworzyć, wybierz z głównego menu polecenie Window/Animator (okno/animator) — rysunek 5.17.
Rysunek 5.17. Za pomocą sterownika animatora można sterować odtwarzaniem animacji Źródło: Unity Technologies.
Kontroler animatora to specjalny rodzaj elementu gry, który definiuje skończoną maszynę stanów (finite state machine — FSM) dla danej siatki. Jest w zasadzie wizualną formą pisania skryptów. Za pomocą kontrolera możesz wprowadzać siatkę w poszczególne stany animacyjne i określać powiązania między tymi stanami, wskazując, kiedy dana animacja ma się uaktywnić i kiedy ma się wyłączyć. Możesz nawet mieszać kilka animacji, np. strzelanie możesz połączyć z marszem. Co więcej, każda z łączonych animacji może mieć przypisaną określoną wagę. W naszym przykładzie przypiszemy siatce tylko jeden utworzony wcześniej klip o nazwie BasicAnimation. Żeby to zadanie wykonać, po prostu przeciągnij ten klip animacyjny z panelu Project do okna Animator. Spowoduje to automatyczne dodanie klipu do grafu animatora, gdzie będzie widoczny w postaci pomarańczowego węzła. (Kolor pomarańczowy oznacza, że jest to węzeł domyślny, czyli taki, który określa początkową animację siatki). A zatem gdy tylko akcja się rozpocznie, siatka wykona animację zapisaną w klipie BasicAnimation.
Prosta animacja kluczowana — od Blendera do Unity
Jeśli w oknie Animator zaznaczysz węzeł BasicAnimation, to w panelu Inspector będziesz mógł ustawić szybkość (Speed) odtwarzania klipu. Przy szybkości równej 1 animacja jest odtwarzana ze swoją nominalną prędkością, czyli z taką, jaka została ustalona w Blenderze. Gdy zwiększysz tę wartość do 2, szybkość odtwarzania wzrośnie dwukrotnie, a jeśli ją zmniejszysz do 0.5, szybkość animacji zmaleje o połowę itd. W naszym przykładzie pozostaw wartość domyślną, czyli 1 (rysunek 5.18).
Rysunek 5.18. Budowanie grafu animatora Źródło: Unity Technologies.
Po utworzeniu grafu animatora możesz go przypisać siatce poprzez jej składnik o nazwie Animator. Oto czynności, jakie należy w tym celu wykonać: 1. Zaznacz siatkę w scenie. 2. Przeciągnij sterownik kontrolera na pole Controller (sterownik) składnika Animator. 3. Kliknij przycisk Play na pasku narzędziowym, aby się przekonać, czy siatka jest rzeczywiście animowana. To działa, ponieważ wraz z rozpoczęciem akcji siatka zawsze będzie wchodziła w stan animacyjny określony przez domyślny (pomarańczowy) węzeł grafu animatora (rysunek 5.19).
Rysunek 5.19. Obiekt w trakcie odtwarzania animacji Źródło: Unity Technologies.
165
166
Rozdział 5
Tok pracy animacyjnej
Animowanie ruchu wzdłuż ścieżki i wypalanie animacji W grach często zachodzi potrzeba przesuwania obiektów, takich jak ludzie, samochody czy statki kosmiczne, wzdłuż wyznaczonych wcześniej ścieżek (rysunek 5.20). Jedno z najbardziej intuicyjnych rozwiązań polega na narysowaniu ścieżki za pomocą blenderowych narzędzi do tworzenia krzywych i zmuszeniu obiektu, by w czasie trwania całej animacji przemierzył wyznaczoną w ten sposób trasę. Podejście takie implikuje jednak problem wypalania albo, jak chcą niektórzy, konwersji animacji.
Rysunek 5.20. Animacja wzdłuż ścieżki Źródło: Blender.
Przyjrzyjmy się tej sprawie, tworząc animowany obiekt, który będzie się poruszał po wyznaczonej ścieżce. Możesz w tym celu stworzyć własną scenę i obiekt, ale możesz też wykorzystać jako punkt wyjścia plik Follow_Path.blend dostępny w materiałach pomocniczych na stronie internetowej powiązanej z książką. Oto szczegóły: 1. W Blenderze możesz rysować ścieżki za pomocą narzędzi służących do tworzenia krzywych. Po prostu wybierz polecenie Add/Curve/Path (dodaj/krzywą/ścieżkę) — rysunek 5.21. 2. Aby ukształtować ścieżkę, włącz tryb edycji (wciśnij klawisz Tab). Ja narysowałem w tym celu zwykły zygzak (rysunek 5.22). Pamiętaj, że w przypadku ścieżek punkty kontrolne nie leżą bezpośrednio na krzywej, a jedynie wyznaczają kierunki jej wygięć. Rzeczywisty przebieg ścieżki jest wynikiem interpolacji położeń punktów kontrolnych. 3. Żeby poprawić widoczność ścieżki, otwórz zakładkę Object data (dane obiektu) panelu Properties i w rolecie Geometry (geometria) zwiększ wartość parametru Depth (głębokość) należącego do grupy Bevel (fazowanie) — rysunek 5.23.
Animowanie ruchu wzdłuż ścieżki i wypalanie animacji
Rysunek 5.21. Tworzenie krzywej stanowiącej ścieżkę animacji Źródło: Blender.
Rysunek 5.22. Punkty kontrolne wyznaczają krzywiznę ścieżki Źródło: Blender.
4. Aby zmusić obiekt do poruszania się wzdłuż ścieżki, przypisz mu więzy typu Follow Path (podążaj wzdłuż ścieżki). W tym celu otwórz zakładkę Constraints (więzy) panelu Properties i wybierz odpowiednią pozycję z listy możliwych więzów (rysunek 5.24).
167
168
Rozdział 5
Tok pracy animacyjnej
Rysunek 5.23. Większa głębokość fazowania może poprawić widoczność ścieżki Źródło: Blender.
Rysunek 5.24. Więzy Follow Path zmuszają obiekt do poruszania się wyłącznie wzdłuż wskazanej ścieżki Źródło: Blender.
Uwa ga
Więcej informacji na temat więzów Follow Path znajdziesz w dokumentacji Blendera na stronie: http://wiki.blender.org/index.php/Doc:2.6/Manual/Constraints/Relationship/Follow_Path. 5. W rolecie z ustawieniami więzów Follow Path kliknij pole Target (cel) i wskaż narysowaną wcześniej krzywą.
Animowanie ruchu wzdłuż ścieżki i wypalanie animacji
6. W polu Forward (do przodu) wskaż wektor wyznaczający przód obiektu (twarz postaci) jako ten, który ma być ciągle skierowany wzdłuż krzywej. Dzięki temu podczas swej wędrówki po ścieżce obiekt będzie zawsze zwrócony przodem w kierunku ruchu1 (rysunek 5.25).
Rysunek 5.25. Ustawianie obiektu zgodnie z orientacją ścieżki Źródło: Blender.
7. Kliknij przycisk Animate Path (animuj ścieżkę). Spowoduje to wygenerowanie danych animacyjnych niezbędnych do przemieszczenia obiektu wzdłuż ścieżki. Aby przetestować ten ruch, przeciągnij znacznik czasu w oknie Timeline. W oknie widokowym powinieneś widzieć, jak obiekt przesuwa się wzdłuż krzywej, rozpoczynając swój ruch w klatce pierwszej i kończąc w ostatniej. Zwróć uwagę na ustawienie obiektu (przodem w kierunku ruchu) podczas tej wędrówki (rysunek 5.26).
Jest jednak problem z przeniesieniem takiej animacji do Unity. Zapewne zauważyłeś (chociażby na rysunku 5.26), że siatka owszem, porusza się, ale w oknach animacyjnych — takich jak Graph Editor czy Dopesheet — nie widać żadnych klatek kluczowych. Wynika to z faktu, że więzy (w tym i Follow Path) nie generują takich klatek. Sterują ruchem obiektów, ale nie używają do tego klatek kluczowych. Unity jednak akceptuje wyłącznie animacje określone przez klatki kluczowe, kluczowanie kształtu i rigowanie, a w tym przypadku żaden z tych elementów nie istnieje. Oznacza to, że nasza animacja przeniesiona w tym stanie do Unity przestanie być animacją. Otrzymamy tam jedynie statyczną siatkę. Żeby animacja spełniła wymagania Unity, trzeba ją „przełożyć” na język klatek kluczowych. Niektórzy nazywają to wypalaniem animacji.
1
Żeby to zadziałało, trzeba jeszcze włączyć opcję Follow Curve (podążaj za krzywą) — przyp. tłum.
169
170
Rozdział 5
Tok pracy animacyjnej
Rysunek 5.26. Animacja polegająca na przemieszczaniu obiektu wzdłuż ścieżki Źródło: Blender.
Aby przeprowadzić proces wypalania, wykonaj następujące czynności: 1. W oknie 3D View zaznacz siatkę obiektu i z menu wybierz polecenie Object/Animation/ Bake Action (obiekt/animacja/wypal akcję) — rysunek 5.27.
Rysunek 5.27. Przygotowanie do wypalenia animacji z więzami Źródło: Blender.
Animowanie ruchu wzdłuż ścieżki i wypalanie animacji
2. W oknie dialogowym Bake Action ustaw numery klatek dla początku (Start Frame) i końca animacji (End Frame). (W przypadku animacji z więzami będzie to zazwyczaj jej pierwsza i ostatnia klatka). W ten sposób wyznaczysz przedział na osi czasu, który zostanie wypalony (przekonwertowany na klatki kluczowe). 3. Parametr Frame Step (odstęp między klatkami) decyduje o dokładności przekładu animacji. Przy mniejszych wartościach otrzymasz wierniejszą kopię animacji pierwotnej. Musisz jednak uważać, bo jeśli ustawisz wartość najmniejszą, czyli 1, Blender utworzy klucze w każdej klatce animacji, od pierwszej do ostatniej. Cała sztuka polega na tym, by uzyskać dużą dokładność przy możliwie najmniejszej liczbie klatek kluczowych. (Mniejsza liczba kluczy oznacza mniejszą liczbę danych animacyjnych i szybsze działanie w Unity). Ja ustawiłem ten parametr na 4, a więc klucze animacyjne zostaną wygenerowane w co czwartej klatce. 4. Włącz opcje Visual Keying (kluczowanie wizualne) i Clear Constraints (usuń więzy), po czym kliknij przycisk OK, aby wypalić dane animacyjne wygenerowane przez więzy Follow Path (rysunek 5.28).
Rysunek 5.28. Ustawianie parametrów wypalania animacji Źródło: Blender.
Po wypaleniu animacji w oknach edytorów pojawią się klatki kluczowe (rysunek 5.29). Teraz możesz wyeksportować siatkę do Unity jako plik FBX z domyślnym ujęciem sceny. Tam zaimportujesz ją jak typową siatkę animowaną, zgodnie ze wskazówkami podanymi w podrozdziale „Prosta animacja kluczowana — od Blendera do Unity”. Gratulacje! Teraz już możesz bez przeszkód przenosić animacje z Blendera do Unity!
171
172
Rozdział 5
Tok pracy animacyjnej
Rysunek 5.29. Podczas wypalania animacji generowane są klatki kluczowe Źródło: Blender.
Miksowanie kształtów i klucze kształtu W Unity 4.3 wprowadzono obsługę animacji morfingowych, zwanych też miksowaniem kształtów (głównie w Unity i programie Maya). W Blenderze nazwano tę funkcję kluczowaniem kształtu. Wszystkie rozpatrywane do tej pory animacje operują na poziomie obiektowym — to znaczy, że wywołują ruch całego obiektu, np. postaci. Natomiast miksowanie kształtów działa na poziomie struktury obiektu i pozwala animować wierzchołki, krawędzie oraz ścianki w obrębie wybranej siatki. Ten rodzaj animacji przydaje się do symulowania zmiennych wyrazów twarzy, ruchów warg, mrugania oczami i jest potrzebny wszędzie tam, gdzie nie chodzi o ruch obiektu jako całości, a jedynie o zmianę jego struktury. Blenderowe kluczowanie kształtów umożliwia modelowanie postaci w rozmaitych pozach i zapisywanie poszczególnych póz jako kluczy kształtu. Każdemu kluczowi można też przypisać niezależną wartość wagową (z przedziału od 0 do 1 w Blenderze i od 0 do 100 w Unity) określającą siłę, z jaką dana poza będzie wpływała na kształt siatki podczas animacji. Spójrzmy na konkretny przykład. Otwórz plik blendshape_start.blend dostępny w materiałach zamieszczonych na stronie powiązanej z książką. Znajdziesz w nim standardowy obiekt blenderowy w kształcie małpiej głowy (rysunek 5.30), który możesz też utworzyć własnoręcznie przez wybranie polecenia Add/Mesh/Monkey (dodaj/siatka/małpa). Celem ćwiczenia będzie nadanie temu obiektowi różnych form i póz — czyli przypisanie mu różnych kluczy kształtu — aby po przeniesieniu do Unity można było zastosować miksowanie kształtów.
Miksowanie kształtów i klucze kształtu
Rysunek 5.30. Przygotowanie do tworzenia kluczy kształtu Źródło: Blender.
Najpierw trzeba utworzyć pozę bazową (Basis). Będzie ona reprezentowała neutralną (domyślną) konfigurację siatki, czyli taką, jaką ma bez przypisanej jakiejkolwiek pozy. Będzie to domyślny układ wierzchołków siatki. Wykonaj więc następujące czynności: 1. Zaznacz głowę małpy w oknie widokowym. 2. W panelu Properties otwórz zakładkę Object data. 3. W rolecie Shape Keys (klucze kształtu) kliknij widoczną z prawej strony ikonę ze znakiem plus (+), aby utworzyć pierwszy, bazowy klucz kształtu (rysunek 5.31).
Rysunek 5.31. Tworzenie bazowego klucza kształtu Źródło: Blender.
173
174
Rozdział 5
Tok pracy animacyjnej
1. Utworzenie bazowego klucza kształtu jest równoznaczne z zapisaniem kopii siatki i jej bieżącego stanu. Aby utworzyć nowy stan, dodaj nowy klucz kształtu, po czym zdeformuj siatkę, używając standardowych narzędzi modelarskich. (Rób to w takiej właśnie kolejności! Najpierw dodawaj klucz, a potem deformuj). Kliknij więc ponownie ikonę ze znakiem plus (+) w rolecie Shape Keys (rysunek 5.32). Modyfikacje, jakie teraz przeprowadzisz, zostaną zapisane w tym kluczu kształtu, który jest zaznaczony na liście. Ja utworzyłem dwa stany: Raised (uniesiony) i Lowered (obniżony). Zauważ, że gdy opuszczasz tryb edycji i wracasz do trybu obiektowego, wszystkie modyfikacje kształtu obiektu przestają być widoczne w oknie widokowym — wyświetlany jest stan bazowy.
Rysunek 5.32. Definiowanie kluczy kształtu Źródło: Blender.
2. Gdy już utworzysz wszystkie potrzebne klucze kształtu, wybierz jeden z nich i zmiksuj z kluczem bazowym przez wpisanie odpowiedniej liczby w polu Value (wartość). Każdemu kluczowi możesz przypisać inną wartość z zakresu od 0 do 1, która określi, jak mocno dany klucz ma się mieszać z kluczem bazowym i innymi kluczami przypisanymi siatce. Wartość 0 oznacza, że klucz nie ma żadnego wpływu na wygląd siatki, a przy wartości równej 1 jego wpływ jest największy. Przykład mieszania kluczy kształtów jest pokazany na rysunku 5.33. 3. Przed zaimportowaniem siatki z kluczami kształtu sprawdź w Unity, czy w panelu Inspector jest włączona opcja Import BlendShapes (importuj mieszane kształty) — rysunek 5.34.
Miksowanie kształtów i klucze kształtu
Rysunek 5.33. Aby wykonać morfing siatki bazowej, zwiększ wartość (Value) klucza Raised Źródło: Blender.
Uwa ga
Blender umożliwia też animowanie zawartości pola Value, a to oznacza, że można tworzyć animowane przejścia pomiędzy poszczególnymi stanami. Wszystko sprowadza się do utworzenia klatek kluczowych z różnymi liczbami w polu Value i wygenerowania klatek pośrednich, które spowodują, że przejście od jednego stanu do drugiego będzie płynne, a nie skokowe. Takiej animacji nie da się jednak przenieść do Unity (przynajmniej w czasie pisania tej książki), mimo że same klucze kształtu są przez tę aplikację akceptowalne. Później pokażę, jak można ten problem rozwiązać, a na razie poprzestaniemy na wyeksportowaniu małpiej głowy z kluczami kształtu do standardowego pliku FBX i zaimportowaniu jej do Unity. 4. Dodaj importowaną siatkę do sceny, zaznacz ją, a następnie sprawdź jej komponenty w panelu Inspector. Znajdziesz tam komponent o nazwie Skinned Mesh Renderer (renderer siatki pokrytej skórą), który w rolecie BlendShapes (mieszane kształty) wyświetla wszystkie zaimportowane klucze kształtów. Każdy klucz ma własną wartość w postaci liczby zmiennoprzecinkowej z przedziału od 0 do 100 (a nie, jak w Blenderze, od 0 do 1), która domyślnie wynosi 0. Aby uzyskać morfing siatki bazowej, musisz zwiększyć tę wartość — podobnie jak robiłeś to w Blenderze (rysunek 5.35).
175
176
Rozdział 5
Tok pracy animacyjnej
Rysunek 5.34. Aby zaimportować blenderowe klucze kształtów, włącz opcję Import BlendShapes Źródło: Blender.
Kości i rigowanie
Rysunek 5.35. Stosowanie kluczy kształtów w Unity po zaimportowaniu ich z Blendera Źródło: Unity Technologies.
Jak już wspominałem, blenderowe klatki kluczowe wygenerowane dla kluczy kształtów nie przenoszą się do Unity jak inne klatki kluczowe — taka jest sytuacja, gdy piszę tę książkę. W praktyce oznacza to, że mieszania kształtów nie da się importować jako danych animacyjnych. Problem ten można rozwiązać na dwa sposoby. ■ Utwórz wszystkie animacje dla kluczy kształtów w Unity. Użyj do tego dawnego edytora animacji dostępnego po wybraniu polecenia Window/Animation (okno/animacja). ■ Utwórz w Blenderze animację obiektu pozornego z klatkami kluczowymi dla ruchu wzdłuż jednej tylko osi (np. Y) i z ograniczeniem położenia do wartości z przedziału od 0 do 100. Następnie zaimportuj ten obiekt do Unity i użyj krzywych funkcyjnych jego animacji do sterowania wartościami kluczy kształtów komponentu Skinned Mesh Renderer. Powiązanie między obiektem pozornym a kluczami kształtu musisz ustalić za pomocą odpowiedniego skryptu.
Kości i rigowanie Trzeci i ostatni rodzaj animacji obsługiwany zarówno przez Blendera, jak i przez Unity to animacja szkieletowa. Polega na deformowaniu siatki za pośrednictwem zbudowanego w tym celu szkieletu (hierarchicznego układu kości). Szkielet zawiera też symulacje stawów i innych połączeń międzykostnych. Proces łączenia go z modelem nosi nazwę skinningu (czyli oblekania skórą). W Unity struktura szkieletowa nazywana jest rigiem, a w Blenderze otrzymała nazwę armatury (rysunek 5.36). Szczegółowe objaśnianie technicznych aspektów kości i rigowania wykracza poza ramy tej książki. Zakładam, że zagadnienia te nie są Ci całkowicie obce, i raczej skoncentruję się na omówieniu praktycznych aspektów przygotowywania szkieletów w Blenderze, zwłaszcza takich, które mają być wykorzystane również w Unity.
177
178
Rozdział 5
Tok pracy animacyjnej
Rysunek 5.36. Model postaci z armaturą w sekwencji animacyjnej chodu Źródło: Blender.
Zawsze nadawaj nazwy poszczególnym kościom Dobrym nawykiem jest nadawanie opisowych nazw wszystkim kościom szkieletu, ponieważ zarówno Blender, jak i Unity często wymagają wskazania określonej kości na podstawie jej nazwy. Nie odkładaj tego na później. Wpisuj nazwy natychmiast po utworzeniu kości. Warto też przyjąć określoną konwencję nazewniczą, zwłaszcza w odniesieniu do szkieletów postaci humanoidalnych i ogólnie istot żywych. Powszechnym standardem jest oznaczanie symetrycznie rozłożonych kości przyrostkami .l lub .r (ewentualnie _left lub _right) w zależności od tego, czy dana kość leży po lewej (left), czy po prawej (right) stronie szkieletu. A zatem kości rąk będą miały nazwy ręka.l i ręka.r, kości udowe — udo.l i udo.r itd. Stosowanie tego typu nazw to nie tylko ułatwienie dla współpracujących z Tobą artystów i programistów; jest po prostu konieczne przy realizacji niektórych zadań w Blenderze i Unity. Blender stosuje symetryczne nazwy dla kości tworzonych za pomocą funkcji X-Axis Mirror (odbicie lustrzane względem osi X), a Unity używa ich do automatycznego konfigurowania kości w awatarze postaci (więcej informacji na ten temat znajdziesz w punkcie „Importowanie zrigowanych postaci do Unity” pod koniec rozdziału) — rysunek 5.37.
Szkielety symetryczne a funkcja X-Axis Mirror Większość postaci ludzkich, humanoidalnych i zwierzęcych ma budowę symetryczną. Na przykład jeśli przyjąć kręgosłup za oś symetrii, człowiek ma po obu stronach rękę i nogę. W rzeczywistości nie są one idealnie symetryczne, ale w grafice komputerowej można śmiało założyć, że właśnie tak jest.
Kości i rigowanie
Rysunek 5.37. Nie zapominaj o nadawaniu kościom stosownych nazw Źródło: Blender.
Wprawdzie możesz tworzyć każdą kość oddzielnie i rigować obie strony szkieletu niezależnie jedną od drugiej, ale dużo czasu i pracy zaoszczędzisz, jeśli skorzystasz z blenderowej funkcji X-Axis Mirror. Wystarczy wtedy, że utworzysz tylko jedną stronę szkieletu, a druga zostanie wygenerowana automatycznie. Aby funkcja zadziałała prawidłowo, kości muszą być odpowiednio ponazywane. Nazwy muszą być zgodne z konwencją przyrostkową. Lustrzane odbicie ręki lub nogi powstanie tylko wówczas, gdy utworzonej kości nadasz nazwę zakończoną przyrostkiem .l lub .r. Aby w trakcie edycji armatury uruchomić funkcję X-Axis Mirror, zaznacz w przyborniku pole wyboru o takiej właśnie nazwie (rysunek 5.38).
Rysunek 5.38. Podczas tworzenia szkieletu symetrycznego włącz funkcję X-Axis Mirror Źródło: Blender.
179
180
Rozdział 5
Tok pracy animacyjnej
Uwa ga
Pamiętaj, że nazwy kości możesz zmieniać w oknie Outliner (organizator), w panelu N i na zakładce Bone (kość) panelu Properties (rysunek 5.38).
Kinematyka prosta i odwrotna Podczas pracy z długimi łańcuchami kości, takimi jak ręce, nogi czy inne kończyny (np. ramiona ośmiornicy), często powstaje problem ułożenia ich odpowiednio do żądanej pozy. Zazwyczaj animator chce ustawić w ustalonym położeniu dłoń lub stopę (czyli koniec łańcucha kości) — choćby po to, żeby postać wzięła do ręki filiżankę z kawą lub postawiła stopę na kolejnym stopniu schodów. Domyślnie Blender przypisuje każdemu szkieletowi kinematykę prostą, co w praktyce znacznie utrudnia animowanie takich póz, ponieważ wymaga manipulowania (przesuwania i obracania) wszystkimi elementami łańcucha, a nie tylko ostatnią kością. W przypadku ręki trzeba ustawić we właściwym położeniu nie tylko dłoń, ale także przedramię, łokieć i ramię, a może nawet i bark. Inaczej sprawa wygląda w kinematyce odwrotnej (Inverse Kinematics — IK). Tutaj wystarczy ustawienie tylko końcówki łańcucha, np. dłoni lub stopy. Pozostałe kości ustawi Blender, dbając przy tym, żeby wszelkie transformacje poszczególnych kości przebiegały zgodnie z ograniczeniami wynikającymi z ich wzajemnych połączeń. Wymaga to jednak specjalnego skonfigurowania systemu szkieletowego na etapie rigowania, ale o tym za chwilę. Włączanie kinematyki odwrotnej dla ręki czy nogi przebiega, ogólnie rzecz biorąc, podobnie. Zobaczmy zatem, jak wygląda to w przypadku nogi. Wykonaj następujące czynności: 1. Włącz tryb układania póz (Pose Mode) i zaznacz kość piszczelową. 2. Z zakładki Bone Constraints (więzy kości) panelu Properties wybierz więzy o nazwie Inverse Kinematics (kinematyka odwrotna) — rysunek 5.39. 3. We właściwościach kinematyki odwrotnej wskaż jako cel (Target) kość stopy, czyli koniec łańcucha, który ma być animowany. 4. W polu Chain Length (długość łańcucha) określ długość łańcucha objętego kinematyką odwrotną. W przypadku nogi, która składa się z dwóch kości — piszczelowej i udowej — długość łańcucha należy ustawić na 2 (rysunek 5.40).
To wszystko! Do skonfigurowania kinematyki odwrotnej dla nogi wystarczyło wykonanie tych kilku czynności. Jeśli teraz przesuniesz lub obrócisz stopę bądź tułów, noga automatycznie zegnie się w kolanie, aby dopasować się do nowej pozy całego szkieletu.
Kości i rigowanie
Rysunek 5.39. Kinematykę odwrotną przypisujemy kościom jako rodzaj więzów Źródło: Blender.
Rysunek 5.40. Konfigurowanie nogi z kinematyką odwrotną Źródło: Blender.
Uwa ga
Darmowa wersja Unity nie obsługuje kinematyki odwrotnej, ale to nie oznacza, że zrigowanych w ten sposób modeli nie da się zaimportować. Podczas eksportowania animacji opartej na kinematyce odwrotnej Blender utworzy odpowiednie klatki kluczowe i w Unity będzie można taką animację odtworzyć. Nie da się jednak skorzystać z kinematyki odwrotnej w czasie rzeczywistym.
181
182
Rozdział 5
Tok pracy animacyjnej
Kości deformowane i sterujące Z punktu widzenia rigowania kości dzielą się na dwa podstawowe rodzaje: ■ Sterujące — do których animator ma bezpośredni dostęp i którymi może manipulować. Zazwyczaj są to elementy końcowe łańcuchów IK (takie jak stopy czy dłonie), a także inne istotne dla animacji kości, typu głowa, tors czy biodra. ■ Deformowane — zwane też kośćmi mechanizmu — są tymi, które w wyniku przemieszczania kości sterujących deformują się, ale animator nie ma do nich bezpośredniego dostępu. Innymi słowy: animator edytuje wyłącznie kości sterujące, a nigdy deformowane. Podział ten nie został oficjalnie wprowadzony ani w Blenderze, ani w Unity; stworzyli go sami animatorzy, którzy podczas opracowywania animacji w różny sposób używają poszczególnych kości szkieletu. Dobrą praktyką jest, aby wszystkie kości deformowane umieszczać na odrębnej warstwie i na czas rigowania ukrywać je, pozostawiając widoczne jedynie kości sterujące. Wtedy praca nad animacją staje się dużo łatwiejsza. Sama procedura ukrywania kości deformowanych wygląda następująco: 1. Zaznacz w szkielecie wszystkie kości deformowane. 2. Wciśnij klawisz M, aby otworzyć okno dialogowe Change Bone Layers (zmień warstwy kości). 3. Wskaż nieużywaną warstwę. Na niej zostaną umieszczone zaznaczone kości (rysunek 5.41). (Przeniesienie kości na inną warstwę wpływa jedynie na ich widoczność, a nie na funkcjonowanie).
Eksportowanie zrigowanych postaci Rigowane i animowane postacie można eksportować zarówno do formatu FBX (.fbx), jak i Collada (.dae). Służą do tego polecenia odpowiednio File/Export/Autodesk FBX i File/Export/Collada. Na rysunku 5.42 pokazane są typowe ustawienia eksportu do formatu FBX, a na rysunku 5.43 — analogiczne ustawienia dla formatu Collada. Jeśli wybierzesz format Collada, pamiętaj, żeby eksportowana postać miała pozę spoczynkową.
Kości i rigowanie
Rysunek 5.41. Ukrywanie deformowanych kości na nieużywanej warstwie Źródło: Blender.
Rysunek 5.42. Eksportowanie zrigowanej siatki do formatu FBX Źródło: Blender.
183
184
Rozdział 5
Tok pracy animacyjnej
Rysunek 5.43. Eksportowanie zrigowanej siatki do formatu DAE Źródło: Blender.
Importowanie zrigowanych postaci do Unity Wprowadzony w Unity 4 animacyjny system Mecanim oferuje bogaty zestaw funkcji służących do pracy ze zrigowanymi postaciami. Import takiej postaci odbywa się niemal tak samo jak import zwykłej siatki — wystarczy przeciągnąć plik z Eksploratora (Windows) lub Findera (Mac OS) do panelu Project w Unity. Po zaimportowaniu postaci trzeba jednak dokonać pewnych modyfikacji w ustawieniach dostępnych w panelu Inspector. Na zakładce Rig należy rozwinąć listę Animation Type i wybrać z niej pozycję Humanoid — tak jak na rysunku 5.44. Będzie to sygnałem dla Unity, że zaimportowana siatka jest nie tylko animowana, ale ma także humanoidalny szkielet. Po takim uściśleniu informacji Unity udostępni dodatkowe funkcje, które sprawią, że praca z postacią stanie się łatwiejsza. (Więcej szczegółów na ten temat znajdziesz w ramce poniżej).
Kości i rigowanie
Rysunek 5.44. Importowanie siatki zrigowanej postaci do Unity Źródło: Unity Technologies.
Op c je dl a szki el e tu h um a n oi da l n eg o
Gdy wybierzesz z listy Animation Type opcję Humanoid, Unity wygeneruje dla zaimportowanej postaci awatar, a mówiąc dokładniej: jego definicję. Jest to specjalny element, który Unity łączy z siatką postaci. Dokładnie rzecz biorąc, wyznacza on swego rodzaju odwzorowanie przypisujące każdej kości szkieletu określony sposób deformowania otaczającej ją siatki. Odwzorowanie to nosi nazwę Muscles (mięśnie). W przypadku siatki zaimportowanej w jednym pliku ze szkieletem stosowanie awatara może wydawać się zbędne, bo przecież wszystkie dane skinningowe wiążące kości z siatką zostały już ustalone w Blenderze jako wagi wierzchołków. Wprowadzenie awatara otwiera jednak dostęp do całej potęgi retargetingu. Dzięki awatarom można sterować różnymi postaciami za pomocą tego samego szkieletu i jego animacji. Za pośrednictwem awatara można określić, jak szkielet ma być powiązany z modelem. Zwykłe przedefiniowanie sposobu powiązania kości z mięśniami pozwala użyć tego samego szkieletu do sterowania postaciami różniącymi się np. rozmiarami. Podgląd i edycja awatara są dostępne w trybie edycji awatara (Avatar Editor) uruchamianym przez kliknięcie przycisku Configure (konfiguruj) w panelu Inspector. Jeśli szkielet ma typowo humanoidalną formę i zawiera właściwe kości, Unity stanie na wysokości zadania i skonfiguruje awatara bez Twojego udziału. Zazwyczaj nie będziesz musiał niczego poprawiać (rysunek 5.45).
185
186
Rozdział 5
Tok pracy animacyjnej
Rysunek 5.45. Podgląd awatara Źródło: Unity Technologies.
W trybie edycji awatara okno widokowe Scene (scena) wyświetla zrigowany model, a w panelu Hierarchy (hierarchia) wyświetlana jest w pełni rozwinięta blenderowa hierarchia kości. W panelu Inspector wyświetlane są powiązania między kośćmi a siatką (zakładka Mapping) oraz parametry mięśni (zakładka Muscles). Powiązania powinny być w kolorze zielonym. Barwa czerwona oznacza problem i zazwyczaj wtedy potrzebna jest interwencja w postaci ręcznego ustawienia właściwych powiązań. Można to zrobić przez przeciągnięcie kości z panelu Hierarchy na odpowiednie pole anatomiczne w panelu Inspector. Tylko z właściwie przypisanymi kośćmi bioder, kręgosłupa, głowy, rąk i nóg awatar będzie działał prawidłowo. Poświęć trochę czasu, aby go należycie zdefiniować.
Podsumowanie W tym rozdziale omówiłem skomplikowane i ważne zagadnienia związane z tworzeniem animacji za pomocą Blendera i Unity. Blender oferuje trzy formy animacyjne, które można bez przeszkód eksportować do Unity: podstawową animację z klatkami kluczowymi (w tym animacja wzdłuż ścieżki), klucze kształtu i animacje szkieletowe. Łącznie umożliwiają one tworzenie animacji ciała sztywnego (czyli ruchów polegających na zmianie położenia lub orientacji całego obiektu) oraz animacji na poziomie strukturalnym (gdy obiekt jako całość pozostaje nieruchomy, a zmienia się jedynie jego struktura — np. gdy postać porusza ustami). Używając tych form, można stworzyć niemal każdą animację potrzebną w grach komputerowych, a to oznacza, że Blender i Unity tworzą duet o naprawdę wielkich możliwościach.
R OZ DZ I AŁ 6
O BIEKTY , ZALEŻNOŚCI I PROGRAMOWANIE ZDARZENIOWE
Złożoność rzeczy — rzeczy w rzeczach — wydaje się nieskończona. To znaczy, że nic nie jest łatwe i nic nie jest proste. — Alice Munro Po przeczytaniu tego rozdziału powinieneś: ■ rozumieć zależności zaprogramowane i znać sposoby ich eliminowania; ■ umieć pracować z komponentami i komunikatami; ■ wiedzieć, na czym polega programowanie zdarzeniowe; ■ umieć tworzyć systemy zdarzeń i powiadomień; ■ rozumieć relacje hierarchiczne między obiektami. W poprzednich rozdziałach koncentrowałem się głównie na prezentowaniu takich metod pracy w Blenderze, których rezultaty mogą być użyteczne w Unity. Pokazywałem więc, jak należy tworzyć elementy gry, takie jak modułowe środowiska, tereny czy siatki animowane, aby dało się je przenieść z Blendera do Unity przy zachowaniu jak największej liczby ich funkcji. Teraz jednak zmienię podejście i skupię się bardziej na praktycznych aspektach pracy w Unity. Szczególnie dużo uwagi zamierzam poświęcić zależnościom zaprogramowanym na sztywno i sposobom ich rozwikływania. Zacznę jednak od wyjaśnienia, czym te zależności są i jak powstają.
187
188
Rozdział 6
Obiekty, zależności i programowanie zdarzeniowe
Zależności zaprogramowane Gra komputerowa jest formą programu i powstaje jako wytwór inżynierii oprogramowania. W większości dziedzin inżynieryjnych wytwarzanie polega na składaniu pewnej całości z mniejszych elementów i nieważne, czy jest to silnik samochodu, most, tunel, budynek czy program komputerowy — gotowy wyrób jest z technicznego punktu widzenia sumą określonej liczby części. Może to budzić zastrzeżenia artystów i estetów, ale w ujęciu strukturalnym całość jest po prostu rezultatem połączenia składników. Solidność i niezawodność wytwarzanego w ten sposób produktu zależą w najwyższej mierze od tego, jakie części zostały użyte i jak je połączono. Ale to nie wszystko — od jakości zastosowanych elementów zależy także łatwość utrzymania w dobrym stanie raz zbudowanej konstrukcji. Rozważmy uproszczony przypadek idealnego zegara złożonego z wielu wzajemnie połączonych kół zębatych. (Wyobraź sobie olbrzymi, klasyczny zegar podobny do Big Bena w Londynie lub Abraj Al Bait w Mekce). Jeśli koła się obracają, zegar działa. Gdy dochodzi do awarii, zegarmistrz sprawdza każde z nich, znajduje wadliwe i wymienia na nowe, takie samo jak oryginalne. Nowe koło zębate integruje się z pozostałymi dokładnie tak samo jak jego poprzednik i cały system zaczyna znów działać, jakby nic się nie stało. Taka izolowana naprawa jest możliwa, ponieważ każde koło, chociaż ściśle współpracuje z pozostałymi, konstrukcyjnie jest od nich niezależne. Wymiana jednego z nich nie wpływa więc w żaden sposób na inne części systemu. Gdyby jednak konstruktor zegara wymyślił zmodyfikowane koło zębate z dodatkowymi elementami, zależnymi od innych składników mechanizmu, wówczas naprawa wymagająca wymiany koła byłaby bardziej skomplikowana. Powodem większych komplikacji byłyby właśnie te dodatkowe połączenia z innymi elementami. Takie koło nie byłoby już konstrukcyjnie niezależne od pozostałych. Zależności te byłyby ustanawiane już na etapie wytwarzania. Przy tworzeniu gier też możemy mieć do czynienia z tego typu zależnościami, zwłaszcza gdy stosujemy programowanie obiektowe, kiedy to jedna klasa jest tworzona jako bezpośrednia lub pośrednia pochodna innej klasy. Powiedzmy, że na potrzeby gry RPG tworzysz postać czarnoksiężnika i w języku C# definiujesz dla niego klasę o nazwie Wizard (czarnoksiężnik). Z pewnością będziesz chciał, aby Twój bohater mógł za pomocą zaklęć zadawać wrogom obrażenia. Również sam powinien ucierpieć, jeśli zostanie zaatakowany. Zdarzenia zachodzące w czasie gry, takie jak ukończenie zadania czy pokonanie wroga, powinny przenosić postać na wyższy poziom i zwiększać liczbę posiadanych przez nią punktów doświadczenia. Żeby to wszystko mogło działać, klasa Wizard musi „wiedzieć”, że takie czy inne zdarzenie zaistniało, mimo że każde z tych zdarzeń dzieje się poza nią. Krótko mówiąc: klasa ta — jak większość klas funkcjonujących w grze — nie może istnieć w całkowitej izolacji od otoczenia. Wcześniej czy później twórca gry musi ją powiązać z innymi klasami.
Zależności zaprogramowane
Rodzi się tylko pytanie: czy takie powiązania da się ustanowić bez zaprogramowania ich na sztywno? Ogólnie rzecz biorąc: zależności zaprogramowane są złe, ponieważ ustanawiają stałe połączenia między klasami i obiektami. Zamiana, modyfikacja lub usunięcie jednej klasy oznacza zatem konieczność wprowadzenia zmian także w innych klasach. Wróćmy do przykładu z czarnoksiężnikiem rzucającym czary na swoich wrogów. Listing 6.1 przedstawia powiązanie czarnoksiężnika z wrogiem zakodowane na sztywno w języku C#. Listing 6.1. Klasa Wizard z zaprogramowanymi zależnościami public class Wizard { //Referencja do klasy wroga. public MyEnemy Enemy; //Funkcja wywoływana, gdy czarnoksiężnik rzuca zaklęcie ognistej kuli. public void OnCastFireBallSpell() { //Zadaje wrogowi 20 punktów obrażeń. Enemy.DealDamage(20); } }
Listing 6.1 przedstawia klasyczny przykład zależności zaprogramowanych. Klasa Wizard utrzymuje bezpośrednią i wewnętrzną referencję do obiektu wroga, aby za pomocą funkcji DealDamage (zadaj obrażenie) uszczuplać jego siły. Oczywiście program sam w sobie nie jest zły, jeśli tylko działa. Problem w tym, że bardzo łatwo można doprowadzić do jego awarii. Nie musisz się specjalnie starać, żeby zaczął źle funkcjonować. Przykładowo: jeśli w jakimś momencie uznasz, że klasę Wizard trzeba całkowicie usunąć, postać wroga nie dozna więcej obrażeń, ponieważ nie będzie już funkcji OnCast FireBallSpell (rzucanie zaklęcia ognistej kuli), która te obrażenia wywołuje. Naturalnie mogłaby istnieć jeszcze inna klasa wywołująca obrażenia ogniowe, ale byłoby to powielaniem tego, co robi już klasa Wizard (takie powielanie też nie jest dobre, ponieważ prowadzi do niepotrzebnego wydłużania kodu). Co więcej, jeśli zmodyfikujesz klasę Enemy, zamieniając funkcję DealDamage na precyzyjniej nazwaną DealFireDamage (zadaj obrażenie ogniowe), kompilator zgłosi błąd, ponieważ postać czarnoksiężnika będzie chciała wywołać funkcję DealDamage, a tej już nie będzie. Krótko mówiąc: chodzi o to, że z powodu wzajemnych zależności między klasami nie można tak po prostu zmienić jednej z nich i nie modyfikować pozostałych. W takich prostych przypadkach jak ten z listingu 6.1 nie jest to wielki problem, ale gdy dotyczy to wszystkich klas rozbudowanego programu (gry), wówczas powstaje taka plątanina wzajemnych powiązań, że zapewnienie sprawności działania kodu staje się wręcz niemożliwe. Na szczęście są sposoby radzenia sobie z tego typu problemami.
189
190
Rozdział 6
Obiekty, zależności i programowanie zdarzeniowe
Rozwiązanie DI1 — projektowanie komponentowe i komunikaty W obiektowo zorientowanym środowisku języka C# problem programowanych zależności rozwiązuje się — na ile jest to w praktyce możliwe — przez stosowanie funkcji wirtualnych, interfejsów i polimorfizmu. Ostatnio pojawiło się jednak kilka rozwiązań o charakterze zewnętrznym w stosunku do środowisk zorientowanych obiektowo. W Unity są to komponenty i komunikaty. Aby móc z nich skorzystać, trzeba dobrze zrozumieć, czym są i jak działają. Zobaczmy zatem, co kryje się pod każdym z tych pojęć.
Projektowanie komponentowe W Unity poziom lub środowisko gry to po prostu scena. Składa się ona z hierarchii obiektów, których nazwy są wyświetlane w panelu Hierarchy (hierarchia), gdy tylko scena jest otwarta w edytorze. Na rysunku 6.1 pokazana jest scena zawierająca jedynie kamerę i umieszczony w punkcie centralnym sześcian.
Rysunek 6.1. Scena jest hierarchicznym układem obiektów Źródło: Unity Technologies.
Tak jak scenę można rozłożyć na poszczególne obiekty, tak i każdy obiekt można rozbić na mniejsze elementy. Każdy obiekt uczestniczący w grze jest zbudowany z komponentów. Obowiązkowo w każdym obiekcie musi być komponent Transform (przekształcenie), który określa położenie, orientację i skalę obiektu względem globalnego układu współrzędnych. Zazwyczaj obiekty zawierają jeszcze inne komponenty. Na przykład sześcian z rysunku 6.2 ma ich cztery: Transform, Mesh Filter (filtr siatki), Box Collider (zderzacz prostopadłościenny) i Mesh Renderer (renderer siatki). Pierwszy określa dane transformacyjne obiektu, drugi zawiera dane siatkowe (wierzchołki, krawędzie i wielokąty), trzeci definiuje prostopadłościan służący do wykrywania kolizji, a czwarty umożliwia wyświetlanie siatki w trakcie gry. 1
Skrót DI oznacza wstrzykiwanie zależności (Dependency Injection) — przyp. tłum.
Rozwiązanie DI — projektowanie komponentowe i komunikaty
Rysunek 6.2. Każdy obiekt na scenie gry składa się z komponentów Źródło: Unity Technologies.
Definiowanie obiektów w kategoriach modułowych komponentów, jak to robi Unity, jest zasadniczo odmienne od tradycyjnego podejścia obiektowego. Zamiast jednej klasy, zamykającej przy użyciu mechanizmów dziedziczenia hermetycznie cały sześcian wraz z danymi siatkowymi i transformacyjnymi, mamy komponenty stanowiące mniejsze i mniej hermetyczne bloki programowe. W ujęciu komponentowym każdy obiekt jest po prostu określonym zestawem powiązanych ze sobą komponentów. Tutaj obiekty są traktowane jak nowo powstałe byty i dlatego czasami projektowanie komponentowe jest określane jako system bytów (entity system). Siatka sześcianu staje się tym, czym jest, ponieważ tak określają ten byt cztery komponenty (Transform/Mesh Filter, Box Collider i Mesh Renderer). Jeśli usuniesz jeden z tych komponentów (z wyjątkiem Transform), nadal będziesz miał obiekt gry, ale inny. Jeśli tym usuniętym komponentem będzie Mesh Renderer, otrzymasz sześcian niewidzialny. Jeśli usuniesz Box Collider, sześcian zostanie pozbawiony cech materialnych i każdy inny obiekt będzie przez niego swobodnie przenikał bez żadnych kolizji. A jeśli pozbawisz go komponentu Mesh Filter, pozostanie Ci tylko zderzacz. Krótko mówiąc: przez definiowanie obiektów i myślenie o nich w kategoriach komponentów unikasz, przynajmniej do pewnego stopnia, zaplątania się w programowane zależności. Przestań traktować obiekty gry jako skończone, kompletne elementy, a zacznij o nich myśleć jak o bytach powstałych z połączenia odpowiednich komponentów. Oczywiście metoda ta ma zastosowanie nie tylko do rozważanego tu sześcianu, ale do wszystkich obiektów.
Komunikaty W listingu 6.1 klasa Wizard bezpośrednio zwraca się do klasy Enemy, by zadać obrażenia. Takie podejście wymaga, aby klasa Wizard znała choć trochę szczegóły implementacji klasy Enemy — powinna przynajmniej wiedzieć, że funkcja DealDamage jest dostępna.
191
192
Rozdział 6
Obiekty, zależności i programowanie zdarzeniowe
A zatem w praktyce, żeby kompilacja kodu z listingu 6.1 przebiegła pomyślnie, klasa Enemy musi mieć wbudowaną funkcję DealDamage. Zależność taka jest dla programistów istotnym ograniczeniem. W Unity można ten problem ominąć przez zastosowanie systemu komponentów, którym do wzajemnej komunikacji nie jest potrzebna żadna wiedza na temat ich wewnętrznej budowy. Oznacza to, że jeden komponent może odwoływać się do drugiego, nie wiedząc, co tamten robi i jak działa. Prześledźmy to na konkretnym przykładzie. Dla sześcianu z rysunku 6.2 utworzymy dwa nowe komponenty (aby utworzyć sześcian, wybierz z menu GameObject/Create Other/Cube, czyli: obiekt gry/utwórz inny/sześcian). Jeden z tych komponentów będzie miał nazwę Wizard, a drugi — Enemy. Zobaczymy, jak się komunikują. Przypominam, że pliki skryptowe tworzymy w Unity za pomocą polecenia Assets/ Create/C# Script (elementy/utwórz/skrypt C#) — rysunek 6.3. Unity umożliwia tworzenie skryptów także w językach JavaScript i Boo, ale nie będziemy z nich korzystać.
Rysunek 6.3. Tworzenie skryptu w języku C# Źródło: Unity Technologies.
Kod komponentu Wizard jest podany w listingu 6.2, a komponentu Enemy — w listingu 6.3. Objaśnienia tych kodów znajdują się za listingami. Oba komponenty powinny być powiązane z tym samym obiektem gry. Ja powiązałem je z sześcianem, co widać na rysunku 6.4.
Rozwiązanie DI — projektowanie komponentowe i komunikaty
Rysunek 6.4. Przyłączanie skryptów do obiektu gry Źródło: Unity Technologies.
Uwa ga
Aby powiązać skrypty z obiektem, po prostu przeciągnij je z panelu Project na obiekt w scenie. Listing 6.2. Wizard.cs using UnityEngine; using System.Collections; public class Wizard : MonoBehaviour { //Aktualizacja wykonywana w każdej klatce. void Update () { //Jeśli gracz naciśnie przycisk strzelania… if(Input.GetButtonDown("Fire")) { //…zadaj obrażenia wrogom, których komponenty są powiązane z tym obiektem.
SendMessage("ApplyDamage", 10.0f,SendMessageOptions.DontRequireReceiver); } } }
Listing 6.3. Enemy.cs using UnityEngine; using System.Collections; public class Enemy : MonoBehaviour { //Funkcja wywoływana, aby zadać wrogowi obrażenie. public void ApplyDamage(float Damage = 0.0f) { Debug.Log ("Damage Dealt - ouch!"); } }
193
194
Rozdział 6
Obiekty, zależności i programowanie zdarzeniowe
A oto kilka uwag na temat tych listingów. ■ Klasa Wizard wysyła za pomocą funkcji SendMessage (wysyłanie komunikatu) komunikat ApplyDamage do obiektu gry (tego, z którym jest powiązany komponent Wizard). Komunikat ten odbiorą wszystkie inne komponenty powiązane z tym obiektem. ■ Komponenty odbiorą komunikat w formie wywołania funkcji. Skoro w komunikacie jest wymieniona funkcja ApplyDamage, to właśnie ona zostanie wywołana w ramach danego komponentu. Jeśli komponent nie ma takiej funkcji, to po prostu nic się w nim nie wydarzy. ■ Argument SendMessageOptions.DontRequireReceiver (opcje wysyłania komunikatu — nie wymaga odbiorcy) pełni funkcję swoistego zabezpieczenia na wypadek, gdyby się okazało, że żaden z komponentów nie ma zaimplementowanej funkcji ApplyDamage. Dzięki jego obecności system nie potraktuje tego jako błąd. Uwa ga
Więcej informacji na temat funkcji SendMessage znajdziesz w dokumentacji programu Unity (http://docs.unity3d.com/Documentation/ScriptReference/GameObject.SendMessage.html). Wielką zaletą systemu opartego na komunikatach i komponentach jest to, że komponent (nadawca) może wysyłać komunikaty do innych (odbiorców) i wcale nie musi znać ich szczegółów implementacyjnych. Odbiorca może odpowiedzieć nadawcy, ale nie musi. Dzięki takiemu systemowi wymiany komunikatów komponenty mogą współpracować ze sobą, mimo że nie są połączone zaprogramowanymi na stałe zależnościami. Uwa ga
Z systemem komponentów i komunikatów wiążą się dość poważne problemy wydajnościowe. Chodzi głównie o to, że funkcja SendMessage (a także BroadcastMessage, o której więcej powiem już za chwilę) wykorzystuje koncepcję znaną jako mechanizm refleksji (reflection). Mówiąc w skrócie: za każdym razem, gdy wywoływana jest funkcja SendMessage, Unity musi przejrzeć wewnętrzną tablicę zaimplementowanych funkcji, aby na podstawie nazwy podanej w argumencie ustalić, którą funkcję i w którym komponencie należy uruchomić (jeśli właściwa zostanie odnaleziona). Jeśli ten proces będzie przeprowadzany często i dla wielu obiektów, to może wydatnie spowolnić działanie całego programu, szczególnie na urządzeniach mobilnych, których moce obliczeniowe są z reguły dość ograniczone. Podsumowując: stosowanie funkcji SendMessage i BroadcastMessage powinno być dobrze przemyślane.
Funkcja BroadcastMessage i hierarchie Czasami bywa konieczne wywołanie określonych funkcji w wielu obiektach jednocześnie. Może to być np. grupa wrogów, którzy powinni zginąć w wyniku eksplozji, a może czarnoksiężnik rzuca czary działające grupowo (np. mass-healing). Aby coś takiego osiągnąć, można wielokrotnie użyć funkcji SendMessage i wysłać odpowiedni komunikat
Wysyłanie komunikatów do wybranych obiektów
do wielu obiektów, ale znacznie wygodniejsze jest użycie funkcji BroadcastMessage (rozpowszechnianie komunikatu). Umożliwia ona przesłanie komunikatu nie tylko do jednego, konkretnego obiektu, ale także do wszystkich jego potomków w scenicznej hierarchii. Innymi słowy: działa podobnie jak SendMessage, tyle że wysyłany przez nią komunikat rozprzestrzenia się kaskadowo w dół hierarchicznej struktury obiektów. A zatem jeśli masz obiekt nadrzędny z wieloma potomkami i chcesz wysłać komunikat do nich wszystkich, to powinieneś skorzystać z funkcji BroadcastMessage. Przykład jej użycia pokazuje listing 6.4. Listing 6.4. Rozsyłanie komunikatu ApplyDamage do bieżącego obiektu i wszystkich jego potomków BroadcastMessage("ApplyDamage", 10.0f, SendMessageOptions.DontRequireReceiver);
Jeśli dobrze zaplanujesz hierarchiczny układ obiektów tworzących scenę, to funkcja BroadcastMessage może się okazać bardzo przydatna. Przy ustalaniu tej hierarchii warto rozważyć wstawienie do sceny obiektu pustego i ustawienie go jako nadrzędnego dla wszystkich pozostałych (rysunek 6.5). Umożliwi to później łatwe rozsyłanie komunikatów, które powinny dotrzeć do każdego obiektu — najczęściej będą to powiadomienia systemowe, takie jak Exit-Game (wyłącz grę), Load-Game (wczytaj grę) czy Save-Game (zapisz grę). Uwa ga
Aby otworzyć pusty obiekt gry, wybierz GameObject/Create Empty (obiekt gry/utwórz pusty). Puste obiekty gry są tym samym co obiekty pozorne w programach do grafiki 3D. Ich jedynym komponentem jest Transform.
Wysyłanie komunikatów do wybranych obiektów Do tej pory rozpatrywaliśmy wysyłanie komunikatów do obiektów pojedynczych i do połączonych ze sobą więzami hierarchicznymi. A co z dowolnymi zestawami obiektów? Może trzeba rozesłać komunikat odrodzenia (respawn) do wszystkich martwych wrogów lub do wszystkich wygasłych dopalaczy (power-upów). Może trzeba zamknąć wszystkie drzwi w reakcji na alarm wywołany przez niepożądanego gościa. A może trzeba tymczasowo wyłączyć wszystkie rodzaje broni będące w posiadaniu graczy, żeby nie mogli atakować. Czasami konieczne jest wysłanie komunikatu do wyselekcjonowanej grupy obiektów, które niekoniecznie muszą należeć do tej samej hierarchii. Możesz wtedy wysłać odpowiedni komunikat kolejno do każdego z tych obiektów za pomocą funkcji SendMessage, ale są sposoby pozwalające uprościć to zadanie. Metoda zaprezentowana w listingu 6.5 polega na zebraniu docelowych obiektów w jedną tablicę, a następnie wysłaniu komunikatu do wszystkich jej elementów.
195
196
Rozdział 6
Obiekty, zależności i programowanie zdarzeniowe
Rysunek 6.5. Tworzenie scen z jednym obiektem nadrzędnym ułatwia rozsyłanie ogólnych komunikatów Źródło: Unity Technologies.
Listing 6.5. Wysyłanie komunikatu do obranych obiektów //Umieść w tablicy wszystkie obiekty określonego typu (wszystkich wrogów). Enemy[] Enemies = Object.FindObjectsOfType
(); //Wyślij komunikat do każdego elementu tablicy. foreach(Enemy E in Enemies) E.SendMessage("ApplyDamage", 10.0f, SendMessageOptions.DontRequireReceiver); //Zrealizuj polecenie zawarte w komunikacie.
Uwa ga
Więcej informacji na temat funkcji FindObjectsOfType (wyszukaj obiekty typu) znajdziesz w oficjalnej dokumentacji programu Unity (http://docs.unity3d.com/Documentation/ScriptReference/Object. FindObjectsOfType.html). Unikaj stosowania jej w procedurach uruchamianych wraz z rysowaniem poszczególnych klatek obrazu.
Wysyłanie komunikatów do obiektów nadrzędnych
Wysyłanie komunikatów do obiektów nadrzędnych Zdarzają się też sytuacje, w których konieczne jest wysłanie jakiegoś sygnału w górę hierarchii. Na przykład gdy w strzelance kosmicznej postać bossa dysponuje bronią wyposażoną w kilka różnych głowic i trzeba te wszystkie głowice unieszkodliwić. Najprawdopodobniej będą one zaimplementowane jako obiekty potomne jednego wspólnego przodka — bossa. Gdy gracz unieszkodliwi taką głowicę, obiekt nadrzędny (przodek) powinien zostać o tym poinformowany. Aby umożliwić obiektowi potomnemu wysłanie powiadomienia do jego przodka, możesz użyć funkcji SendMessageUpwards (wysyłanie komunikatu w górę). Działa ona podobnie jak funkcja BroadcastMessage, tyle że wysyła komunikat do obiektów nadrzędnych (przodków), a nie podrzędnych (potomków). Trzeba jednak uważać, bo wysłany w ten sposób komunikat nie zatrzymuje się na pierwszym napotkanym przodku, lecz wędruje dalej, aż do najwyższego poziomu hierarchii. Przykład wywołania tej funkcji prezentuje listing 6.6. Listing 6.6. Wysyłanie komunikatu w górę (do przodków) SendMessageUpwards("ApplyDamage", 10.0f, SendMessageOptions.DontRequireReceiver);
System powiadomień Stosowanie funkcji SendMessage i BroadcastMessage pozwala uniknąć logicznych zależności i wzajemnych powiązań między obiektami, ale czasami może to nie wystarczyć. Przykładowo: gdy czarnoksiężnik zadaje obrażenia wrogowi, korzystając z funkcji SendMessage, zapewne jeszcze kilka innych obiektów w scenie powinno być poinformowanych o tym zdarzeniu. Klasa GUI musi przecież takie rzeczy wiedzieć, chociażby po to, żeby zaktualizować informacje wyświetlane na pasku stanu gry. Również inni wrogowie chcieliby mieć taką informację, by na jej podstawie ich sztuczna inteligencja mogła podjąć decyzję np. o ucieczce. Ogólnie rzecz biorąc: zawsze jest pewna liczba klas, które powinny wiedzieć, co zdarzyło się w grze — i dotyczy to nie tylko jednego zdarzenia, lecz wszystkich, jakie mają miejsce w czasie trwania gry. Jeśli komunikaty między obiektami przesyłasz za pomocą funkcji SendMessage i BroadcastMessage, czyli w sposób bezpośredni, bez udziału żadnych struktur po-
średniczących, to przy dużych projektach możesz mieć problemy z obsługą zdarzeń. Żeby ich uniknąć, powinieneś już na wstępnym etapie programowania gry utworzyć wyspecjalizowane centrum zarządzania powiadomieniami o zdarzeniach obsługujące wszystkie obiekty gry. Cały system powiadamiania powinien być tak skonstruowany, aby sprawca zdarzenia (nadawca komunikatu) przekazywał informację o typie tego zdarzenia do centrali i dopiero ta rozsyłała ją do wszystkich klas nasłuchujących (listeners), które zostały zarejestrowane jako oczekujące na taki właśnie sygnał. Listing 6.7 przedstawia pełny kod źródłowy kompletnego systemu obsługi zdarzeń (o nazwie NotificationsManager) zapisany w języku C#. W następnych podrozdziałach objaśnię jego działanie i pokażę, jak można go w praktyce wykorzystać.
197
198
Rozdział 6
Obiekty, zależności i programowanie zdarzeniowe
Uwa ga
System NotificationsManager, a także przykład jego użycia w projekcie Unity z obiektami czarownika i wrogów znajdziesz w materiałach powiązanych z książką (w folderze Rozdział06). Listing 6.7. NotificationsManager //KLASA ZARZĄDZAJĄCA ZDARZENIAMI — do przyjmowania powiadomień i powiadamiania //listenerów (klas nasłuchujących). //–––––––––––––––––––––––––––––––––––––––––––––––– using UnityEngine; using System.Collections; using System.Collections.Generic; //–––––––––––––––––––––––––––––––––––––––––––––––– public class NotificationsManager : MonoBehaviour { //Zmienne prywatne. //–––––––––––––––––––––––––––––––––––––––––––––––– //Metoda dostępowa do singletonu. public static NotificationsManager Instance { get{ if(!instance) instance = new NotificationsManager(); return instance; } } //Wewnętrzna referencja do instancji obiektu. private static NotificationsManager instance = null; //Wewnętrzna referencja do wszystkich listenerów. private Dictionary> Listeners = new Dictionary>(); //Metody //–––––––––––––––––––––––––––––––––––––––––––––––– //wywoływane przy uruchamianiu gry. void Awake() { //Jeśli instancja jest aktywna, usuń ją… if(instance) {DestroyImmediate(gameObject); return;} //lub ustaw bieżącą instancję jako jedyną. instance = this; } //–––––––––––––––––––––––––––––––––––––––––––––––– //Funkcja rejestrująca nowy listener. public void AddListener(Component Sender, string NotificationName) { //Dodaj listener do słownika powiadomień. if(!Listeners.ContainsKey(NotificationName)) Listeners.Add (NotificationName, new List()); //Dodaj obiekt do listenerowej listy nadawców danego powiadomienia. Listeners[NotificationName].Add(Sender); } //–––––––––––––––––––––––––––––––––––––––––––––––– //Funkcja usuwająca listener ze słownika powiadomień. public void RemoveListener(Component Sender, string NotificationName) {
System powiadomień //Jeśli w słowniku nie ma takiego klucza, to zakończ. if(!Listeners.ContainsKey(NotificationName)) return; //Przejrzyj listę listenerów i gdy znajdziesz właściwy, usuń go. for(int i = Listeners[NotificationName].Count-1; i>=0; i--) { //Sprawdź identyfikator instancji. if(Listeners[NotificationName][i].GetInstanceID() == Sender.GetInstanceID()) Listeners[NotificationName].RemoveAt(i); //Pasuje. Usuń z listy. } } //–––––––––––––––––––––––––––––––––––––––––––––––– //Funkcja wysyłająca powiadomienie do listenerów. public void PostNotification(Component Sender, string NotificationName) { //Jeśli w słowniku nie ma żadnego klucza, to zakończ działanie. if(!Listeners.ContainsKey(NotificationName)) return; //W przeciwnym razie wyślij powiadomienie do właściwych listenerów. foreach(Component Listener in Listeners[NotificationName]) Listener.SendMessage(NotificationName, Sender, SendMessageOptions.DontRequireReceiver); } //–––––––––––––––––––––––––––––––––––––––––––––––– //Funkcja usuwająca wszystkie listenery. public void ClearListeners() { //Usuń wszystkie listenery. Listeners.Clear(); } //–––––––––––––––––––––––––––––––––––––––––––––––– //Funkcja usuwająca ze słownika redundantne listenery — usunięte i nieistniejące. public void RemoveRedundancies() { //Utwórz nowy słownik. Dictionary> TmpListeners = new Dictionary>(); //Przejrzyj wszystkie pozycje w słowniku. foreach(KeyValuePair> Item in Listeners) { //Przejrzyj listę obiektów listenerów i usuń wpisy z wartością null. for(int i = Item.Value.Count-1; i>=0; i--) { //Jeśli pozycja ma wartość null, usuń tę pozycję. if(Item.Value[i] == null) Item.Value.RemoveAt(i); } //Jeśli lista listenerów dla danego powiadomienia nie jest pusta, dodaj to powiadomienie do //tymczasowego słownika. if(Item.Value.Count > 0) TmpListeners.Add (Item.Key, Item.Value); } //Zamień dotychczasowy słownik na nowy, zoptymalizowany. Listeners = TmpListeners; } //––––––––––––––––––––––––––––––––––––––––––––––––
199
200
Rozdział 6
Obiekty, zależności i programowanie zdarzeniowe
//Funkcja wywoływana przy dodawaniu nowego poziomu; usuwa zbędne wpisy w słowniku //pozostawione przez poprzednią scenę. void OnLevelWasLoaded() { //Usuń zbędne wpisy. RemoveRedundancies(); } //–––––––––––––––––––––––––––––––––––––––––––––––– }
NotificationsManager w szczegółach Podsumowując: system NotificationsManager umożliwia obiektom typu listener rejestrowanie się za pomocą funkcji AddListener jako oczekujące na powiadomienie o zaistnieniu określonego zdarzenia. Na każdy rodzaj zdarzeń (atak wroga, ponowne rozpoczęcie poziomu, śmierć gracza itp.) może oczekiwać kilka listenerów, jeden albo żaden. Gdy tylko zdarzenie zaistnieje, wszystkie listenery zarejestrowane jako oczekujące na ten właśnie typ zdarzenia zostaną o tym fakcie poinformowane. Komponent wywołujący w trakcie gry określone zdarzenie wysyła stosowne powiadomienie do obiektu klasy NotificationsManager za pomocą funkcji PostNotification. Po odebraniu powiadomienia obiekt NotificationsManager przesyła je dalej do wszystkich listenerów zainteresowanych tego rodzaju zdarzeniem. Przykładowo: klasa Enemy dzięki zarejestrowaniu się jako oczekująca na zdarzenie ApplyDamage może zareagować na każde jego wystąpienie (listing 6.8). Listing 6.8. Rejestracja klasy Enemy w systemie NotificationsManager using UnityEngine; using System.Collections; public class Enemy : MonoBehaviour { void Start() {
NotificationsManager.Instance.AddListener(this, "ApplyDamage"); } //Funkcja wywoływana w celu zadania obrażeń wrogowi (zostanie wywołana automatycznie //przez obiekt klasy NotificationsManager). public void ApplyDamage(Component Sender) { Debug.Log ("Damage Dealt - ouch!"); } }
Jak widać, klasa Enemy wykorzystuje standardową funkcję Start(), aby zarejestrować się w systemie zarządzania powiadomieniami jako oczekująca na powiadomienia o wszystkich zdarzeniach typu ApplyDamage. Rejestracja jest czymś, co każdy listener musi zrobić, ale tylko raz. Gdy wystąpi zdarzenie oczekiwane przez listener, system wyśle mu powiadomienie za pomocą funkcji SendMessage, co spowoduje wywołanie w nim odpowiedniej
Singletony
metody. Nazwa metody powinna być zgodna z nazwą oczekiwanego zdarzenia. W listingu 6.8 zarówno metoda klasy Enemy, jak i oczekiwane przez nią zdarzenie mają nazwę ApplyDamage. Przy zachowaniu tych wszystkich reguł informacja o każdym zdarzeniu może być przekazana do dowolnej liczby listenerów — a zatem wielu wrogów może odebrać informację o ataku i odpowiednio zareagować. Zobaczmy teraz, jak wygląda działanie systemu zarządzania powiadomieniami z perspektywy obiektu wywołującego zdarzenie, o którym powinny zostać powiadomione listenery (listing 6.9). Listing 6.9. Przykładowa klasa Wizard wywołująca atak na obiekty klasy Enemy using UnityEngine; using System.Collections; public class Wizard : MonoBehaviour { //Funkcja aktualizacyjna wywoływana w każdej klatce. void Update () { //Jeśli gracz wciska przycisk Fire… if(Input.GetButtonDown("Fire1")) { //…powiadom o tym wrogów.
NotificationsManager.Instance.PostNotification(this, "ApplyDamage"); } } }
Listing ten nie wymaga dodatkowego komentarza (chociaż jest tu widoczny pewien ważny aspekt zarządzania powiadomieniami, ale o tym powiem szerzej w następnym podrozdziale) — po prostu gdy tylko zdarzenie ma miejsce, obiekt, który je wygenerował, wysyła powiadomienie do obiektu NotificationsManager. Uwa ga
Jeśli chcesz zobaczyć obiekt NotificationsManager w działaniu, zajrzyj do plików z projektu Unity w materiałach powiązanych z książką (w folderze Rozdział06).
Singletony NotificationsManager jest obiektem specjalnego rodzaju. Zazwyczaj klasę lub kom-
ponent projektuje się po to, by na tej podstawie utworzyć więcej podobnych do siebie obiektów (instancji klasy lub komponentu). Taki charakter mają przykładowe klasy Wizard i Enemy i jeśli tylko zechcemy, mogą posłużyć nam do zapełnienia sceny wieloma czarnoksiężnikami i wrogami. I tak też się często dzieje. Inaczej jednak wygląda sprawa z obiektem NotificationsManager, który z natury rzeczy jest jednostką centralną, a zatem
201
202
Rozdział 6
Obiekty, zależności i programowanie zdarzeniowe
powinien być jedynym przedstawicielem swojej klasy na scenie, a wszystkie inne obiekty gry powinny z nim współpracować jako z głównym elementem systemu przesyłania powiadomień o zdarzeniach. Praktyka też pokazuje, że jeden obiekt klasy NotificationsManager w scenie całkowicie wystarcza i nie ma żadnych powodów, aby tworzyć kolejne jej instancje. A skoro tak, to moglibyśmy po prostu przyjąć ten fakt do wiadomości i przejść do spraw bardziej interesujących. Jest jednak pewna kwestia, która wymaga nieco głębszej analizy. Wróć na chwilę do listingu 6.1 i przyjrzyj się następującym składnikom klasy NotificationsManager: polu Instance (instancja) i metodzie Awake (budzenie). Zobacz także w listingach 6.8 i 6.9, jak klasy Enemy i Wizard porozumiewają się z klasą NotificationsManager, stosując konwencję NotificationsManager.Instance. Obiekt NotificationsManager został zaimplementowany jako singleton, czyli należący do klasy niezezwalającej na więcej niż jedną instancję. Singleton to obiekt, który nie jest jedynym przedstawicielem swojej klasy przez przypadek — bo akurat więcej nie potrzeba — lecz dlatego, że kolejnych instancji nie da się już utworzyć. Po prostu tak jest zaprogramowana klasa. W praktyce oznacza to, że każda inna klasa występująca gdziekolwiek w projekcie Unity może uzyskać dostęp do tej jedynej instancji klasy NotificationsManager właśnie za pomocą konstrukcji NotificationsManager.Instance. A zatem każda klasa ma pełny dostęp do systemu zarządzania powiadomieniami o zdarzeniach! Sławny pisarz Arthur C. Clark powiedział kiedyś: „Każda dostatecznie zaawansowana technologia jest jak magia”. Rzeczywiście, singletony mają w sobie coś z magii. Za ich pomocą można naprawdę skutecznie rozwiązywać problemy z zarządzaniem klasami.
Komunikaty a obiekty aktywne Jak już wspominałem, obiekt NotificationsManager wysyła powiadomienia do listenerów za pomocą funkcji SendMessage. Jednak żeby jej działanie było prawidłowe i skuteczne, muszą być spełnione pewne warunki. W Unity zarówno ta funkcja, jak i BroadcastMessage oraz SendMessageUpwards są skuteczne tylko w odniesieniu do obiektów aktywnych, czyli takich, u których pole activeInHierarchy (aktywny w hierarchii) ma wartość true (prawda). Wartość tę można zmieniać bezpośrednio w panelu Inspector przez włączanie lub wyłączanie opcji pokazanej na rysunku 6.6. To samo można zrobić za pomocą skryptu, używając funkcji SetActive (ustaw aktywny). (Więcej informacji na ten temat znajdziesz pod adresem: http://docs.unity3d.com/Documentation/ScriptReference/GameObject.SetActive.html). Gdy pole activeInHierarchy ma wartość false (fałsz), wówczas obiekt ignoruje wszystkie powiadomienia i zachowuje się tak, jakby żadne powiadomienie nigdy nie zostało wysłane. Dlatego jeśli chcesz, żeby dany obiekt właściwie współpracował z systemem zarządzania powiadomieniami, upewnij się, że jest aktywny.
Przemierzanie hierarchii obiektów
Rysunek 6.6. Przełączanie stanu aktywności obiektu Źródło: Unity Technologies.
Przemierzanie hierarchii obiektów Niezależnie od rozsyłania komunikatów do całych grup obiektów przydatna byłaby też możliwość wędrowania po całej hierarchii obiektów i modyfikowania jej za pośrednictwem poleceń skryptowych. Przykładowo: załóżmy, że gra zawiera obiekt magazynowy, w którym przechowywane są rozmaite przedmioty, takie jak kolby chemiczne, zbroje, karabiny czy napoje magiczne. Magazyn jest obiektem nadrzędnym, a umieszczone w nim przedmioty są jego obiektowymi potomkami. Sam proces umieszczania przedmiotów w magazynie i usuwania ich stamtąd powinien się odbywać dynamicznie, a więc musi być sterowany za pomocą skryptu. Listing 6.10 przedstawia fragment kodu odpowiedzialny za dodanie obiektu do magazynu. Listing 6.10. Przypisanie obiektowi nowego przodka //Przeszukaj scenę i znajdź obiekt o nazwie ObjectName. GameObject Child = GameObject.Find(ObjectName); //Jeśli nie ma takiego obiektu, zakończ procedurę. if(Child == null) return; //Zmień przodka.
Child.transform.parent = Parent.transform;
Z powyższego listingu wynika, że dane dotyczące przodka obiekt przechowuje w komponencie Transform, a konkretnie w polu transform.parent. Więcej informacji na temat komponentu Transform znajdziesz pod adresem: http://docs.unity3d.com/Documentation/ Script-Reference/Transform.html. Teraz rozważmy przypadek przetwarzania wszystkich potomków obiektu przez wykonanie pętli, w której każde przejście będzie poświęcone innemu potomkowi. Przykład
203
204
Rozdział 6
Obiekty, zależności i programowanie zdarzeniowe
takiej pętli zawiera listing 6.11. Od każdego potomka jest tam pobierany komponent Transform, ale w taki sam sposób można uzyskać dostęp do każdego innego komponentu. Listing 6.11. Pobieranie komponentu Transform od każdego obiektu potomnego //Wykonaj pętlę po wszystkich obiektach potomnych. for(int i=0; i
GameObject ChildObj = Parent.transform.GetChild(i).gameObject; //Jeśli obiekt nie jest pusty… if(ChildObj == null) continue; //…pobierz od niego komponent Transform. Transform TransComp = ChildObj.GetComponent(); }
Podsumowanie Ten rozdział poświęciłem wyłącznie powiązaniom między obiektami, komunikatami i zdarzeniami. Przez wielu młodych i niedoświadczonych twórców gier zagadnienie to jest traktowane jako marginalne i niezasługujące na większą uwagę. Mam więc nadzieję, że udało mi się udowodnić, iż taki lekceważący stosunek do tych spraw jest całkowicie nieuzasadniony. Starałem się również pokazać, jak ważne z punktu widzenia poprawności działania systemu zarządzania powiadomieniami jest właściwe ustawienie hierarchii obiektów. Szczegółowo omówiłem implementację singletonowej klasy Notifications Manager, która dopuszcza istnienie tylko jednej swojej instancji. Użycie tej struktury pozwala każdej klasie istniejącej w ramach projektu gry uzyskać dostęp do systemu zarządzającego przepływem informacji o zdarzeniach i przekazywać mu takie informacje bądź je odbierać.
R OZ DZ I AŁ 7
R ETOPOLOGIZACJA
Upraszczaj, upraszczaj. — Henry David Thoreau Po przeczytaniu tego rozdziału powinieneś: ■ wiedzieć, czym jest retopologizacja i dlaczego należy jej używać; ■ umieć posługiwać się blenderowymi narzędziami rzeźbiarskimi; ■ wiedzieć, na czym polega przyciąganie do powierzchni i jak działa modyfikator Shrinkwrap; ■ umieć ręcznie zretopologizować siatkę; ■ umieć zmniejszyć szczegółowość siatki za pomocą modyfikatora Decimate. W początkowym okresie rozwoju grafiki 3D i gier wideo większość siatek była modelowana popularną metodą pudełkową (box-modeling). Nazwa wzięła się stąd, że artyści niemal zawsze zaczynają modelowanie od standardowej siatki w kształcie prostopadłościanu, podobnie jak rzeźbiarze rozpoczynają pracę od bezkształtnego kawałka drewna, kamienia czy gliny. Z taką siatką artysta może zrobić niemal wszystko. Może jej nadać dowolny kształt przez wytłaczanie ścianek, rozcinanie ich, zagęszczanie, fazowanie, a także przez bezpośrednie manipulowanie poszczególnymi krawędziami i wierzchołkami. Modelowanie pudełkowe jest opisane w większości książek poświęconych podstawom Blendera, więc nie będę go objaśniał. Ostatnio jednakże metoda ta stała się częścią bardziej rozbudowanego toku pracy zawierającego również rzeźbienie siatki i modelowanie wysokorozdzielcze (high-poly modeling). Co te pojęcia oznaczają? Co ważnego wnoszą do tworzenia modeli w Blenderze? Jaki jest ich wpływ na eksportowanie modeli do silnika Unity? Na te i inne pytania postaram się odpowiedzieć w pozostałej części rozdziału. Ważne z punktu widzenia tych odpowiedzi okaże się również pojęcie retopologizacji.
205
206
Rozdział 7
Retopologizacja
Siatki high-poly, czyli o dużej gęstości Rozdzielczość, lub inaczej: szczegółowość siatki jest określana liczbą wierzchołków. Z technicznego punktu widzenia im więcej wierzchołków ma siatka, tym jest bardziej szczegółowa, ponieważ każdy wierzchołek reprezentuje punkt kontrolny na powierzchni siatki, za pomocą którego można kształtować szczegóły. Zazwyczaj, gdy siatka ma być importowana do silnika gry, powinna mieć tak mało wierzchołków, jak to tylko możliwe, żeby jej przetwarzanie nie zajmowało zbyt wiele czasu. (Oczywiście zalecenie to należy stosować z umiarem, aby nie przesadzić z nadmiernym uproszczeniem siatki). Nie oznacza to jednak, że o ograniczaniu liczby wierzchołków trzeba myśleć przez cały czas modelowania siatki. Wielu artystów postępuje wręcz odwrotnie — najpierw tworzą model o małej gęstości (low-poly), a potem w miarę jego uszczegółowiania zwiększają rozdzielczość siatki, stosując dodatkowe podziały ścianek i krawędzi. W rezultacie otrzymują siatkę wysokorozdzielczą — znaną też jako subdivision surface (powierzchnia wielokrotnie dzielona) — rysunek 7.1. Na rysunku 7.2 pokazane jest, jak przez wielokrotne dzielenie zagęścić siatkę zwykłego sześcianu (A) do poziomu umożliwiającego wymodelowanie mniejszych szczegółów (B).
Rysunek 7.1. Wysokorozdzielcza siatka głowy. Siatki tego typu mają zagęszczoną powierzchnię, w której można łatwo wyrzeźbić niezbędne szczegóły Źródło: Blender.
Siatki high-poly, czyli o dużej gęstości
Rysunek 7.2. Siatki o małej rozdzielczości można zagęszczać przez wielokrotny podział ich powierzchni Źródło: Blender.
Głównym celem dzielenia powierzchni siatki jest zwiększenie liczby jej wierzchołków, bo umożliwia to modelowanie drobnych szczegółów. Szczegóły te to zazwyczaj zmarszczki, szpary, fałdy tkaniny, chropowatość powierzchni, wybrzuszenia, pęknięcia, włosy, włókna itp. Zagęszczanie powierzchni nie pozostaje jednak bez wpływu na przebieg pracy artysty. Zmniejsza się bowiem przydatność metod modelowania pudełkowego, które wymagają od artysty bezpośredniego manipulowania poszczególnymi wierzchołkami i ściankami. Przy dużych gęstościach siatki modelowanie na poziomie wierzchołkowym byłoby niezwykle żmudne i czasochłonne. Znacznie lepszym rozwiązaniem jest rzeźbienie modelu za pomocą specjalnych narzędzi, które swym działaniem przypominają rzeczywiste narzędzia rzeźbiarskie. Wiele z nich omówiłem już w rozdziale 4. przy okazji tworzenia terenów (rysunek 7.3).
Rysunek 7.3. Blenderowe narzędzia rzeźbiarskie umożliwiają modelowanie wysokorozdzielczych siatek Źródło: Blender.
207
208
Rozdział 7
Retopologizacja
Krótko mówiąc: siatki o dużych rozdzielczościach otwierają przed artystą niezwykle bogaty świat szczegółów. Dysponując mocnym sprzętem i wydajnym oprogramowaniem, może on teraz formować takie detale, jakie dawniej mógł jedynie symulować za pomocą tekstur. Niestety, każdy kij ma dwa końce. Większa szczegółowość siatek staje się wielkim problemem logistycznym i właśnie tej kwestii poświęcam następny podrozdział.
Siatki wysokorozdzielcze a gry czasu rzeczywistego Największym problemem szczegółowych siatek tworzonych na potrzeby gier jest ich wysoka rozdzielczość. Podczas rzeźbienia w Blenderze renderują się one z akceptowalną szybkością, ale zupełnie inaczej wygląda sytuacja po przeniesieniu ich do Unity, gdzie są renderowane wraz z kompletem tekstur i z wieloma innymi siatkami, a wszystko musi się odbywać w czasie rzeczywistym. Siatki o dużych rozdzielczościach mogą powodować poważne problemy wydajnościowe objawiające się chwilowym lub nawet całkowitym zawieszeniem gry — i niekoniecznie tylko na urządzeniach mobilnych, ale również na komputerach stacjonarnych i konsolach. Popularny sprzęt używany do gier nie jest jeszcze przystosowany do szybkiego renderowania takich siatek. W przyszłości pewnie się to zmieni, ale na razie trzeba się liczyć z tego typu ograniczeniem. Rodzi się więc pytanie: czy tworzenie wysokorozdzielczych siatek w ogóle ma sens, skoro nie dają się płynnie renderować w czasie rzeczywistym? Czy zajmowanie się nimi nie jest tylko marnowaniem czasu? Odpowiedzi na te pytania nie są tak proste, jak mogłoby się wydawać. Artysta może bowiem zastosować technikę mapowania normalnych (normal mapping) polegającą na wyrzeźbieniu szczegółowego modelu o dużej rozdzielczości, a następnie wypaleniu (bake) jego powierzchni w formie obrazu zwanego mapą normalnych (normal map). Mając taką mapę, może ją potem zrzutować na uproszczoną (odpowiednią dla gry) wersję siatki, aby symulować istnienie tych wszystkich szczegółów, które wcześniej wyrzeźbił w siatce wysokorozdzielczej. Użyta właściwie metoda ta pozwala nadać siatce low-poly wygląd siatki high-poly. Innymi słowy: siatka taka wygląda na bardziej szczegółową niż jest w rzeczywistości. Aby wyprodukować obie siatki — pierwotną wysokorozdzielczą oraz uproszczoną, o mniejszej rozdzielczości, która będzie eksportowana do silnika gry wraz z mapą normalnych — artyści zazwyczaj stosują bardzo specyficzny tok pracy. Najpierw tworzą siatkę o dużej rozdzielczości i kształtują ją za pomocą narzędzi rzeźbiarskich. Następnie generują jej wersję uproszczoną; robią to ręcznie lub automatycznie. Proces generowania takiej wersji low-poly jest określany jako retopologizacja (retopologizing). Artysta musi tu często stosować techniki modelowania pudełkowego, aby w sposób właściwy zbudować nową topologię będącą dobrym przybliżeniem oryginału.
Retopologizacja w praktyce
Retopologizacja w praktyce Pozostałą część rozdziału poświęcimy na analizowanie toku pracy związanego z retopologizacją, czyli takiego, który po wymodelowaniu siatki szczegółowej ma doprowadzić do utworzenia siatki uproszczonej (łatwej do przetwarzania) współgrającej z mapą normalnych. Pliki ćwiczeniowe znajdują się w folderze Rozdział07, a poszczególne czynności opisuję krok po kroku w kolejnych punktach. Na rysunku 7.4 pokazany jest model postaci w obu wersjach: wysokorozdzielczej i uproszczonej.
Rysunek 7.4. Model szczegółowy (po lewej) i jego wersja uproszczona (po prawej) Źródło: Blender.
Etap 1. Wymodeluj metodą pudełkową wstępną wersję obiektu Gdy zamierzasz wymodelować szczegółową siatkę postaci, samochodu, broni, organicznej struktury czy jeszcze czegoś innego, zazwyczaj rozpoczynasz od zgrubnego uformowania siatki o niskiej rozdzielczości. Zwykle też robisz to metodami modelowania pudełkowego, czyli przez wytłaczanie ścianek i manipulowanie nimi. W ten sposób tworzysz podstawową formę i strukturę przyszłego modelu. Na razie nie ma on jeszcze żadnych szczegółów, stanowi tylko ogólny zarys tego, co ma dopiero powstać. Na tym etapie siatka jest zdecydowanie low-poly, a nie high-poly. A skoro tak, to możesz stosować niemal wszystkie dostępne narzędzia przeznaczone do modelowania pudełkowego. Blender udostępnia je w trybie Edit (edycja) za pośrednictwem menu Mesh (siatka) — rysunek 7.5.
209
210
Rozdział 7
Retopologizacja
Rysunek 7.5. Zacznij od utworzenia bazowej siatki o niskiej rozdzielczości. Uformuj ją wstępnie metodą modelowania pudełkowego. Odpowiednie narzędzia znajdziesz w menu Mesh po włączeniu trybu edycji Źródło: Blender.
Uwa ga
W przypadku modeli z jedną płaszczyzną symetrii (np. modele humanoidalne) zazwyczaj wystarczy zbudować tylko jedną ich stronę (połowę). Potem można utworzyć jej lustrzane odbicie za pomocą modyfikatora Mirror (lustro). Więcej informacji na ten temat znajdziesz pod adresem: http://wiki.blender. org/index.php/Doc:2.6/Manual/Modifiers/Generate/Mirror. Siatka bazowa (taka jak na rysunku 7.5) stanowi punkt wyjścia do dalszych prac. Wkrótce podzielisz jej ścianki na mniejsze (zagęścisz siatkę), ale bez naruszania ogólnego kształtu i struktury. W ten sposób przygotujesz ją do kolejnego etapu prac, który będzie polegał na rzeźbieniu szczegółów. Gdy już zaczniesz pracować z tak zagęszczoną siatką, pamiętaj o następujących sprawach: ■ Stosuj tylko czworokąty (i trójkąty, ale tylko tam, gdzie to jest absolutnie konieczne). Innymi słowy: dąż do tego, aby model składał się wyłącznie ze ścianek o czterech krawędziach. Trójkątne zostawiaj tylko tam, gdzie czworokątne być nie mogą. Ścianki trójkątne lubią tworzyć ostre kanty i niepożądane załamania. Unikaj też wielokątów o większej liczbie boków, ponieważ mogą być przyczyną problemów przy dzieleniu ich na mniejsze kawałki podczas zagęszczania siatki (rysunek 7.6).
Retopologizacja w praktyce
Rysunek 7.6. Siatkę bazową utwórz z czworokątów. Trójkąty stosuj tylko tam, gdzie jest to nieuniknione. W ogóle nie używaj ścianek o liczbie boków większej niż cztery Źródło: Blender.
■ Znormalizuj wymiary czworokątów. Staraj się utrzymać rozmiary wszystkich ścianek na mniej więcej tym samym poziomie. W procesie zagęszczania siatki każda ścianka zostanie podzielona na równej wielkości fragmenty, czyli mniejsze ścianki. Jeśli w siatce podstawowej wszystkie ścianki mają zbliżone rozmiary, to skutki zagęszczania (gdy to nastąpi) będą przewidywalne i jednakowe na całej powierzchni siatki. Jeśli natomiast niektóre ścianki będą znacząco większe od pozostałych — np. na ramionach będą dłuższe niż na torsie — to zagęszczenie siatki stanie się nierównomierne i jedne obszary mogą być bardziej szczegółowe niż inne (rysunek 7.7).
Rysunek 7.7. Zadbaj, aby wszystkie ścianki miały podobne rozmiary. Duża dokładność nie jest potrzebna Źródło: Blender.
211
212
Rozdział 7
Retopologizacja
■ Wyznacz charakterystyczne linie sylwetki. Określ położenie ważnych linii modelu, takich jak talia, linia kolan, szyi, klatki piersiowej, barków, stawów łokciowych, oczu, nosa itp. Następnie dopasuj geometrię siatki do tych linii. W praktyce polega to na ułożeniu krawędzi w sposób ułatwiający późniejsze uwydatnienie tych linii oraz animowanie modelu bez zbytniego deformowania siatki w miejscach przegubowych, takich jak kolana czy łokcie (rysunek 7.8).
Rysunek 7.8. Ułóż krawędzie wzdłuż ważnych linii modelu Źródło: Blender.
Etap 2. Zwiększ rozdzielczość siatki Po wykonaniu siatki bazowej trzeba ją zagęścić, aby możliwe było rzeźbienie szczegółów. Stopień zagęszczenia powinien być odpowiedni do rozmiarów detali, które zamierzasz uformować. Jeśli będzie za mały, siatka zyska wygląd kanciasty zamiast gładkiego, organicznego. Przed rozpoczęciem dzielenia ścianek na mniejsze warto też zatwierdzić wszystkie modyfikatory użyte podczas wstępnego modelowania. Szczególnie trzeba pamiętać o modyfikatorze Mirror (pod warunkiem, że był użyty), bo jeśli pozostanie niezatwierdzony, może pozostawić po sobie ślad w postaci wyraźnego szwu wzdłuż przecięcia siatki z płaszczyzną symetrii. Zatwierdzenie go umożliwi algorytmom wygładzającym działanie na całej powierzchni modelu, a nie tylko na jego połówkach. Poza tym w trakcie
Retopologizacja w praktyce
rzeźbienia będzie możliwe formowanie szczegółów w sposób asymetryczny, co z kolei pozwoli w pewien sposób zróżnicować obie strony modelu. Aby zatwierdzić modyfikator Mirror, po prostu kliknij przycisk Apply (zastosuj) w panelu Properties (właściwości) — rysunek 7.9.
Rysunek 7.9. Zatwierdzanie modyfikatora Mirror Źródło: Blender.
W Blenderze jest kilka sposobów na zwiększenie rozdzielczości siatki. Chyba najprościej jest zaznaczyć wszystkie ścianki, a następnie wybrać polecenie Mesh/Edges/Subdivide (siatka/krawędzie/podziel). Metoda ta nie powoduje jednak żadnego wygładzania siatki; zwiększa liczbę jej ścianek, ale nie zmienia jej kształtu ani formy. W pewnych sytuacjach może to być korzystne, np. przy modelowaniu samochodu, ale gdy trzeba wymodelować jakieś struktury organiczne, takie jak kwiaty, ludzie czy grzyby, wygładzanie będzie tak samo przydatne jak zagęszczanie (rysunek 7.10). Innym sposobem zwiększenia rozdzielczości siatki — tym razem w połączeniu z wygładzaniem — jest zastosowanie modyfikatora Subdivision Surface (rysunek 7.11). Modyfikator ten nie tylko dzieli ścianki siatki, ale również wygładza jej powierzchnię. Siłę jego działania można regulować za pomocą parametrów View (widok) i Render (renderowanie).
213
214
Rozdział 7
Retopologizacja
Rysunek 7.10. Polecenie Subdivide zachowuje kształt siatki, ale zwiększa liczbę jej ścianek Źródło: Blender.
Pierwszy z nich określa stopień wygładzenia i podziału powierzchni modelu wyświetlanego w oknie widokowym, a drugi pozwala na określenie tego stopnia odrębnie dla procesu renderowania przy użyciu renderera Blender Render (będzie omawiany w rozdziale 9.) lub Cycles Renderer. Jeśli model jest tworzony na potrzeby gry, istotna jest tylko wartość parametru View, ponieważ docelowy rendering będzie wykonywany w Unity, a nie w Blenderze. Istotnym mankamentem tego modyfikatora jest to, że nie pozwala na stosowanie odrębnego poziomu podziału siatki dla trybu rzeźbienia. Poziom ten można ustalać wyłącznie w odniesieniu do podglądu w oknie widokowym i do renderowania. Dlatego dla procesu rzeźbienia lepszym rozwiązaniem jest to, które prezentuję poniżej. Najlepsza metoda zagęszczania i wygładzania siatki przeznaczonej do rzeźbienia polega na zastosowaniu modyfikatora Multiresolution (wielorozdzielczość) — rysunek 7.12. Aby go użyć, należy go wybrać z listy modyfikatorów na zakładce Modifiers panelu Properties, a następnie kliknąć przycisk Subdivide (podziel), co spowoduje zwiększenie stopnia podziału o jeden poziom. Wysokorozdzielcza siatka z rysunku 7.4 (ta po lewej) została zagęszczona do poziomu siódmego.
Retopologizacja w praktyce
Rysunek 7.11. Modyfikator Subdivision Surface Źródło: Blender.
Rysunek 7.12. Modyfikator Multiresolution z trzecim poziomem zagęszczenia siatki Źródło: Blender.
215
216
Rozdział 7
Retopologizacja
Im wyższy poziom zagęszczenia zastosujesz, tym drobniejsze szczegóły będziesz mógł wyrzeźbić. Za pomocą parametrów Preview (podgląd), Sculpt (rzeźbienie) i Render (renderowanie) możesz ustawić różne poziomy podziału siatki dla tych trzech trybów, co może być przydatne w sytuacji, gdy trzeba zmniejszyć rozdzielczość siatki w celu poprawienia wydajności komputera przy rzeźbieniu lub oglądaniu modelu. Kolejne poziomy zagęszczenia dodawaj ostrożnie, ponieważ wyższe poziomy mogą negatywnie wpływać na wydajność komputera i powodować opóźnienia w jego działaniu. Ustal eksperymentalnie, do jakiej wartości możesz się posunąć, i staraj się jej nie przekraczać.
Etap 3. Rzeźbij i dziel Po przypisaniu siatce modyfikatora Multiresolution możesz rozpocząć proces rzeźbienia. Ja zazwyczaj robię to etapami, stosując coraz to wyższe poziomy podziału. Maksymalna gęstość nie jest konieczna do wyrzeźbienia detali o stosunkowo dużych rozmiarach, a tylko niepotrzebnie obciążałaby komputer. Dlatego zwiększam poziom zagęszczenia dopiero wtedy, gdy tego naprawdę potrzebuję — gdy muszę wymodelować jeszcze mniejsze szczegóły. Przebieg takiego procesu wygląda następująco: 1. Wybierz narzędzie rzeźbiarskie. W tym celu musisz zaznaczyć model i włączyć tryb rzeźbienia (Sculpt Mode) — rysunek 7.13.
Rysunek 7.13. Po zaznaczeniu siatki włącz tryb rzeźbienia Źródło: Blender.
Retopologizacja w praktyce
2. Włącz pierwszy poziom zagęszczenia siatki — w rolecie z ustawieniami modyfikatora Multiresolution kliknij przycisk Subdivide. 3. Wyrzeźbij detale, których rozmiary są odpowiednie dla aktualnej rozdzielczości siatki. 4. Ponownie kliknij przycisk Subdivide, aby włączyć kolejny poziom zagęszczenia, i wyrzeźbij nieco mniejsze detale. 5. Powtarzaj ten cykl rzeźbienia, zagęszczania i znowu rzeźbienia, aż dojdziesz do maksymalnego poziomu gęstości i szczegółowości modelu (rysunek 7.14).
Rysunek 7.14. Przeplataj rzeźbienie z zagęszczaniem siatki Źródło: Blender.
Podczas rzeźbienia pamiętaj o następujących kwestiach: ■ Jeśli Cię stać, zafunduj sobie tablet graficzny i używaj go do rzeźbienia (zamiast myszy). Tablet, dzięki temu że reaguje na nacisk piórka, pozwala bardziej precyzować działanie narzędzi rzeźbiarskich. W lewym panelu z ustawieniami narzędzia możesz ustalić, które parametry mają być uzależnione od nacisku, a które nie (rysunek 7.15). W tym celu musisz włączyć opcję Enable tablet pressure sensitivity for… (włącz wrażliwość tabletu na nacisk dla…) widoczną na prawo od pola danego parametru.
217
218
Rozdział 7
Retopologizacja
Rysunek 7.15. Włącz wrażliwość tabletu na nacisk piórka podczas rzeźbienia Źródło: Blender.
■ Stosuj lustrzane odbicie przy rzeźbieniu detali symetrycznych. Jeśli musisz wyrzeźbić identyczne szczegóły po obu stronach modelu, włącz w sekcji Mirror (lustro) rolety Symmetry (symetria) odpowiednią oś odbicia lustrzanego. To, co wyrzeźbisz po jednej stronie, automatycznie pojawi się po drugiej stronie siatki. Właśnie w taki sposób zostały zdeformowane policzki postaci z rysunku 7.16. Stosowanie odbicia lustrzanego może zaoszczędzić dużo czasu, więc korzystaj z tej funkcji, gdy tylko pozwala na to symetryczność modelu.
Retopologizacja w praktyce
Rysunek 7.16. Przy rzeźbieniu symetrycznych detali stosuj lustrzane odbicie Źródło: Blender.
■ Wybieraj narzędzie odpowiednie do zadania, jakie masz wykonać. Ludzie często przywiązują się do jednego narzędzia i za jego pomocą usiłują robić wszystko, również to, o czym projektant narzędzia nawet nie pomyślał. Narzędzi rzeźbiarskich jest kilka i każde ma swoją specjalność (rysunek 7.17). Zapoznaj się z nimi wszystkimi. Brush (pędzel) i SculptDraw (rzeźbiące wyciąganie) są najlepsze w łagodnym wyciąganiu lub wpychaniu okrągłych fragmentów siatki, więc można ich używać do modelowania gładkich szczegółów organicznych, czyli wszelkiego rodzaju guzów, wybrzuszeń, zagłębień i szczelin. To prawda, że za pomocą tych narzędzi można wymodelować również mnóstwo innych rzeczy, ale czasami ich użycie staje się po prostu niewygodne. Przykładowo: przy rzeźbieniu fałdów, kantów, pęknięć czy zmarszczek znacznie lepiej spisują się narzędzia Crease (kant), Nudge (popychanie) i Pinch (szczypanie). Właśnie ich użyłem do wymodelowania fałdek skóry wokół ust i nosa na twarzy przykładowej postaci.
219
220
Rozdział 7
Retopologizacja
Rysunek 7.17. Używaj wszystkich dostępnych pędzli Źródło: Blender.
■ Używaj parametru Texture (tekstura). Rzeźbienie z użyciem tekstury jako elementu sterującego działaniem pędzla jest doskonałym sposobem na modelowanie porów skóry, włosów, tkanin, zmarszczek na wargach i palcach oraz wielu innych tego typu szczegółów. Po prostu wybierz właściwą teksturę, a ona zmusi pędzel do zróżnicowania siły działania w zależności od zapisanych w niej wartości kolorów. Aby z tego skorzystać, musisz najpierw przygotować odpowiednią teksturę. Może to być tekstura proceduralna, np. Clouds (chmury), ale może też być wczytana z pliku. Wszystko to ustalisz na zakładce Texture panelu Properties. Wymagany rodzaj tekstury wybierz z listy rozwijanej Type (typ). Gdy już ustalisz wszystkie właściwości tekstury, wskaż ją w próbniku dostępnym po kliknięciu okna podglądu w rolecie Texture przybornika (rysunek 7.18). Ja wykorzystałem teksturę chmurową do wyrzeźbienia łuków brwiowych.
Retopologizacja w praktyce
Rysunek 7.18. Do rzeźbienia tak drobnych szczegółów jak brwi użyj pędzla z odpowiednią teksturą Źródło: Blender.
Etap 4. Retopologizuj Po zakończeniu rzeźbienia wysokorozdzielczego modelu możesz przystąpić do jego retopologizacji, czyli tworzenia wersji uproszczonej, z siatką o mniejszej gęstości. Blender umożliwia przeprowadzenie tej operacji na wiele sposobów, oferując zarówno własne narzędzia, jak i z komercyjnych dodatków. Ja skoncentruję się na dwóch metodach ręcznego retopologizowania na podstawie narzędzi blenderowych. Są to metody z pewnością żmudniejsze niż te oparte na bardziej zautomatyzowanych narzędziach (takich jak Contours Retopology), ale gdy raz poznasz metodę ręczną, będziesz mógł retopologizować siatki niezależnie od tego, czy masz dostęp do komercyjnych narzędzi, czy nie. Jest takie znane powiedzenie: „Dasz biedakowi rybę, to będzie syty przez jeden dzień, ale jeśli nauczysz go łowić ryby, będzie syty do końca życia”. Ja chcę Cię nauczyć łowienia ryb i dlatego opiszę najpierw metodę z przyciąganiem do powierzchni, a potem z modyfikatorem Shrinkwrap (owijanie).
Retopologizowanie z przyciąganiem do powierzchni Metoda z przyciąganiem do powierzchni pozwala utworzyć siatkę low-poly stopniowo, ścianka po ściance. Zacznij od włączenia widoku lokalnego (Local), czyli takiego, w którym wyświetlana będzie tylko siatka zaznaczona (pozostałe będą niewidoczne), aby móc pracować szybciej, gdyż Blender będzie miał mniej ścianek do wyrenderowania. W tym celu zaznacz właściwą siatkę i wciśnij klawisze Shift+I (jeśli masz włączony tryb sterowania z programu Maya) lub wybierz polecenie View/View Global/Local (widok/widok globalny/lokalny) — rysunek 7.19. W razie potrzeby możesz pójść dalej i jeszcze bardziej ułatwić pracę sobie oraz Blenderowi. Możesz mianowicie włączyć tryb edycji, zaznaczyć niepotrzebne w danym momencie ścianki i ukryć je. Aby ukryć zaznaczone ścianki, wciśnij klawisze Ctrl+H
221
222
Rozdział 7
Retopologizacja
Rysunek 7.19. W widoku lokalnym wyświetlana jest tylko siatka zaznaczona Źródło: Blender.
lub wybierz polecenie Mesh/Show/Hide/Hide Selected (siatka/pokaż/ukryj/ukryj zaznaczone). Aby przywrócić widoczność ukrytych ścianek, wciśnij klawisze Shift+Ctrl+H lub wybierz Mesh/Show/Hide/Show Hidden (siatka/pokaż/ukryj/pokaż ukryte) — rysunek 7.20. Chodzi o to, żeby widoczny był tylko ten fragment siatki, który właśnie zamierzasz zretopologizować. W s k a z ów ka
Możesz oczywiście retopologizować siatkę w całości, ale łatwiej jest robić to etapami. Po zretopologizowaniu jednej części przechodź do następnej itd. Za każdym razem ukrywaj te fragmenty siatki, którymi nie będziesz się zajmował. Tak pracuje się znacznie łatwiej. Po tych wstępnych przygotowaniach zacznij tworzyć nową, uproszczoną siatkę. Wykonaj następujące czynności: 1. Włącz widok siatki od przodu. 2. Wstaw do sceny nowy obiekt płaszczyzny. W tym celu kliknij przycisk Plane (płaszczyzna) w przyborniku po lewej stronie interfejsu (rysunek 7.21) lub wybierz z górnego menu polecenie Add/Mesh/Plane (dodaj/siatkę/płaszczyzna) — jeśli używasz Blendera w wersji starszej niż 2.7.
Retopologizacja w praktyce
Rysunek 7.20. Przywracanie widoczności ukrytych ścianek Źródło: Blender.
Rysunek 7.21. Utwórz płaszczyznę jako pierwszy wielokąt siatki low-poly Źródło: Blender.
223
224
Rozdział 7
Retopologizacja
3. W ustawieniach tworzonej płaszczyzny zaznacz pole Align to View (wyrównaj do widoku), aby ustawić ją równolegle do powierzchni okna widokowego. 4. Jeśli jest to potrzebne, zmniejsz lub zwiększ wymiary płaszczyzny. 5. Ustaw płaszczyznę w dogodnym położeniu przed wysokorozdzielczą siatką. Nie obracaj jej. Płaszczyzna jest pierwszym wielokątem siatki niskorozdzielczej. 6. Płaszczyzna będzie przedmiotem wielu zabiegów, więc powinna być dobrze widoczna na tle siatki wysokorozdzielczej. Aby poprawić widoczność płaszczyzny, zaznacz ją, po czym w panelu Properties (właściwości) otwórz zakładkę Object (obiekt) i w rolecie Display (wyświetlanie) włącz następujące opcje:
X-Ray (rentgen) — spowoduje, że płaszczyzna będzie zawsze widoczna przed siatką; Wire (kontury); Draw All Edges (rysuj wszystkie krawędzie). Dodatkowo z listy rozwijanej Maximum Draw Type (maksymalny typ rysunku) wybierz Wire, aby płaszczyzna była renderowana w trybie konturowym. Umożliwi to rysowanie konturowej struktury siatki niskorozdzielczej wokół siatki retopologizowanej (rysunek 7.22).
Rysunek 7.22. Popraw widoczność siatki niskorozdzielczej Źródło: Blender.
W ramach przygotowań do retopologizacji musisz jeszcze uaktywnić tryb przyciągania do powierzchni, aby za każdym razem, gdy przesuniesz wierzchołek siatki low-poly, był on przyciągany — przysuwany automatycznie — do najbliższego punktu powierzchni
Retopologizacja w praktyce
siatki high-poly. Umożliwi Ci to budowanie ścianka po ściance nowej siatki, która będzie automatycznie dopasowywana do trójwymiarowych kształtów modelu wysokorozdzielczego, a więc na pewno będzie dobrym jego przybliżeniem. Aby uaktywnić przyciąganie do powierzchni, wykonaj następujące czynności: 1. Włącz funkcję przyciągania (snapping). W tym celu kliknij ikonę w kształcie magnesu znajdującą się na pasku narzędziowym okna 3D View (widok 3D). 2. Rozwiń listę Snap Element (element przyciągający) i jako element przyciągający wybierz ściankę (Face), ponieważ siatka low-poly ma być dopasowana do ścianek siatki high-poly. 3. Aby mieć pewność, że przyciągany będzie element aktywny, czyli zaznaczony, wybierz z listy rozwijanej Snap Target (cel przyciągania) opcję Active (aktywny). 4. Włącz funkcję Project individual elements on the surface of other objects (rzutowanie poszczególnych elementów na powierzchnię innych obiektów). Dzięki temu przyciąganie będzie mogło funkcjonować między zupełnie odrębnymi siatkami, a więc wierzchołki siatki low-poly będą przyciągane do ścianek siatki high-poly. Pełna konfiguracja funkcji przyciągania jest pokazana na rysunku 7.23.
Rysunek 7.23. Uaktywnienie przyciągania do powierzchni wymaga ustawienia kilku różnych opcji Źródło: Blender.
W końcu, gdy już wszystko jest gotowe do retopologizacji, wykonaj następującą procedurę: 1. Przy zaznaczonej siatce płaszczyzny włącz tryb edycji (Edit Mode). 2. W ortogonalnym widoku od przodu ustaw wierzchołki płaszczyzny w ważnych punktach siatki high-poly. Staraj się zachować kształt i kontury oryginału, układając pętle krawędziowe tam, gdzie prawdopodobnie byłyby, gdybyś od początku modelował całą siatkę metodą pudełkową. Przesuwane przez Ciebie wierzchołki będą automatycznie przyciągane do powierzchni oryginału. Kontynuuj ten proces, dodając kolejne wielokąty przez wytłaczanie (Extrude) istniejących (rysunek 7.24).
225
226
Rozdział 7
Retopologizacja
Rysunek 7.24. Wymodeluj siatkę niskorozdzielczą, stosując techniki znane z metody pudełkowej Źródło: Blender.
3. Po ustawieniu kilku ścianek obejrzyj model pod różnymi kątami w widoku perspektywicznym. Zobacz, jak dzięki przyciąganiu do powierzchni nowe ścianki dopasowały się do kształtu siatki pierwotnej (rysunek 7.25).
Rysunek 7.25. Przyciąganie do powierzchni skutecznie pomaga w dopasowywaniu nowej geometrii do kształtu siatki wysokorozdzielczej Źródło: Blender.
Retopologizacja w praktyce
4. Postępuj tak aż do wygenerowania całej siatki niskorozdzielczej (rysunek 7.26). Ostrzegam: ręczna retopologizacja jest czasochłonna. Gdy w grę wchodzi model postaci ludzkiej, proces ten może zająć kilka godzin.
Rysunek 7.26. Siatka low-poly wygenerowana z użyciem funkcji przyciągania dobrze przylega do modelu wysokorozdzielczego Źródło: Blender.
Uwa ga
Więcej informacji na temat funkcji przyciągania do siatek znajdziesz w dokumentacji Blendera na stronie: http://wiki.blender.org/index.php/Doc:2.6/Manual/3D_interaction/Transform_Control/Snap_to_Mesh.
Retopologizowanie z użyciem modyfikatora Shrinkwrap Metoda ta jest rzadziej używana, bo nie jest tak uniwersalna jak poprzednia, ale za to znakomicie sprawdza się, gdy trzeba szybko zretopologizować siatkę lub jej fragment o kształcie symetrycznym, wydłużonym i najlepiej cylindrycznym, np.: nogę, rękę czy nawet tors. Gorzej wypada, gdy siatka jest niesymetryczna i nieliniowa, jak w przypadku ucha, dłoni czy fryzury. Nic jednak nie stoi na przeszkodzie, aby stosować ją tam, gdzie szybko daje dobre rezultaty, a w trudniejszych obszarach sięgać po metodę opartą na przyciąganiu do powierzchni. Żeby poznać bliżej modyfikator Shrinkwrap (owijanie), rozważmy przypadek retopologizacji siatki wysokorozdzielczej samych nóg modelu humanoidalnego (rysunek 7.27).
227
228
Rozdział 7
Retopologizacja
Rysunek 7.27. Modyfikator Shrinkwrap przydaje się do szybkiego retopologizowania długich, cylindrycznych siatek Źródło: Blender.
Oczywiście można by te nogi zretopologizować również omówioną wcześniej metodą przyciągania do powierzchni, ale modyfikator Shrinkwrap pozwoli nam zaoszczędzić trochę czasu. Oto procedura jego użycia: 1. Wykonaj cylindryczną siatkę, aby aproksymować wymodelowaną nogę. Zacznij od utworzenia okręgu — w przyborniku otwórz panel Create (tworzenie) i kliknij przycisk Circle (okrąg). Korzystając z widoków od przodu i z boku, ustaw okrąg na wysokości kostki lewej bądź prawej nogi (rysunek 7.28). 2. Włącz tryb edycji. Następnie zaznacz wszystkie krawędzie okręgu i wytłocz je w górę, tworząc pionowe ścianki (wzdłuż osi Z) jako powierzchnię boczną walca. Walec powinien być tak długi jak noga (rysunek 7.29). 3. Wstaw kilka dodatkowych pętli krawędziowych i rozstaw je w miejscach istotnych dla kształtu nogi. Z grubsza uformuj walec, ale nie trać czasu na dokładne modelowanie, ponieważ zrobi to za Ciebie modyfikator Shrinkwrap (rysunek 7.30).
Retopologizacja w praktyce
Rysunek 7.28. Aproksymowanie nogi rozpocznij od utworzenia okręgu Źródło: Blender.
Rysunek 7.29. Przez wytłoczenie krawędzi okręgu utwórz walec wysoki jak noga Źródło: Blender.
229
230
Rozdział 7
Retopologizacja
Rysunek 7.30. Z grubsza uformuj siatkę walca Źródło: Blender.
4. Wstaw jeszcze kilka pętli pomiędzy tymi, które utworzyłeś poprzednio, aby uzyskać odpowiednią rozdzielczość nowej siatki (rysunek 7.31).
Rysunek 7.31. Zwiększ nieco rozdzielczość nowej siatki przez wstawienie dodatkowych pętli krawędziowych Źródło: Blender.
Dziesiątkowanie
5. Teraz możesz użyć modyfikatora Shrinkwrap. Zaznacz więc cylindryczną siatkę nogi i na zakładce Modifiers panelu Properties wybierz modyfikator Shrinkwrap. 6. W polu Target (cel) ustaw nazwę wysokorozdzielczej nogi. Siatka niskorozdzielcza automatycznie się do niej dopasuje. Po prostu modyfikator przyciągnie jej wierzchołki do powierzchni siatki docelowej (rysunek 7.32).
Rysunek 7.32. Przez użycie modyfikatora Shrinkwrap do retopologizowania siatek cylindrycznych można zaoszczędzić sporo czasu Źródło: Blender.
7. Po zatwierdzeniu modyfikatora może się okazać, że niektóre wierzchołki wylądowały nie tam, gdzie powinny. W takim przypadku po prostu zastosuj standardową technikę przyciągania do powierzchni i ustaw te wierzchołki we właściwych miejscach.
Dziesiątkowanie Omówię jeszcze jedną metodę upraszczania siatek, szybką, ale mało dokładną. Polega ona na zastosowaniu modyfikatora Decimate (dziesiątkowanie). Chociaż nie jest on uznawany oficjalnie jako narzędzie retopologizacyjne, umożliwia osiągnięcie podobnego rezultatu, czyli uproszczonej siatki. Modyfikator Decimate ma charakter proceduralny i parametryczny, co oznacza, że jego działanie jest zależne w dużym stopniu od wpisanych przez Ciebie liczb. Na podstawie tych liczb redukuje szczegółowość siatki i zmniejsza liczbę jej ścianek. Określam tę metodę jako „szybką i niedokładną”, ponieważ jest rzeczywiście szybka, ale tylko w nie-
231
232
Rozdział 7
Retopologizacja
wielkim stopniu pozwala na sterowanie procesem tworzenia nowej topologii siatki. Czasami daje rezultat, o jaki chodziło, a czasami nie. Ty tylko wpisujesz liczbę, a resztę załatwia Blender. Może Ci się spodoba, a może nie! Procedura użycia modyfikatora Decimate wygląda następująco: 1. Zaznacz siatkę i na zakładce Modifiers panelu Properties wybierz modyfikator Decimate (rysunek 7.33).
Rysunek 7.33. Dziesiątkowanie to szybka metoda na zmniejszenie szczegółowości siatki. Tutaj zastosowano je w odniesieniu do głowy postaci Źródło: Blender.
2. Za pomocą parametru Ratio (stopień) ustal, w jakim stopniu siatka ma być zdziesiątkowana. Przy wartości 1 nic się nie zmieni. Gdy ustawisz 0, siatka w zasadzie zniknie. A zatem zwykle będzie to wartość większa od 0 i mniejsza od 1. Im mniejszą wpiszesz, tym mniej zostanie ścianek i szczegółów siatki. Cała sztuka polega na tym, by parametr Ratio miał jak najmniejszą wartość, ale żeby siatka zachowała przy tym należyty wygląd (rysunek 7.34).
Jako ciekawostkę podam, że w przypadku modelu z rysunku 7.34 modyfikator Decimate wykonał kawał roboty, zmniejszając liczbę ścianek głowy z ponad miliona do zaledwie kilku tysięcy. Jednakże gdy już zatwierdziłem modyfikator i w trybie edycyjnym zacząłem sprawdzać topologię nowej siatki, wyszły na jaw typowe dla tej metody problemy. Otóż modyfikator, dziesiątkując ścianki, nie zwracał uwagi na strukturę topologiczną nowej siatki i w zupełnie niepotrzebnych miejscach powstawiał ścianki trójkątne. Uzyskana w ten sposób siatka właściwie nie nadaje się do animacji (rysunek 7.35).
Dziesiątkowanie
Rysunek 7.34. Zmniejszanie szczegółowości siatki za pomocą modyfikatora Decimate Źródło: Blender.
Rysunek 7.35. Modyfikator Decimate zazwyczaj pozostawia po sobie wielki nieład. Zgrabne deformowanie takiej siatki jest raczej niemożliwe Źródło: Blender.
233
234
Rozdział 7
Retopologizacja
Podsumowanie W tym rozdziale omówiłem zagadnienie retopologizacji (czasami określanej krótko retopo). Celem tego procesu jest po prostu przekształcenie szczegółowej siatki wysokorozdzielczej w uproszczoną siatkę o niskiej rozdzielczości — taką, którą da się łatwo przenieść do silnika gry (np. Unity) i którą będzie można tam łatwo przetwarzać. Model wysokorozdzielczy jest potrzebny do wypalenia mapy normalnych, czyli specjalnej tekstury zawierającej informacje o detalach jego powierzchni. Później, podczas trwania gry, mapa ta będzie rzutowana na uproszczoną siatkę modelu w celu zasymulowania tych wszystkich szczegółów. Do map normalnych i ich wypalania wrócimy w rozdziale 9. W Blenderze stosuje się trzy podstawowe metody retopologizacji: z przyciąganiem do powierzchni, z modyfikatorem Shrinkwrap i z modyfikatorem Decimate. Najdokładniejszą i zarazem najbardziej elastyczną metodą jest ręczne budowanie nowej siatki z automatycznym przyciąganiem jej wierzchołków do ścianek siatki pierwotnej. Modyfikator Shrinkwrap może być przydatny, gdy siatka pierwotna jest długa i cylindryczna. Natomiast modyfikator Decimate nadaje się do retopologizowania wyłącznie siatek, które nie będą animowane i deformowane.
R OZ DZ I AŁ 8
Z APISYWANIE STANU GRY A TRWAŁOŚĆ DANYCH
Trudno sobie wyobrazić moc, jaką można posiąść, mając dostęp do tak wielu rodzajów danych. — Tim Berners-Lee Po przeczytaniu tego rozdziału powinieneś: ■ rozumieć pojęcie trwałości danych; ■ znać klasę PlayerPrefs; ■ mieć świadomość ograniczeń klasy PlayerPrefs; ■ wiedzieć, czym jest XML i na czym polega serializacja danych; ■ umieć tworzyć, zapisywać i wczytywać stany gry. Jednym z wielu zadań, jakie stoją przed każdym twórcą gier, jest umożliwienie graczowi zapisania bieżącego stanu gry, aby po przerwie mógł ją wznowić dokładnie od tego momentu, w którym ją przerwał. Chodzi o zapisanie tzw. danych trwałych (persistent data), które w stanie niezmienionym mogą być przechowywane w pamięci masowej i gdy zajdzie potrzeba, posłużą do odtworzenia wszystkich parametrów niezbędnych do wznowienia gry. Użytkownik, nawet gdy wyłącza komputer, powinien mieć pewność, że jeśli włączy go ponownie, dane te będą nadal dostępne. Pod tym względem są one przeciwieństwem danych ulotnych (np. bieżące położenie wskaźnika myszy), które są przechowywane w pamięci RAM i bezpowrotnie tracone z chwilą zakończenia gry lub wyłączenia komputera. W tym rozdziale obiektem naszych zainteresowań będą wyłącznie dane trwałe. Pokażę mianowicie, jak w programie Unity można zaprogramować ich tworzenie, zapisywanie i wczytywanie. Unity oferuje dwa podejścia do tego zagadnienia i oba zostaną opisane. 235
236
Rozdział 8
Zapisywanie stanu gry a trwałość danych
Uwa ga
W tym rozdziale skupimy się na Unity, nie na Blenderze. Przedmiotem rozważań będzie głównie pisanie w języku C# skryptów realizujących zadania tworzenia, zapisywania i wczytywania stanu gry.
Dane trwałe Niemal w każdej grze da się wyróżnić dwa podstawowe rodzaje danych trwałych. Są to: ■ Dane odnoszące się do gry. Są to wszystkie dane związane z elementami gry. Pozostają one niezmienne przez cały czas trwania gry i obejmują dane definiujące sceny, dźwięki, ruchy, animacje, siatki, tekstury itp. Dane te muszą istnieć zarówno w trakcie trwania gry, jak i po wyłączeniu komputera, a na dodatek nie mogą być w żaden sposób modyfikowane przez gracza. Są po prostu stałą częścią gry. ■ Dane odnoszące się do użytkownika. Należą do nich wszystkie dane wewnętrzne, które mogą się zmieniać w trakcie gry i których stan ma znaczenie dla jej przebiegu. Mają zatem dwie ważne cechy: zdolność do zmieniania się i powiązanie z przebiegiem gry. Dane odnoszące się do użytkownika ulegają zmianom, gdy gracz przenosi się w inny rejon sceny, gdy postać wroga rusza w pościg za postacią gracza lub gdy główny bohater otwiera drzwi i ich nie zamyka (zmienia stan drzwi z zamkniętych na otwarte). W takich i podobnych przypadkach dochodzi do zmiany — obiekt przechodzi z jednego stanu do innego. Jak już wspomniałem, drugą ważną cechą danych odnoszących się do użytkownika jest ich stopień powiązania z przebiegiem gry. Załóżmy przykładowo, że scena zawiera ptaka, który gdzieś w oddali leci sobie po wytyczonej ścieżce i jedyną jego rolą jest spotęgowanie wrażenia, że gra toczy się w środowisku naturalnym. Obiekt ten, chociaż ulega zmianom, nie ma jednak większego znaczenia dla samej istoty gry i tak naprawdę trudno jego stan zaliczyć do danych, które trzeba zapisywać. Gdy użytkownik będzie zapisywał stan gry, ptak może być rzeczywiście w innym położeniu niż początkowe, ale tak naprawdę nie ma ważnego powodu, by taką zmianę zachowywać. Po wznowieniu gry nie będzie przecież miało znaczenia, czy ptak wznowi swój lot z miejsca zapisanego, czy po prostu zacznie go od nowa. Jedynym rodzajem danych, które trzeba zapisywać jako stan gry, są te odnoszące się do użytkownika. Przy ustalaniu, co i jak należy zapisać, wystarczy, że skupisz się na określonego typu obiektach i wybranych mechanizmach zapisu. Warto zrobić to dość wcześnie, aby pod tym kątem zoptymalizować cały projekt gry. Nie od rzeczy jest myślenie o zapisywaniu stanu gry już na etapie przygotowań i wstępnego projektowania. Poświęć trochę czasu na przemyślenie tych kwestii i wcześniej ustal, które obiekty trzeba będzie brać pod uwagę przy zapisie stanu gry, a które można pominąć. Nie myśl, że będziesz mógł to zrobić na końcu, gdy gra jest już niemal gotowa.
Preferencje gracza
Preferencje gracza W Unity najszybszym i chyba najprostszym sposobem na zapisywanie danych trwałych, który można stosować na wszystkich platformach, jest posłużenie się klasą PlayerPrefs (skrót od player preferences — preferencje gracza). Zgodnie z nazwą jest ona przeznaczona do zapisywania pewnego rodzaju danych odnoszących się do użytkownika, a konkretnie preferencji gracza, czyli zdefiniowanych przez niego ustawień parametrów gry. A zatem jeśli potrzebujesz zapisać dane, takie jak rozdzielczość obrazu, stopień trudności, napisy, jasność, język, system sterowania, tryb okienkowy itp., możesz śmiało użyć klasy PlayerPrefs. Jedną z zalet klasy PlayerPrefs jest to, że do posługiwania się nią nie jest wcale potrzebna znajomość takich spraw, jak nazwy plików czy ścieżki systemowe. Można się przy tym skoncentrować wyłącznie na gromadzeniu informacji i zapisywaniu ich w odpowiednim momencie. Klasa ta działa jak baza danych, do której można wprowadzać dane i je stamtąd pobierać, ale wszystkie potrzebne do tego mechanizmy systemowe są uruchamiane wewnętrznie przez Unity. Zobaczmy na kilku przykładach, jak to wygląda w praktyce. Spójrz na listing 8.1 z kodem zapisu bieżącej rozdzielczości w bazie danych PlayerPrefs. Listing 8.1. Zapisywanie rozdzielczości przy użyciu klasy PlayerPrefs PlayerPrefs.SetInt("ScreenWidth",Screen.width); PlayerPrefs.SetInt("ScreenHeight",Screen.height); PlayerPrefs.Save();
Uwa ga
Metoda Save() klasy PlayerPrefs jest wywoływana automatycznie przy zamykaniu programu Unity. Natomiast w przypadku awarii lub przedwczesnego wyjścia z głównej pętli gry żadne dane nie są zapisywane. Dlatego warto ręcznie wywoływać tę metodę i zapisywać dane, gdy tylko taka potrzeba zachodzi. Klasa PlayerPrefs zawiera kilka metod służących do zapisywania danych. SetInt zapisuje wartości całkowite, SetFloat — wartości zmiennoprzecinkowe, a SetString — wartości tekstowe. W listingu 8.1 do zapisu wymiarów okna, czyli szerokości i wysokości, wyrażonych w pikselach użyto metody SetInt. (Zazwyczaj okno gry obejmuje cały ekran, ale nie zawsze tak musi być). Wszystkie wymienione wyżej metody zapisujące mają taką samą postać. Pierwszy argument określa unikatową nazwę zapisywanej wartości. W listingu 8.1 są to łańcuchy znakowe ScreenWidth i ScreenHeight. Wynika stąd, że właśnie pod takimi nazwami zostaną zapisane w bazie danych PlayerPrefs rekordy z liczbami całkowitymi oznaczającymi szerokość i wysokość ekranu. Liczby te można w każdym momencie trwania gry pobrać w sposób pokazany w listingu 8.2.
237
238
Rozdział 8
Zapisywanie stanu gry a trwałość danych
Listing 8.2. Wczytywanie rozdzielczości przy użyciu klasy PlayerPrefs int ScreenWidth = PlayerPrefs.GetInt("ScreenWidth"); int ScreenHeight = PlayerPrefs.GetInt("ScreenHeight");
Klasa PlayerPrefs do każdej metody Set dodaje jej odpowiednik typu Get (istnieją więc pary GetInt i SetInt, GetFloat i SetFloat oraz GetString i SetString). W sumie daje to pełny zestaw funkcji zapisu i odczytu danych. Same polecenia są, jak widać, bardzo proste, ale w związku z tym rodzą się pytania: ■ Gdzie dane są fizycznie zapisywane? ■ Co się stanie, gdy na tym samym komputerze dwie różne gry będą zapisywały dane do rekordów o tych samych nazwach i z tych samych rekordów będą je odczytywały? Co się stanie np. wtedy, gdy obie gry zapiszą różne wartości w rekordzie ScreenWidth? Czy wystąpi między nimi konflikt, czy po prostu nowsza wartość zastąpi starszą? Przyjrzyjmy się tym kwestiom nieco bliżej.
Preferencje gracza — ciąg dalszy Klasa PlayerPrefs jest z założenia klasą abstrakcyjną. Skoro ma działać na różnych platformach i być maksymalnie intuicyjna, to musi ukrywać przed użytkownikiem wszystkie operacje niskiego poziomu specyficzne dla poszczególnych systemów. Jako twórca gier otrzymujesz więc spójny i uniwersalny interfejs dostępowy do bazy danych — taki, który działa na wszystkich obsługiwanych platformach. Działanie to jest jednak trochę różne na poszczególnych platformach. W zależności od systemu operacyjnego zmieniają się miejsce i sposób zapisu danych. Szczegółowo jest to opisane w internetowej dokumentacji programu Unity pod hasłem PlayerPrefs, natomiast my skupimy się jedynie na praktycznych konsekwencjach wspomnianych różnic. Uwa ga
Więcej informacji na temat klasy PlayerPrefs można znaleźć w dokumentacji programu Unity zamieszczonej pod adresem: https://docs.unity3d.com/Documentation/ScriptReference/PlayerPrefs.html. Przede wszystkim, zanim się przystąpi do tworzenia gry, warto w programie Unity wpisać jej tytuł oraz nazwę firmy, ponieważ klasa PlayerPrefs używa tych informacji podczas zapisywania danych. Po prostu wybierz polecenie Edit/Project Settings/Player (edycja/ ustawienia projektu/gracz) — rysunek 8.1 — i w panelu Inspector (inspektor) wypełnij pola Company Name (nazwa firmy) oraz Product Name (nazwa produktu). Jeśli pozostawisz w tych polach wartości domyślne, firma otrzyma nazwę DefaultCompany (firma domyślna), a gra zostanie zatytułowana source (źródło).
Wybieranie danych trwałych
Rysunek 8.1. Ustalanie nazwy firmy i tytułu gry w programie Unity Źródło: Unity Technologies.
W zależności od systemu operacyjnego baza danych PlayerPrefs jest zapisywana w następujących lokalizacjach: ■ Na platformach Windows jest to rejestr systemowy. Dane trafiają tam do klucza HKCU\ Software\[nazwafirmy]\[nazwaproduktu]. (Wartości [nazwafirmy] i [nazwaproduktu] zostaną zastąpione zawartością odpowiednich pól z panelu Inspector). Informacje na temat odczytywania i modyfikowania zawartości rejestru wykraczają poza ramy tej książki. Więcej szczegółów na temat rejestru w systemie Windows można znaleźć pod adresem: http://windows.microsoft.com/en-gb/windows-vista/what-is-the-registry. ■ W systemach Mac preferencje gracza można znaleźć w folderze Library\Preferences jako plik o nazwie unity.[nazwafirmy].[nazwaproduktu].plist. (Wartości [nazwafirmy] i [nazwaproduktu] zostaną zastąpione zawartością odpowiednich pól z panelu Inspector). ■ W Linuksie dane składające się na preferencje gracza są umieszczane w folderze /.configunity3d [nazwafirmy]/[nazwaproduktu]. (I tu też wartości [nazwafirmy] i [nazwaproduktu] zostaną zastąpione zawartością odpowiednich pól z panelu Inspector).
Wybieranie danych trwałych PlayerPrefs jest wygodną i użyteczną klasą interfejsu programistycznego, która robi
dokładnie to, na co wskazuje jej nazwa: obsługuje zapis i odczyt preferencji gracza. Zazwyczaj jednak trzeba zachowywać też i inne dane. Muszą przecież być zapamiętywane kompletne stany gry, czyli położenia wrogów, położenie głównego bohatera, jego stan zdrowia, stan zdrowia wrogów, zebrane sztuki broni, status drzwi (czy są otwarte, czy zamknięte), status szuflad itp. Można oczywiście zapisywać te wszystkie dane, używając klasy PlayerPrefs i wywołując odpowiednią liczbę razy metody Set i Get, ale mogłoby to spowodować nadmierne rozbudowanie plików konfiguracyjnych lub rejestru,
239
240
Rozdział 8
Zapisywanie stanu gry a trwałość danych
a nawet wolniejsze działanie systemu. Krótko mówiąc: zastosowanie klasy PlayerPrefs do zapisu dużych ilości danych nie jest rozwiązaniem optymalnym. Znacznie lepsze może być użycie plików XML i właśnie tym zajmiemy się w następnym podrozdziale.
Pliki XML — a może JSON lub binarne Pisanie skryptów C# w Unity polega w dużej mierze na pracy w środowisku programistycznym typu open source o nazwie Mono, które z kolei opiera się na microsoftowej platformie programistycznej .NET. Mono jest darmową i uniwersalną implementacją tej platformy proponującą podobne funkcje i klasy. W naturalny sposób obsługuje pliki w tekstowym formacie XML, który stanowi chyba najczęściej stosowaną metodę zapisu danych w produkowanych obecnie grach. Mono udostępnia kilka metod korzystania z plików XML, ale tym razem skupimy się na metodzie zwanej serializacją danych (data serialization). Przykład zapisu stanu gry zrealizowany w ten właśnie sposób jest przedstawiony w listingu 8.3. Listing 8.3. Przykład zapisu stanu gry w pliku XML 1.94054472 0.019997187 -8.58917 0 129.9697 0 1 1 1 3 100 12.1057281 1.05 -17.1096153 0 39.75003 0 1 1 1 200
Pliki XML — a może JSON lub binarne true 50
Uwa ga
O platformie programistycznej Mono dowiesz się więcej ze strony: http://www.mono-project.com/Main_Page. Zanim przejdziemy do bardziej szczegółowego omówienia plików XML, warto wspomnieć o jeszcze dwóch innych formatach, których można by tutaj użyć. Poniżej podaję ich krótkie opisy wraz z uzasadnieniami, dlaczego je odrzuciłem. Oczywiście powody te mogą się okazać nieuzasadnione w innych projektach. Krótko mówiąc: do każdego przypadku trzeba podchodzić indywidualnie.
Pliki JSON Format JavaScript Object Notation (JSON), podobnie jak XML, ma charakter tekstowy, co oznacza, że po otwarciu takiego pliku można łatwo przeczytać jego zawartość. Jest to jednak format znacznie uproszczony w porównaniu z XML-em. Pod wieloma względami świetnie nadaje się do zapisu stanów gry, ale niestety Unity nie ma wbudowanej jego obsługi i nie jest też obsługiwany przez środowisko programistyczne Mono. Istnieją wprawdzie dodatki, które problem obsługi tego formatu rozwiązują, ale nie są to stałe składniki pakietu Unity, a ponieważ przyjąłem zasadę, że będę opisywał tylko te narzędzia, które są na stałe wbudowane w Unity, muszę format JSON pominąć. Uwa ga
Więcej informacji na temat możliwości stosowania formatu JSON w Unity znajdziesz pod adresem: http://wiki.unity3d.com/index.php/SimpleJSON.
Pliki binarne W odniesieniu do plików pojęcie binarny oznacza każdy plik, który nie jest tekstowy. Pliki tego typu po otwarciu w zwykłym edytorze tekstowym wyglądają raczej bezsensownie (rysunek 8.2), ale właśnie to czyni je bardzo zdatnymi do zapisu stanów gry, ponieważ niemożność odszyfrowania ich zawartości znakomicie utrudnia graczom oszukiwanie przez manipulowanie zapisami. Mono ma wbudowaną obsługę takich plików, a służy do tego klasa binaryFormatter. Na razie pozostanę jednak przy formacie XML, ponieważ w trakcie tworzenia gry tekstowej zapisy jej stanów są bardzo pożądane jako pomoc w debugowaniu i dopracowywaniu finalnej wersji. Dzięki temu, że zapisane stany gry dają się łatwo odczytać, można szybko zweryfikować ich spójność z głównym kodem. Można też bez problemu sprawdzić, jak będą wyglądały rozmaite scenerie gry, bo przecież wystarczy odpowiednio zmodyfikować zapis jej stanu, aby szybko przejść do dowolnego etapu.
241
242
Rozdział 8
Zapisywanie stanu gry a trwałość danych
Rysunek 8.2. Plik binarny otwarty w zwykłym edytorze tekstowym Źródło: Notepad++.
Serializacja klasy Platforma Mono oferuje dwie główne metody zapisu danych trwałych w plikach XML. ■ Zapis ręczny. Metoda ta polega na tworzeniu węzłów i przeprowadzaniu analizy składniowej zapisywanych danych. Mono oferuje kilka klas ułatwiających wykonanie tych zadań. Między innymi są tam klasy XmlDocument i XmlReader, które służą do zapisywania i odczytywania plików XML węzeł po węźle. Stosowanie ich oznacza konieczność tworzenia nowego węzła dla każdej zapisywanej wartości, co może być tak samo żmudne jak tworzenie rekordów w przypadku stosowania klasy Player Prefs. Przy dużej liczbie zapisywanych danych może też prowadzić do nadmiernego wydłużenia kodu. Uwa ga
Więcej informacji na temat klasy XmlDocument znajdziesz pod adresem: http://msdn.microsoft.com/en-us/library/system.xml.xmldocument%28v=vs.110%29.aspx. ■ Serializacja. Serializacja polega na zapisywaniu całych obiektów (w sensie struktur programowych) jako strumieni bajtów. Wygenerowany w ten sposób plik jest tak zorganizowany, że przy jego odczycie następuje odtworzenie zapisanych wcześniej obiektów. Bez stosowania serializacji programista musi sam przeszukiwać klasy i obiekty, aby wybrać te dane, które mają być zapisane. Natomiast gdy stosuje serializację, po prostu kieruje do pliku całą klasę. Są pewne ograniczenia, ponieważ nie wszystkie typy danych można w ten sposób zapisać, ale jest to problem, który daje się łatwo rozwiązać. W następnym podrozdziale pokażę, jak to się robi.
Przygotowanie danych do serializacji
Przygotowanie danych do serializacji Aby zobaczyć, jak w praktyce wygląda zapisywanie stanu gry, rozważmy prosty przykład gry FPS w stylu Duke Nukem czy Doom, w której obok postaci gracza występują także wrogowie. Zapiszmy transformacje wrogów (położenie, orientację i skalę) obecnych w scenie, a także niektóre istotne dane dotyczące gracza, np. jego transformacje. Zapisanie takich danych pozwoli później na odtworzenie położenia wszystkich postaci. Aby zrobić to metodą serializacji, trzeba najpierw utworzyć dla tych danych dodatkowe klasy, których typ musi być zmieniony na potrzeby serializacji. Klasy te mogą zawierać tylko podstawowe typy danych wbudowane w język C#, a nie pochodzące z API czy z jakiejś biblioteki. Przykładowo: w Unity wektory są instancjami specyficznej dla tego środowiska klasy Vector3, a tak naprawdę są to tylko powiązane ze sobą trójki liczb zmiennoprzecinkowych określających składowe wektorów względem osi X, Y i Z. Podobnie orientacje, czyli obroty, są w Unity reprezentowane przez czteroskładnikowe struktury zwane kwaternionami, a w gruncie rzeczy to samo można zapisać za pomocą trójki liczb zmiennoprzecinkowych określających kąty obrotu względem trzech osi układu odniesienia. A zatem wszystkie informacje dotyczące transformacji obiektu można wyrazić za pomocą jedynie fundamentalnych typów danych. I właśnie dla takich danych utworzymy nową klasę (listing 8.4). Listing 8.4. Klasa danych transformacyjnych nadająca się do serializacji public struct DataTransform { public float X; public float Y; public float Z; public float RotX; public float RotY; public float RotZ; public float ScaleX; public float ScaleY; public float ScaleZ; }
Uwa ga
W listingu 8.4 zastosowano typ danych float (zmiennoprzecinkowe), ale nie jest to jedyny typ dozwolony w serializacji. Dopuszczalne są również typy bool (dane logiczne), int (liczby całkowite), string (dane tekstowe) i list (lista). Więcej informacji na temat typów danych i klas zdatnych do serializacji znajdziesz pod adresem: http://msdn.microsoft.com/en-us/library/ms731923%28v=vs.110%29.aspx. Spójrz na listing 8.5. Prezentuje on niemal całą zawartość pliku źródłowego (SaveState.cs) zawierającego przykładowy zestaw klas służących do obsługi serializacji danych opisujących stan gry — są to dane dotyczące postaci gracza i wrogów. Dodatkowe objaśnienia umieściłem za listingiem.
243
244
Rozdział 8
Zapisywanie stanu gry a trwałość danych
Listing 8.5. Przykładowy zestaw klas C# potrzebnych do zapisu stanu gry metodą serializacji //Zapisuje i odczytuje stan gry, korzystając z formatu XML. //----------------------------------------------using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Xml; using System.Xml.Serialization; using System.IO; //----------------------------------------------public class SaveState { //Zapisz dane gry. [XmlRoot("GameData")] //----------------------------------------------//Dane transformacyjne obiektu znajdującego się w scenie. [System.Serializable] public struct DataTransform { public float X; public float Y; public float Z; public float RotX; public float RotY; public float RotZ; public float ScaleX; public float ScaleY; public float ScaleZ; } //----------------------------------------------//Ogólnie dostępna klasa dla postaci wroga.
public class EnemyData { //Dane transformacyjne wroga. public DataTransform Transformation; //Czy wróg jest uzbrojony? public bool Armed = false; } //----------------------------------------------//Ogólnie dostępna klasa dla postaci gracza.
public class PlayerData { //Dane transformacyjne gracza. public DataTransform PlayerTransform; } //----------------------------------------------//Klasa przechowująca dane zasadnicze. public class GameData { //Dane dotyczące wrogów.
public List Enemies = new List(); //Dane dotyczące gracza.
Przesyłanie danych do pliku XML public PlayerData PD = new PlayerData(); } //----------------------------------------------//Składnik klasy GameData (dane gry). public GameData GD = new GameData(); } //-----------------------------------------------
Klasa SaveState (zapis stanu) zawiera pełny zestaw klas służących do przechowywania danych mających kluczowe znaczenie dla zapisu i przywracania stanu gry. Klasy te są zebrane w składniku GD (instancji klasy GameData). Klasa GameData (dane gry) zawiera składnik PlayerData (dane gracza) z transformacjami gracza i listę wrogów. Wrogowie tworzą strukturę typu lista, która w trakcie trwania gry może się dynamicznie wydłużać lub skracać, aby pomieścić dokładnie tyle elementów, ile potrzeba do zapisania wszystkich wrogów.
Przesyłanie danych do pliku XML Opisana w poprzednim podrozdziale klasa SaveState zawiera wszystko, co na poziomie klasy jest potrzebne do przechowywania informacji na temat położenia postaci gracza i wrogów. Wszystkie tego typu dane są zebrane w składniku GameData i właściwie on sam wystarczyłby jako źródło danych definiujących stan gry. Na razie klasa SaveState nie ma jeszcze składnika, który by te dane przeniósł do pliku. Aby ten brak nadrobić, dodamy jej metodę Save() (zapisz). Argumentem tej metody będzie pełna ścieżka dostępu do pliku XML, do którego zostanie skierowany strumień danych. Szczegóły implementacyjne metody Save() są pokazane w listingu 8.6. Listing 8.6. Zapisywanie danych do pliku //Zapisuje dane gry do pliku XML.
public void Save(string FileName = "GameData.xml") { //Teraz zapisz dane gry.
XmlSerializer Serializer = new XmlSerializer(typeof(GameData)); FileStream Stream = new FileStream(FileName, FileMode.Create);
Serializer.Serialize(Stream, GD); Stream.Close(); }
Najpierw metoda Save()przyjmuje argument w postaci łańcucha znakowego o nazwie FileName (nazwa pliku), który wskazuje, gdzie w lokalnym systemie pamięci masowej powinien być zapisany plik XML. Następnie korzysta ona z klasy XmlSerializer (serializator XML), aby utworzyć obiekt typu FileStream (strumień plikowy) odpowiedzialny za przesyłanie danych do pliku. Cała operacja kończy się serializacją obiektu GameData i zamknięciem strumienia przesyłowego. Żeby się jednak wszystko udało, trzeba spełnić
245
246
Rozdział 8
Zapisywanie stanu gry a trwałość danych
jeden podstawowy warunek — argument metody Save()musi reprezentować prawidłową ścieżkę dostępu do miejsca zapisu pliku. Pod koniec rozdziału pokażę, jak należy ten argument konstruować, aby działał na większości platform komputerowych.
Odczytywanie danych z pliku XML Listing 8.6 pokazuje, jak można wykonać serializację obiektu GameData i zapisać go w pliku XML, używając klasy XmlSerializer. A jak teraz przywrócić zapisane w ten sposób dane? Mówiąc inaczej: jak przeprowadzić ich deserializację? Odpowiedzią na to pytanie jest metoda Load()(wczytaj) z listingu 8.7. Listing 8.7. Przywracanie danych zapisanych w pliku XML //Wczytuje dane z pliku XML. public void Load(string FileName = "GameData.xml") { XmlSerializer Serializer = new XmlSerializer(typeof(GameData)); FileStream Stream = new FileStream(FileName, FileMode.Open);
GD = Serializer.Deserialize(Stream) as GameData; Stream.Close(); }
Jak widać, metoda Load() jest bardzo podobna do metody Save(). Z formalnego punktu widzenia różnią się one tylko tym, że jedna wykorzystuje funkcję Serialize(), a druga robi użytek z funkcji Deserialize(). To właśnie za pomocą funkcji Deserialize()metoda Load() odtwarza instancję klasy GameData — tworzy ją i inicjalizuje na podstawie danych zapisanych w pliku XML. Pełną definicję klasy SaveState z dodanymi już metodami Save()i Load() przedstawia listing 8.8. Listing 8.8. Kompletna klasa SaveState //Zapisuje i odczytuje stan gry, korzystając z formatu XML. //----------------------------------------------//Nie zapomnij dołączyć wszystkich klas potrzebnych do zapisania stanu gry. using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Xml; using System.Xml.Serialization; using System.IO; //----------------------------------------------//Zapisem i odczytem danych zajmie się klasa SaveState. public class SaveState { //Zapisz dane gry. [XmlRoot("GameData")] //----------------------------------------------//Dane transformacyjne obiektu znajdującego się w scenie. [System.Serializable] public struct DataTransform
Odczytywanie danych z pliku XML { public public public public public public public public public
float float float float float float float float float
X; Y; Z; RotX; RotY; RotZ; ScaleX; ScaleY; ScaleZ;
} //----------------------------------------------//Ogólnie dostępna klasa dla postaci wroga //ma przechowywać dane dotyczące postaci wroga, które mają być zapisane w pliku XML. public class EnemyData { //Dane transformacyjne wroga (położenie, obrót i skala). public DataTransform Transformation; //Czy wróg jest uzbrojony?(O ile to ma sens w danej grze). public bool Armed = false; } //----------------------------------------------//Ogólnie dostępna klasa dla postaci gracza. public class PlayerData { //Dane transformacyjne gracza. public DataTransform PlayerTransform; } //----------------------------------------------//Klasa przechowująca dane zasadnicze. public class GameData { //Dane dotyczące wrogów. public List Enemies = new List(); //Dane dotyczące głównego gracza. //Instancja przechowująca wszystkie dane dotyczące gracza (położenie, obroty, skala itp.). public PlayerData PD = new PlayerData(); } //----------------------------------------------//Składnik klasy GameData (dane gry). //Główna struktura danych zawiera wszystkie dane przeznaczone do zapisu w pliku XML. public GameData GD = new GameData(); //----------------------------------------------//Zapisuje dane gry do pliku XML. public void Save(string FileName = "GameData.xml") { //Teraz zapisz dane gry. XmlSerializer Serializer = new XmlSerializer(typeof(GameData)); FileStream Stream = new FileStream(FileName, FileMode.Create); Serializer.Serialize(Stream, GD); Stream.Close(); } //-----------------------------------------------
247
248
Rozdział 8
Zapisywanie stanu gry a trwałość danych
//Wczytuje dane z pliku XML. public void Load(string FileName = "GameData.xml") { XmlSerializer Serializer = new XmlSerializer(typeof(GameData)); //Otwiera plik i przesyła jego zawartość (w formie strumienia danych) do obiektu GameData. FileStream Stream = new FileStream(FileName, FileMode.Open); GD = Serializer.Deserialize(Stream) as GameData; Stream.Close(); } //----------------------------------------------} //-----------------------------------------------
Uwa ga
Kompletna klasa SaveState jest zamieszczona w materiałach powiązanych z książką. Szukaj jej w folderze Rozdział08.
Dodatkowe uwagi na temat klasy SaveState Teraz klasa SaveState zawiera już wszystko, co jest potrzebne do zapisania stanu gry. Gdy gra utworzy instancję tej klasy i wypełni składnik GameData odpowiednimi wartościami, będzie on gotowy do serializacji. Proces ten zostanie przeprowadzony, gdy tylko wywołamy metodę Save(). Potem, żeby wczytać zapisany stan gry, wystarczy wywołać metodę Load(). Trzeba jednak obu tym metodom podać ścieżkę do miejsca, w którym plik ma być zapisany lub już się znajduje. Jak ustalić tę ścieżkę? Na szczęście Unity potrafi wykonać to zadanie za nas i automatycznie wygenerować właściwy łańcuch znakowy. Spójrz na listing 8.9, który przedstawia funkcję Update() (aktualizacja). Za każdym razem, gdy użytkownik wciśnie klawisz S, funkcja ta zapisze stan gry (przy użyciu klasy SaveState), a przywróci go, gdy użytkownik wciśnie klawisz L. W tym listingu klasa SaveState jest reprezentowana przez obiekt SS. Listing 8.9. Zapisywanie i wczytywanie stanów gry //Aktualizacja jest wywoływana raz na klatkę. void Update() { //Jeśli wciśnięto S, zapisz stan gry. if(Input.GetKeyDown(KeyCode.S)) {
SS.Save(Application.persistentDataPath + "/GameState.xml"); } //Jeśli wciśnięto L, wczytaj stan gry. if(Input.GetKeyDown(KeyCode.L)) {
SS.Load(Application.persistentDataPath + "/GameState.xml"); } }
Podsumowanie
Z listingu 8.9 wynika, że wygenerowany łańcuch znajduje się w zmiennej Application. persistentDataPath. Ścieżka będzie się zmieniała w zależności od komputera, systemu operacyjnego i tego, jak ten system jest skonfigurowany, ale zawsze będzie wskazywała miejsce w lokalnym systemie pamięci masowej, gdzie informacje o stanie gry mogą być bezpiecznie zapisane. Gratulacje! Własnoręcznie zbudowałeś system zapisu i odczytu danych. W materiałach towarzyszących książce (w folderze Rozdział08) znajduje się przykładowy projekt, w którym zastosowano klasę SaveState do zapisywania i odczytywania danych. Zachęcam do przeanalizowania tego projektu. Uwa ga
Więcej informacji na temat zmiennej persistentDataPath znajdziesz w internetowej dokumentacji programu Unity na stronie: https://docs.unity3d.com/Documentation/ScriptReference/ApplicationpersistentDataPath.html.
Podsumowanie W tym rozdziale omówiłem zapisywanie i odczytywanie stanów gry. Przedstawiłem dwie zasadnicze metody postępowania. Pierwsza polega na zastosowaniu klasy PlayerPrefs, a druga wymaga użycia klasy XmlSerializer. W obu przypadkach dane były zapisywane w pliku XML, którego zawartość można łatwo odczytać i modyfikować za pomocą zwykłego edytora tekstu. Obie metody pozwalają na zapisanie właściwie każdego rodzaju danych potrzebnych do zdefiniowania stanu gry.
249
250
Rozdział 8
Zapisywanie stanu gry a trwałość danych
R OZ DZ I AŁ 9
W YPALANIE
Sukces zależy od wcześniejszego przygotowania, a bez takiego przygotowania klęska jest pewna. — Konfucjusz Po przeczytaniu tego rozdziału powinieneś: ■ wiedzieć, czym jest wypalanie; ■ umieć wypalać tekstury i mapy okluzji otoczenia w Blenderze; ■ umieć komponować wypalone tekstury w GIMP-ie; ■ rozumieć mapowanie światła w Unity; ■ umieć obchodzić się z próbnikami światła w Unity. Z jednej strony, autorzy gier ciągle szukają nowych sposobów na przełamywanie twórczych barier, kreowanie coraz wspanialszych efektów i sprawianie, by gry stawały się jeszcze bardziej realistyczne i wciągające. Z drugiej strony, muszą ciągle myśleć o kwestiach związanych z optymalizacją i wydajnością. Muszą być świadomi ograniczeń sprzętowych i konfrontować swoje kreatywne wizje z możliwościami używanego powszechnie sprzętu. Istnienie tych dwóch antagonistycznych skrajności — przełamywania barier i respektowania ograniczeń — oznacza, że twórca gry musi sporo czasu poświęcić na szukanie właściwej równowagi między tym, co chciałby uzyskać, a tym, co większość potencjalnych użytkowników będzie w stanie uruchomić na swoim sprzęcie. Jednym ze sposobów godzenia tych dwóch skrajności jest stosowanie rozmaitych technik optymalizacyjnych, a jedną z nich jest tzw. wypalanie (baking). W tym rozdziale skupimy się na rozmaitych formach wypalania zarówno w Blenderze i Unity, jak i poza tymi programami.
251
252
Rozdział 9
Wypalanie
Czym jest wypalanie? W trakcie trwania gry komputer ciągle wykonuje mnóstwo rozmaitych obliczeń. Musi przecież na bieżąco wyznaczać położenie wrogów i innych postaci, określać parametry efektów świetlnych, ustalać poziom dźwięku w zależności od tego, gdzie znajduje się postać gracza itp. Danych do przetworzenia jest tak wiele, że nie sposób je tutaj wszystkie wymienić. Jeśli tylko uda Ci się uwolnić komputer od przetwarzania jakichś danych w czasie rzeczywistym, gra zyska na wydajności, bo komputer będzie miał więcej czasu na pozostałe obliczenia. A zatem jeśli tylko możesz wykonać jakieś obliczenia wcześniej i umieścić w grze ich gotowe wyniki, zrób to, aby komputer nie musiał się tym zajmować podczas działania gry. Proces przygotowywania takich danych nosi nazwę wypalania, a same dane określane są jako wypalone (baked data). W tym rozdziale omówię trzy rodzaje wypalania danych w Unity i Blenderze: ■ wypalanie oświetlenia statycznego, ■ wypalanie oświetlenia dynamicznego, ■ wypalanie nawigacji.
Wypalanie oświetlenia statycznego Wypalanie oświetlenia polega na wcześniejszym wykonaniu obliczeń związanych z efektami świetlnymi. Wypalanie takie przybiera różne formy, ale niemal zawsze sprowadza się do wykonania odpowiednich tekstur z zakodowanymi poziomami światła. Najbardziej rozpowszechnioną techniką jest mapowanie światła (lightmapping). Polega ona na obliczeniu oświetlenia dla wszystkich obiektów statycznych (nieruchomych) i zapisaniu rezultatu w formie specjalnych tekstur zwanych mapami światła (lightmaps). Często zawierają one zarówno oświetlenie bezpośrednie, jak i pośrednie, a także cienie. W trakcie działania gry tekstury te są nakładane na siatki obiektów i mieszane z ich zwykłymi teksturami, aby wywołać złudzenie rzeczywistego oświetlenia światłem pochodzącym ze źródeł umieszczonych w scenie na stałe. Przykład sceny z nałożonymi mapami światła jest pokazany na rysunku 9.1. Nieco inną techniką jest wypalanie oświetlenia (z okluzją otoczenia) w standardowych teksturach za pomocą Blendera i następnie importowanie do Unity siatek już oświetlonych — system mapowania światła w Unity jest wtedy całkowicie pomijany. Dokładniejszy opis tej metody znajdziesz w dalszej części rozdziału.
Wypalanie oświetlenia dynamicznego Mapowanie światła — i w ogóle oświetlenie wypalane w teksturach — napotyka istotne ograniczenia, jeśli chodzi o oświetlanie obiektów dynamicznych (czyli takich, które w trakcie działania gry zmieniają swoje położenie). Wcześniejsze obliczenie i wypalenie
Czym jest wypalanie?
Rysunek 9.1. Scena z nałożonymi mapami światła w Unity (plik z tą sceną znajduje się w materiałach powiązanych z książką, w folderze Rozdział09) Źródło: Unity Technologies.
oświetlenia jest w takim przypadku po prostu niemożliwe, ponieważ nie da się z góry określić, gdzie i kiedy dany obiekt będzie się znajdował. Oświetlenie siatki, która jest w ruchu, może się zmieniać na nieskończenie wiele sposobów, gdyż ciągle zmienia się jej usytuowanie względem źródeł światła. Dlatego mapowanie światła stosuje się na ogół tylko w odniesieniu do obiektów nieruchomych. Obliczenia związane z oświetleniem siatek dynamicznych często przeprowadza się w czasie rzeczywistym, bo wtedy efekty są najdokładniejsze i najbardziej realistyczne. Takie rozwiązanie jest jednak zaprzeczeniem optymalizacji. Dlatego w Unity wprowadzono możliwość stosowania techniki pośredniej, polegającej na połowicznym wypalaniu oświetlenia dla obiektów dynamicznych (rysunek 9.2). Jej istotą jest stosowanie tzw. próbników światła (light probes).
Wypalanie nawigacji Wypalanie wiąże się najczęściej z oświetleniem i teksturami, ale bywa stosowane także przy opracowywaniu innych elementów gry. Może to być np. wyznaczanie tras i nawigacja. Postacie inne niż postać gracza (non-player characters — NPCs) powinny przecież poruszać się w sposób inteligentny. Muszą „wiedzieć”, dokąd zmierzają, i po drodze omijać wszelkie przeszkody. Muszą przechodzić przez drzwi, a nie przez ściany. Wyznaczanie właściwych tras może jednak wymagać wielu obliczeń — szczególnie w rozbudowanym środowisku i przy dużej liczbie postaci. Jednym z rozwiązań mogących ułatwić i przyspieszyć ten proces jest wypalenie, albo inaczej: wygenerowanie specjalnej
253
254
Rozdział 9
Wypalanie
Rysunek 9.2. Próbniki światła umożliwiają symulację oświetlenia dynamicznego Źródło: Unity Technologies.
siatki reprezentującej możliwe do przejścia obszary środowiska (rysunek 9.3). Jest ona powszechnie nazywana siatką nawigacyjną (navmesh) i może w znacznym stopniu przyspieszyć obliczenia nawigacyjne. Jak taka siatka działa w praktyce, zobaczysz w dalszej części rozdziału.
Rysunek 9.3. Siatka nawigacyjna ułatwia wyznaczanie możliwych do przejścia tras Źródło: Unity Technologies.
Przygotowanie mapowania światła w Unity
Przygotowanie mapowania światła w Unity Wróćmy do pierwszej formy wypalania w Unity, czyli do mapowania światła, albo inaczej: do wypalania oświetlenia statycznych siatek środowiskowych, żeby w trakcie gry środowisko wyglądało na oświetlone przez istniejące w scenie źródła światła. Zakładam, że już masz jakieś środowisko zmontowane wcześniej w Unity. Jeśli nie masz, zajrzyj do rozdziału 3. Tam znajdziesz wskazówki, które pozwolą Ci samodzielnie zbudować środowisko metodą modułową. W ostateczności, jeśli nie chcesz robić tego teraz sam, możesz wykorzystać gotowe moduły środowiska zapisane w powiązanych z książką plikach z folderu Rozdział09/LightMapStart. Rysunek 9.4 przedstawia utworzoną przeze mnie scenę, którą zamieściłem w plikach dołączonych do książki. Są tu już lampy, ale mapowanie światła jeszcze nie zostało przeprowadzone. W tej chwili oświetlenie jest wyliczane na bieżąco (w czasie rzeczywistym), co nie jest rozwiązaniem optymalnym, zwłaszcza że żadna z obecnych tu siatek nigdy podczas gry nie będzie zmieniała pozycji. Zauważ też, że nie ma tu efektów oświetlenia pośredniego i cieni, a cała scena jest dość ciemna. To, że jest ciemniej niż powinno, może być nawet korzystne, ponieważ mapowanie światła prawie zawsze rozjaśnia scenę przez dodanie oświetlenia pośredniego.
Rysunek 9.4. Scena gotowa do mapowania światła Źródło: Unity Technologies.
Zanim przejdziemy dalej, wyłącz ogólne oświetlenie sceny (jeśli jeszcze nie jest wyłączone). Domyślnie każda scena w Unity jest oświetlana ogólnym światłem otaczającym, które jest stosunkowo słabe, rozchodzi się we wszystkich kierunkach i wszędzie ma takie samo natężenie. Jest to podstawowe oświetlenie zapewniające widoczność obiektów nawet wtedy, gdy nie wstawiliśmy jeszcze żadnego źródła światła. Oświetlenie
255
256
Rozdział 9
Wypalanie
takie przydaje się podczas budowania sceny, ale po wstawieniu wszystkich zaplanowanych źródeł światła należy to oświetlenie wyłączyć, ponieważ spłyca głębię przestrzenną, a to jest na ogół niepożądane. Aby wyłączyć światło otaczające, wybierz polecenie Edit/Render Settings (edycja/ustawienia renderingu) i w panelu Inspector ustaw w polu Ambient Light (światło otaczające) kolor czarny. Czerń oznacza brak światła (ciemność), więc ustawienie takiego koloru jest równoznaczne z całkowitym wyłączeniem światła (rysunek 9.5).
Rysunek 9.5. Wyłączanie światła otaczającego Źródło: Unity Technologies.
Następnie zaznacz wszystkie siatki środowiska i przypisz im status statycznych, włączając w panelu Inspector opcję Static (statyczny) — rysunek 9.6. Mapowanie światła ma zastosowanie tylko w odniesieniu do siatek statycznych (nieruchomych), takich jak ściany, podłogi i wszelkie stałe rekwizyty. Pamiętaj: statyczność można przypisać wielu obiektom jednocześnie!
Rysunek 9.6. W ramach przygotowań do mapowania światła oznacz odpowiednie obiekty jako statyczne Źródło: Unity Technologies.
Mapowanie światła. Rozdzielczość mapy światła
Mapowanie światła. Rozdzielczość mapy światła Rozdzielczość mapy światła decyduje o szczegółowości wygenerowanej tekstury zawierającej zapis oświetlenia sceny. Więcej informacji na ten temat podam przy objaśnianiu kolejnych etapów poniższej procedury. Wykonaj następujące czynności: 1. Wybierz polecenie Window/Lightmapping (okno/mapowanie światła), aby otworzyć edytor Lightmapping. Edytor ten zwykle otwiera się jako niezależne okno, ale ja wolę go mieć zadokowanego po prawej stronie razem z panelem Inspector, tyle że na oddzielnej zakładce. Dzięki temu mogę widzieć jednocześnie i ustawienia mapy światła, i scenę (rysunek 9.7).
Rysunek 9.7. Edytor Lightmapping zadokowany obok okna widokowego Źródło: Unity Technologies.
2. Kliknij zakładkę Bake (wypalanie) edytora, aby uzyskać dostęp do głównych opcji mapowania światła (rysunek 9.8). W dolnej części znajduje się jeden z najważniejszych parametrów tego procesu, a mianowicie Resolution (rozdzielczość). To od jego wartości zależy, jak duża (w pikselach) i jak szczegółowa będzie wygenerowana mapa światła. 3. Z parametrem Resolution powiązana jest opcja Show Resolution (pokaż rozdzielczość) umieszczona w oknie Lightmap Display (wyświetlanie mapy światła), które pokazuje się w oknie widokowym, gdy tylko włączony jest edytor Lightmapping. Jeśli włączysz wspomnianą opcję, na powierzchnię wszystkich ścianek zostanie nałożona półprzezroczysta szachownica o gęstości zgodnej z gęstością mapy światła, jaka zostałaby wygenerowana przy bieżących ustawieniach (rysunek 9.9). Każde pole szachownicy odpowiada jednemu pikselowi mapy światła. Gdy zwiększasz wartość parametru Resolution w edytorze Lightmapping, pola szachownicy stają się mniejsze, ponieważ przy większej rozdzielczości więcej pikseli (a więc i pól szachownicy)
257
258
Rozdział 9
Wypalanie
Rysunek 9.8. Rozdzielczość decyduje o rozmiarze tekstury pełniącej funkcję mapy światła Źródło: Unity Technologies.
musi się zmieścić na tym samym obszarze. Krótko mówiąc: wraz ze wzrostem rozdzielczości rośnie liczba pikseli mapy światła, a ona sama staje się bardziej szczegółowa. Wzrost liczby pikseli oznacza jednak większe zapotrzebowanie na pamięć i większe obciążenie procesora. Trzeba więc szukać kompromisu i ustawiać rozdzielczość na najniższym poziomie, przy którym jakość oświetlenia będzie zadowalająca.
Rysunek 9.9. Podgląd rozdzielczości mapy światła Źródło: Unity Technologies.
Tryb mapowania światła
Uwa ga
Rozdzielczość nie jest jedynym parametrem decydującym o rozmiarze wygenerowanej mapy światła. Wartość tego parametru określa jedynie relację między rozmiarem piksela a globalną jednostką powierzchni. Całkowita liczba pikseli w mapie światła zależy i od rozdzielczości, i od łącznej powierzchni wszystkich siatek statycznych, a dokładnie: jest równa iloczynowi tych wartości.
Tryb mapowania światła Drugim ważnym parametrem mapowania światła jest Mode (tryb). Od niego zależy równowaga między oświetleniem obliczanym w czasie rzeczywistym a oświetleniem zapisanym w mapie światła. To on decyduje, ile światła podczas gry będzie pochodziło z bieżących obliczeń, a ile z teksturowej mapy światła. Parametr ten jest też odpowiedzialny za mieszanie obu rodzajów oświetlenia. Jak wynika z rysunku 9.10, parametr ten może przyjmować jedną z trzech wartości.
Rysunek 9.10. Ustalanie trybu mapowania światła Źródło: Unity Technologies.
■ Single Lightmaps (pojedyncze mapy światła). W tym trybie wszystkie efekty oświetleniowe, włącznie z oświetleniem pośrednim i bezpośrednim, z okluzją otoczenia i z cieniami, są wypalane w teksturach siatek statycznych. W naszym przykładzie zastosujemy właśnie ten tryb. ■ Dual Lightmaps (podwójne mapy światła). Po wybraniu tego trybu tworzone są dwie odmiany teksturowych map światła. Jedna (mapa dalsza) zawiera wszystkie informacje oświetleniowe, podobnie jak pojedyncza mapa światła. Jest ona nakładana na siatki, gdy te są oglądane z większej odległości. Gdy kamera przysuwa się bliżej, miejsce mapy dalszej zajmuje mapa bliższa, która zawiera efekty oświetlenia pośredniego i okluzji otoczenia. Nie ma w niej efektów oświetlenia pośredniego, ponieważ te zostaną wyliczone w czasie rzeczywistym i dopiero wtedy będą mieszane z wy-
259
260
Rozdział 9
Wypalanie
palonymi efektami oświetleniowymi, aby jeszcze bardziej urealnić ostateczny rezultat. Krótko mówiąc: tryb Dual Lightmaps został wprowadzony po to, aby zwiększyć realizm scen, zwłaszcza w grach FPS, gdzie odległość kamery od siatek często się zmienia. ■ Directional Lightmaps (kierunkowe mapy światła). Jest to jeszcze bardziej wyspecjalizowana odmiana trybu Dual Lightmaps, która pozwala na wypalenie w mapie światła dodatkowych danych, takich jak mapa normalnych. Właściwości tego trybu wykraczają jednak poza ramy tej książki i nie będą omawiane. Uwa ga
Więcej informacji na temat mapowania światła znajdziesz w internetowej dokumentacji programu Unity (http://docs.unity3d.com/Documentation/Manual/LightmappingInDepth.html). Uwa ga
Pojęcie oświetlenia bezpośredniego (direct illumination) odnosi się do oświetlenia powstałego w wyniku padania promieni wyemitowanych przez źródło światła. Promienie te rozchodzą się po liniach prostych i jeśli na swej drodze trafią na ścianę, podłogę czy inną siatkę, oświetlają ją. Z oświetleniem pośrednim (indirect illumination) mamy do czynienia wtedy, gdy promienie oświetlające daną powierzchnię wcześniej odbiły się od innej siatki (lub innych siatek). Oświetlenie bezpośrednie na ogół tworzy jaśniejsze i bardziej rzucające się w oczy efekty świetlne, a oświetlenie pośrednie pełni raczej funkcję uzupełniającą. Na potrzeby naszego przykładu wybierzemy tryb Single Lightmaps i wypalimy wszystkie efekty oświetlenia statycznego w teksturach mapy światła.
Oświetlenie pośrednie i okluzja otoczenia Jeśli używasz programu Unity w wersji Pro, to masz dostęp do pełnego zakresu funkcji związanych z mapowaniem światła, włącznie z wypalaniem efektów oświetlenia pośredniego i okluzji otoczenia. Jeśli jednak nie masz wersji Pro, to i tak możesz to samo zrobić, tyle że w Blenderze — będzie o tym mowa w jednym z następnych podrozdziałów. Na razie zakładam, że masz Unity Pro, i w związku z tym pozostaję przy omawianiu wypalania efektów oświetlenia pośredniego oraz okluzji otoczenia za pomocą funkcji dostępnych w tym programie. Jak wynika z rysunku 9.11, Unity oferuje kilka różnych ustawień, za pomocą których można sterować efektami oświetlenia pośredniego. ■
Bounces (odbicia). W tym polu można ustawić maksymalną liczbę odbić, jakim może ulec promień światła, aby mógł jeszcze oświetlić kolejną powierzchnię. Liczba 1 będzie oznaczała, że promień może się odbić tylko raz — gdy padnie na następną powierzchnię, oświetli ją, ale już się od niej nie odbije. Liczba 2 zezwoli na
Oświetlenie pośrednie i okluzja otoczenia
Rysunek 9.11. Parametry mapowania światła związane z oświetleniem pośrednim Źródło: Unity Technologies.
maksymalnie dwa odbicia itd. Zmniejszenie tej wartości do 0 spowoduje całkowite wyłączenie efektów oświetlenia pośredniego, ponieważ zakazane zostaną jakiekolwiek odbicia promieni świetlnych. W teorii wyższe wartości dają bardziej realistyczne rezultaty, ale dzieje się to kosztem wydłużenia czasu obliczeń. W praktyce wartości 1 lub 2 są na ogół wystarczające. ■ Sky Light Color (kolor światła firmamentu) i Sky Light Intensity (natężenie światła firmamentu). Te parametry określają barwę i natężenie światła otaczającego i wszechobecnego, które w scenach plenerowych pochodzi od kopuły nieba. W scenach przedstawiających wnętrza oświetlenie to powinno być raczej wyłączone — należy wartość parametru Sky Light Intensity ustawić na 0. ■ Bounce Boost (wzmocnienie odbicia). Za pomocą tego suwaka można symulować większą liczbę odbić bez ponoszenia kosztów (czasu trwania obliczeń) związanych ze zwiększeniem wartości parametru Bounces. Przydaje się, gdy scena jest zbyt ciemna i chcielibyśmy ją rozjaśnić przez wzmocnienie oświetlenia pośredniego. ■ Bounce Intensity (intensywność odbicia). Parametr ten działa jak mnożnik i pozwala równomiernie zwiększać lub zmniejszać ogólną jasność oświetlenia pośredniego. Wartość 1 niczego nie zmienia, wartość 2 zwiększa jasność dwukrotnie, wartość 0,5 zmniejsza ją o połowę itd. Podobnie jak Bounce Boost, przydaje się do intensyfikowania lub tonowania oświetlenia pośredniego.
261
262
Rozdział 9
Wypalanie
■ Final Gather Rays (promienie Final Gather). W tym polu można ustalić całkowitą liczbę promieni wysyłanych w kierunku sceny w celu ustalenia jej oświetlenia. Im wyższa będzie ta liczba, tym dokładniejsze będzie oświetlenie. Często jednak wystarcza pozostawienie wartości domyślnej. Pozostałe ustawienia dotyczą m.in. okluzji otoczenia, czyli efektu będącego rezultatem oświetlenia pośredniego. Gdy światło odbija się od otoczenia, to z oczywistych powodów częściej pada na przestrzenie duże i odsłonięte niż na małe i ciasno osłonięte innymi powierzchniami, np.: w szparach, szczelinach między blisko położonymi obiektami, zakamarkach, kątach utworzonych przez ściany, podłogi i sufity oraz w innych tego typu miejscach. W rezultacie miejsca te są zacienione, a zjawisko to jest nazywane cieniem kontaktowym (contact shadow). Okluzja otoczenia (ambient occlusion) jest sposobem na symulowanie tego zjawiska (rysunek 9.12). Zastosowanie jej pomaga wzmocnić trójwymiarowość obrazu. Aby ją włączyć, wystarczy za pomocą suwaka Ambient Occlusion ustawić jakąkolwiek wartość większą od 0. Im większą wartość ustawisz (maksymalnie 1), tym efekt będzie mocniejszy (rysunek 9.13).
Rysunek 9.12. Ten sam obiekt z wyłączoną okluzją otoczenia (A) i z włączoną (B). Okluzja otoczenia symuluje obecność cieni kontaktowych Źródło: Unity Technologies.
Z suwakiem Ambient Occlusion skojarzone są parametry Max Distance (maksymalna odległość) i Contrast (kontrast), za pomocą których można dostosować efekt do własnych upodobań. Pierwszy z nich wpływa na rozległość cienia kontaktowego, a drugi decyduje o jego intensywności. Jeśli chcesz spotęgować efekt cieni kontaktowych, zastosuj wyższe wartości tych parametrów.
Wypalanie map światła
Rysunek 9.13. Włączanie okluzji otoczenia Źródło: Unity Technologies.
Wypalanie map światła Na rysunku 9.14 pokazane są ustawienia, jakie zastosowałem przy wypalaniu mapy światła w trybie Single Lightmaps. Jeśli już poustawiałeś wszystkie parametry według własnego uznania, kliknij przycisk Bake Scene (wypal scenę) w dolnej części zakładki Bake edytora Lightmapping. Spowoduje to uruchomienie specjalnego modułu o nazwie Beast, który najpierw wyznaczy oświetlenie bezpośrednie i pośrednie wraz z okluzją otoczenia, a następnie wygeneruje zestaw map światła. Cały ten proces może zająć od kilku minut do wielu godzin w zależności od: liczby źródeł światła w scenie, rozdzielczości map światła, powierzchni siatek, parametrów oświetlenia pośredniego, mocy obliczeniowej komputera i wielu innych czynników. Gdy się zakończy, scena zostanie oświetlona za pomocą wygenerowanych map światła (rysunek 9.15). Aby się przekonać, że oświetlenie rzeczywiście jest teraz wypalone w teksturach nałożonych na siatki, wyłącz na chwilę wszystkie lampy zainstalowane w scenie. Oświetlenie sceny nie powinno się w ogóle zmienić! Uwa ga
Beast jest nazwą wyspecjalizowanego w mapowaniu światła silnika, którego Unity używa do generowania map światła.
263
264
Rozdział 9
Wypalanie
Rysunek 9.14. Finalne ustawienia parametrów mapowania światła Źródło: Unity Technologies.
Rysunek 9.15. Scena oświetlona za pomocą map światła Źródło: Unity Technologies.
Wypalanie map w Blenderze
Wypalone mapy światła zostaną dodane do projektu jako oddzielny zasób tekstur (rysunek 9.16), a podczas zapisywania sceny zostaną umieszczone w automatycznie wygenerowanym folderze.
Rysunek 9.16. Mapy światła stanowią odrębny element projektu Źródło: Unity Technologies.
Zwróć uwagę na rozkład pikseli w mapach światła widocznych w oknie podglądowym zajmującym dolną część panelu Inspector. Upewnij się, że są to tekstury maksymalnie zoptymalizowane. Jeśli rozdzielczość mapy światła ustawiłeś zbyt niską w stosunku do powierzchni siatek, mapa będzie zawierała dużo jednakowych pikseli niewiele wnoszących do ostatecznego rezultatu. Tekstury będą automatycznie powiększane do rozmiarów będących kolejną potęgą liczby 2, aby pomieścić wszystkie piksele potrzebne dla mapy światła bez skalowania czy ponownego próbkowania. Czasem jednak tekstura okazuje się większa niż trzeba, a to oznacza, że jej powierzchnia nie jest optymalnie wykorzystana — np. wtedy, gdy mapa ma więcej niż 512×512 pikseli, ale nie jest w stanie zapełnić tekstury o wymiarach 1024×1024 (rysunek 9.17). W takiej sytuacji zmień wartość parametru Resolution i ponownie wypal mapę światła, aby zapełnić jak największą powierzchnię tekstury.
Wypalanie map w Blenderze Czasami lepiej jest — a nawet trzeba — ominąć system mapowania światła w Unity i wygenerować mapy światła w Blenderze. Wtedy zazwyczaj wykonujemy oddzielne tekstury z mapami okluzji otoczenia, podstawowymi mapami renderingu i innymi rodzajami map oświetleniowych. Następnie w edytorze graficznym, takim jak GIMP czy Photoshop, składamy z nich jedną teksturę i dopiero w takiej postaci importujemy do Unity.
265
266
Rozdział 9
Wypalanie
Rysunek 9.17. Tekstura mapy światła o rozdzielczości równej 10. Taka wartość jest zbyt mała i prowadzi do zmarnowania dużej części tekstury Źródło: Unity Technologies.
Powody, dla których często stosuje się takie podejście, są dwojakiego rodzaju: ■ W darmowej wersji Unity nie wszystkie funkcje związane z wypalaniem map są dostępne. Nie ma tam obsługi oświetlenia pośredniego i okluzji otoczenia. W takiej sytuacji najprostszym i najbardziej oczywistym rozwiązaniem — pomijając kupno pełnej wersji programu Unity — jest wypalenie tych wszystkich map w Blenderze i potem połączenie ich w jedną mapę światła rozproszonego (diffuse map). ■ Niekiedy, nawet jeśli mamy Unity Pro, trzeba ręcznie sterować procesem wypalania. Najczęściej taka potrzeba zachodzi, gdy stosujemy własne shadery i typy materiałów lub chcemy poprawić wydajność przez zmniejszenie liczby tekstur przetrzymywanych w pamięci — wszystkie efekty oświetleniowe pakujemy wtedy do jednej tekstury z mapą światła rozproszonego. W tym podrozdziale zajmiemy się właśnie ręcznym wypalaniem map światła za pomocą Blendera i GIMP-a. Aby ułatwić sobie śledzenie kolejnych etapów tej procedury, możesz wczytać do Blendera zawartość powiązanego z książką pliku Rozdział09/blender_ environment/env_final.blend. Jest to zestaw modułów do budowy scenerii laboratorium naukowego (rysunek 9.18). Oczywiście techniki wypalania, które zaprezentuję, będą mogły być stosowane także w odniesieniu do innych siatek. Ten zestaw ma Ci tylko pomóc w nauce posługiwania się odpowiednimi narzędziami.
Wypalanie map w Blenderze
Rysunek 9.18. Przygotowanie do wypalania tekstur oświetleniowych Źródło: Blender.
Aby własnoręcznie wypalić teksturę, wykonaj następujące czynności: 1. Upewnij się, że jako domyślny renderer systemowy włączony jest wewnętrzny renderer Blendera o nazwie Blender Render (a nie Cycles lub Blender Game). W tym celu rozwiń na pasku Info (informacje) listę z rendererami i wybierz pozycję Blender Render (rysunek 9.19).
Rysunek 9.19. Ustawianie systemu renderowania w Blenderze Źródło: Blender.
2. Zacznij od wypalenia mapy okluzji otoczenia dla zestawu środowiskowego. Będzie to odrębna tekstura w skali szarości reprezentująca dane dotyczące efektu okluzji otoczenia będącego rezultatem oświetlenia pośredniego. Utwórz więc nową pustą
267
268
Rozdział 9
Wypalanie
teksturę, która przyjmie te dane, gdy efekt zostanie wyrenderowany. Zaznacz siatkę zestawu w oknie widokowym, po czym wciśnij klawisze Ctrl+A, żeby zaznaczyć wszystkie ścianki w siatce. Następnie zmień układ interfejsu na UV Editing (edycja współrzędnych UV) i na pasku narzędziowym kliknij przycisk Create a new image (utwórz nowy obraz) — rysunek 9.20.
Rysunek 9.20. Utwórz nową teksturę, aby zapisać w niej mapę okluzji otoczenia Źródło: Blender.
3. Otworzy się okno dialogowe z ustawieniami. Wpisz nazwę nowego obrazu (ja wpisałem AO_Map), ustaw biały kolor tła i wyłącz opcję Alpha (przezroczystość alfa nie będzie potrzebna w teksturze z mapą okluzji otoczenia) — rysunek 9.21. Następnie kliknij przycisk OK. Wszystkie ścianki dopasują się kolorystycznie do nowej, białej tekstury.
Rysunek 9.21. Parametry tekstury tworzonej dla okluzji otoczenia Źródło: Blender.
4. W panelu Properties otwórz zakładkę Render (renderowanie). (W zależności od aktualnie wybranego układu interfejsu być może będziesz musiał wrócić do układu domyślnego. O zmienianiu układów interfejsu Blendera możesz przeczytać w rozdziale 2., w punkcie „Konfigurowanie interfejsu Blendera”). Przewiń zawartość za-
Wypalanie map w Blenderze
kładki Render, aby uzyskać dostęp do rolety Bake (wypalanie), rozwiń tam listę Bake Mode (tryb wypalania) i wybierz Ambient Occlusion (okluzja otoczenia) — rysunek 9.22. Włącz też opcję Normalized (znormalizowane), aby materiały przypisane obiektom nie miały wpływu na renderowane efekty przez wywoływanie odbić i załamywanie światła. Włączenie tej opcji spowoduje, że przy renderowaniu efektów okluzji otoczenia właściwości materiałów nie będą uwzględniane.
Rysunek 9.22. Ustawianie parametrów wypalania na zakładce Render panelu Properties Źródło: Blender.
5. W panelu Properties otwórz zakładkę World (świat) i zaznacz pola wyboru w roletach Ambient Occlusion, Environment Lighting (oświetlenie środowiska) i Indirect Lighting (oświetlenie pośrednie). Spowoduje to, że wyrenderowana mapa światła będzie zawierała szeroki zakres efektów oświetlenia pośredniego. Na koniec włącz w rolecie Gather (zbieranie) opcję Approximate (przybliżenie) — rysunek 9.23. 6. Aby uruchomić proces wypalania, zaznacz siatkę w oknie widokowym, otwórz ponownie zakładkę Render i kliknij przycisk Bake (wypal). Nie zapomnij o zaznaczeniu siatki, ponieważ proces wypalania obejmie tylko zaznaczone obiekty. Rezultat wypalania zostanie wyświetlony w oknie UV/Image Editor (edytor współrzędnych UV i obrazów). Tutaj też możesz zapisać uzyskany obraz jako zwykłą teksturę. W tym celu z menu edytora wybierz polecenie Image/Save as Image (obraz/zapisz jako obraz) — rysunek 9.24.
269
270
Rozdział 9
Wypalanie
Rysunek 9.23. Ustawianie opcji wypalania na zakładce World Źródło: Blender.
Rysunek 9.24. Zapisywanie mapy okluzji otoczenia jako obrazu Źródło: Blender.
Komponowanie renderingów w GIMP-ie
Oczywiście wypalać możesz także inne mapy światła, włącznie z mapami odblasków, normalnych, cieni, a nawet pełne renderingi zawierające w jednym obrazie wszystkie efekty oświetleniowe. Większe korzyści daje jednak wypalanie poszczególnych efektów oddzielnie — jeden obraz dla okluzji otoczenia, drugi dla odblasków, trzeci dla cieni itd. Otóż przez to, że każdy efekt trafia do odrębnego pliku, możesz je potem swobodnie komponować według własnego uznania, a więc zyskujesz dodatkową możliwość wpływania na ostateczny wygląd sceny. I właśnie procesem komponowania efektów oświetleniowych zajmiemy się w następnym podrozdziale.
Komponowanie renderingów w GIMP-ie Za pomocą blenderowej funkcji wypalania możesz zapisać poszczególne przebiegi renderujące w odrębnych plikach — przebiegi takie jak okluzja otoczenia, odblaski, cienie, rozpraszanie światła itp. Gdy wykonujesz pełny rendering, Blender łączy rezultaty tych wszystkich przebiegów w jeden skonsolidowany obraz. Jest to rozwiązanie na pewno szybkie i wygodne, ale pozbawia Cię dodatkowej możliwości wpływania na sam proces łączenia wygenerowanych efektów. Jeśli zapiszesz je w oddzielnych plikach, będziesz mógł je umieścić na osobnych warstwach w edytorze graficznym, takim jak GIMP czy Photoshop, a to da Ci możliwość łączenia ich na różne sposoby przy użyciu trybów mieszania, poziomów krycia i innych parametrów. Omówię ten proces na przykładzie łączenia mapy okluzji otoczenia ze standardową teksturą rozproszenia światła. Do „współpracy” Blendera z GIMP-em wrócimy jeszcze w następnym rozdziale. Uwa ga
Program GIMP można pobrać w wersjach dla systemów Windows, Mac i Linux za darmo z witryny: http://www.gimp.org/. Do zrozumienia treści prezentowanych w tym podrozdziale potrzebna jest przynajmniej podstawowa znajomość tej aplikacji. Więcej informacji na jej temat znajdziesz jeszcze w rozdziale 10. i w dodatku A. Na rysunku 9.25 widać otwartą w GIMP-ie teksturę z mapą rozproszenia światła na elementach zestawu środowiskowego. Plik z tą teksturą (env_diffuse.png) znajdziesz w materiałach powiązanych z książką, w folderze Rozdział09/GIMP. Tekstura ma wymiary 1024×1024 piksele i zawiera dane odnośnie do rozpraszania światła na powierzchni obiektów tworzących środowisko gry. Nie obejmuje informacji na temat okluzji otoczenia i oświetlenia pośredniego. Reprezentuje teksturę, jaka w Unity byłaby przypisana siatkom przed rozpoczęciem procesu mapowania światła. W GIMP-ie została umieszczona na warstwie tła. Mapa okluzji otoczenia (o takich samych wymiarach, czyli 1024×1024 pikseli) utworzona w poprzednim ćwiczeniu przejęła współrzędne UV od siatek elementów środowiska. Oznacza to, że ma nie tylko takie same wymiary jak mapa rozproszenia światła, ale również współrzędne UV, a zatem można je ze sobą łączyć. Aby mapę okluzji umieścić
271
272
Rozdział 9
Wypalanie
Rysunek 9.25. Najpierw otwórz teksturę z mapą rozpraszania światła Źródło: GNU Project.
na odrębnej warstwie w jednym pliku z mapą rozproszenia, po prostu przeciągnij jej plik z Eksploratora (Windows) lub Findera (Mac OS) do okna GIMP-a, w którym jest już otwarty obraz z mapą rozproszenia. GIMP automatycznie umieści ją na nowej warstwie. Gdyby się okazało, że mapa okluzji nie została umieszczona na szczycie stosu warstw, przesuń ją w górę za pomocą strzałek widocznych na pasku narzędziowym u dołu okna dialogowego Layers (Warstwy) — rysunek 9.26. Znajdująca się na szczycie stosu warstw mapa okluzji otoczenia zakrywa całkowicie leżącą niżej mapę rozproszenia światła. Nie o takie połączenie jednak chodzi. Warstwy te muszą być ze sobą zmieszane, a nie nałożone jedna na drugą. Mapa okluzji jest obrazem w skali szarości i z punktu widzenia technik komponowania ma to istotne znaczenie. Możesz bowiem od razu przypisać jej tryb mieszania Multiply (Mnożenie) i skutecznie zmieszać jej zawartość z zawartością mapy rozproszenia (rysunek 9.27). W rezultacie otrzymasz jeden obraz zawierający informacje, które w Unity byłyby rozłożone na dwóch odrębnych mapach. Zmniejszysz więc liczbę używanych w grze tekstur i tym samym obniżysz jej zapotrzebowanie na pamięć komputera.
Komponowanie renderingów w GIMP-ie
Rysunek 9.26. Przesuń warstwę okluzji otoczenia na szczyt stosu warstw Źródło: GNU Project.
Rysunek 9.27. Mapa okluzji otoczenia skomponowana z mapą rozpraszania światła Źródło: GNU Project.
273
274
Rozdział 9
Wypalanie
Wypalanie oświetlenia dynamicznego z użyciem próbników światła Mapowanie światła najlepiej sprawdza się w odniesieniu do obiektów statycznych (nieruchomych), takich jak elementy scenerii i stałe rekwizyty. Zupełnie inaczej sprawa wygląda w przypadku obiektów dynamicznych, które w trakcie gry przemieszczają się z miejsca na miejsce, zmieniając położenie względem otaczających je źródeł światła. Oznacza to, że ich oświetlenie zmienia się wraz z każdym ich ruchem. Unity ma wbudowane dwa sposoby obchodzenia się z takimi obiektami. Pierwszy sprowadza się do zupełnej rezygnacji z wypalania jakichkolwiek map i całkowitego przejścia na obliczanie oświetlenia w czasie rzeczywistym, zwłaszcza gdy źródła światła mają charakter kierunkowy, reflektorowy lub punktowy. Sposób drugi polega na zastosowaniu próbników światła (light probes) wygenerowanych (wypalonych) na etapie tworzenia gry w celu aproksymacji oświetlenia scenicznego. W czasie rzeczywistym pozostaje tylko odpowiednia interpolacja zawartych w nich informacji i to już pozwala dość dobrze symulować oświetlenie obiektów dynamicznych. Zanim przejdziemy do wykonywania praktycznych ćwiczeń z próbnikami światła, przygotuj kompletną scenę lub cały poziom ze wszystkimi źródłami światła. Do celów ćwiczeniowych możesz wykorzystać zawartość pliku Rozdział09/Light_Probes_Start, który znajdziesz w materiałach powiązanych z książką. Jest to scena z zestawem środowiskowym zaopatrzonym w mapy oświetlenia statycznego i dynamiczną postacią, której oświetlenie jest obliczane w czasie rzeczywistym. Postać tę można przemieszczać po całym poziomie w sposób typowy dla gier z perspektywą trzeciej osoby (rysunek 9.28). Aby poprawić wydajność, zastąpimy istniejący sposób oświetlenia postaci metodą opartą na próbnikach światła.
Rysunek 9.28. Scena gotowa do wygenerowania próbników światła Źródło: Unity Technologies.
Wypalanie oświetlenia dynamicznego z użyciem próbników światła
Uwa ga
Próbniki światła są dostępne tylko w Unity Pro. W wersji darmowej można uzyskać podobne rezultaty przez zastosowanie własnych shaderów lub utworzenie klasy, która uśredni wartości oświetlenia w scenie i dopasuje kolor obiektu do barwy pobliskiego źródła światła. Twórcy gier starają się uzyskać realistyczne oświetlenie ruchomych siatek przez staranne rozmieszczenie w scenie zestawu powiązanych ze sobą węzłów (próbników) sieci oświetleniowej. W fazie realizacyjnej każdy próbnik rejestruje parametry oświetlenia istniejącego w miejscu, w którym się znajduje. Zapisuje kierunek, natężenie i barwę światła docierającego do niego z otaczających go źródeł. Potem, w czasie trwania gry, próbniki te automatycznie pocieniują i pokolorują każdą dynamiczną siatkę, która znajdzie się w ich zasięgu. Oświetlenie siatki będzie rezultatem interpolacji danych pochodzących od wszystkich otaczających ją próbników. Żeby użyć próbników, trzeba je najpierw utworzyć i odpowiednio porozstawiać. Oto, jak należy to zrobić. 1. Utwórz nowy pusty obiekt i ustaw go w środku globalnego układu współrzędnych. 2. Dodaj składnik Light Probe Group (grupa próbnika światła). Aby taki składnik dodać, wybierz polecenie Component/Rendering/Light Probe Group (składnik/renderowanie/ grupa próbnika światła) — rysunek 9.29.
Rysunek 9.29. Do pustego obiektu dodaj komponent Light Probe Group, aby wszystkie dodawane próbniki utworzyły jedną sieć Źródło: Unity Technologies.
275
276
Rozdział 9
Wypalanie
3. Dodaj do sceny pierwszy próbnik. W tym celu kliknij przycisk Add Probe (dodaj próbnik) w rolecie Light Probe Group panelu Inspector. 4. Za pomocą standardowych narzędzi transformacyjnych zaznacz i ustaw próbnik w odpowiednim miejscu. 5. Powtarzając czynności z etapów 3. i 4., utwórz całą sieć próbników rozstawionych w różnych miejscach sceny. Próbniki automatycznie połączą się ze sobą w jedną sieć. Cała sztuka polega na tym, żeby jak najmniejszą liczbą próbników obstawić wszystkie ważne miejsca, czyli te, w których występują istotne efekty oświetleniowe. Oznacza to, że najwięcej próbników trzeba umieścić w obszarach o zróżnicowanym oświetleniu, a tam, gdzie światło jest w miarę jednorodne, można ich wstawić mniej. Sieć, jaką ja stworzyłem, jest pokazana na rysunku 9.30.
Rysunek 9.30. Rozstaw próbniki światła w całym poziomie, aby zarejestrować jak najlepsze przybliżenie oświetlenia Źródło: Unity Technologies.
Uwa ga
Nie ustawiaj wszystkich próbników na tej samej wysokości (z tą samą wartością współrzędnej Y). Jeśli poprzesuwasz je również w pionie, rozkład zrealizowanego za ich pomocą oświetlenia będzie bardziej przestrzenny. 6. Samo rozmieszczenie sieci próbników w scenie nie spowoduje, że docierające do nich światło zostanie zarejestrowane. Po utworzeniu całej sieci musisz otworzyć edytor Lightmapping, kliknąć w nim zakładkę Bake, rozwinąć listę Bake Probes (wypalanie próbek) i zaznaczyć w niej opcję Bake Probes (rysunek 9.31). Wypalanie próbek światła jest szybsze niż mapowanie światła, ale i tak w przypadku dużych poziomów z wieloma próbnikami może potrwać kilka minut.
Wypalanie oświetlenia dynamicznego z użyciem próbników światła
Rysunek 9.31. Aby wypalić oświetlenie sceny, włącz w edytorze Lightmapping opcję Bake Probes Źródło: Unity Technologies.
7. Proces wypalania osadza w próbnikach istotne dane oświetleniowe, które potem są na bieżąco interpolowane i nakładane na dynamiczne siatki, co w sumie daje efekt podobny do tego, jaki można uzyskać na drodze obliczeń w czasie rzeczywistym. Domyślnie jednak żadna siatka nie jest w ten sposób oświetlana. Aby to nastąpiło, trzeba taką siatkę wyraźnie wskazać. W tym celu zaznacz wszystkie dynamiczne siatki i w rolecie Mesh Renderer (renderer siatki) lub Skinned Mesh Renderer (renderer siatki powleczonej skórą) zaznacz pole wyboru Use Light Probes (użyj próbników światła). (W przypadku postaci z ćwiczeniowego pliku należy zaznaczyć siatkę retop_char, która ma komponent Skinned Mesh Renderer, co widać na rysunku 9.32). Teraz postać otrzyma na wpół wypalone oświetlenie od próbników światła. Znakomicie! Umiesz już optymalizować oświetlenie siatek dynamicznych.
277
278
Rozdział 9
Wypalanie
Rysunek 9.32. Aby oświetlić siatkę dynamiczną za pomocą próbników, włącz opcję Use Light Probes Źródło: Unity Technologies.
Wypalanie nawigacji Ostatnia technika wypalania, jaką rozpatrujemy w tym rozdziale, ma związek z wyznaczaniem tras i nawigacją. Wszyscy wrogowie i postacie typu NPC powinny przecież wykazywać pewien poziom inteligencji. Nie powinny wpadać na ściany, krzesła czy zamknięte drzwi, a na dodatek powinny wybierać najkrótszą i najsensowniejszą drogę do miejsca, w którym chciałyby się znaleźć. Zachowanie takie można im przypisać za pomocą Unity na wiele sposobów. W wersjach wcześniejszych niż 3.5 najczęściej stosowana była technika oparta na węzłach i wyznaczaniu tras za pomocą algorytmu A* lub Dijkstry. Później jednak popularniejsze stało się stosowanie funkcji wbudowanych w Unity, a szczególnie siatek nawigacyjnych. Podobnie jak próbniki światła siatki nawigacyjne pozwalają tylko na połowiczne wykorzystanie techniki wypalania, bo przecież muszą w naturalny sposób współpracować z siatkami dynamicznymi. Sporą część danych nawigacyjnych można wypalić w siatce nawigacyjnej, ale i tak Unity musi pewne obliczenia przeprowadzać w czasie rzeczywistym. Aby zobaczyć, jak to działa w praktyce, wykonaj opisane poniżej ćwiczenie. Zacznij od wczytania pliku Rozdział09/Navigation_Start. Jego zawartość stanowią statyczne środowisko i postać gracza. Obiekty te będą pełniły funkcję wrogów. Będą ciągle podążały za postacią gracza, starając się omijać wszelkie napotkane przeszkody. Do kierowania ich ruchem użyjemy siatki nawigacyjnej. Wstępny układ tej przykładowej sceny jest pokazany na rysunku 9.33.
Wypalanie nawigacji
Rysunek 9.33. Scena przygotowana do ćwiczenia z użyciem siatek nawigacyjnych. Klocki będą musiały ciągle odnajdywać drogę do postaci gracza, gdy ta będzie zmieniała położenie Źródło: Unity Technologies.
Zanim zaczniesz wypalać nawigację, musisz najpierw wygenerować siatkę nawigacyjną. Jest to niewidoczna w czasie gry niskorozdzielcza siatka wytyczająca obszary, po których obiekty mogą się poruszać. To właśnie na jej podstawie wrogowie będą wytyczać swoje ścieżki w czasie rzeczywistym. Aby wygenerować siatkę nawigacyjną, wykonaj następujące czynności: 1. Za pomocą polecenia Window/Navigation (okno/nawigacja) otwórz edytor Navigation. Podobnie jak edytor Lightmapping ten też warto zadokować przy prawej krawędzi interfejsu, aby nie zasłaniał okna widokowego (rysunek 9.34).
Rysunek 9.34. Edytor Navigation zadokowany obok panelu Inspector Źródło: Unity Technologies.
2. W edytorze Navigation kliknij przycisk Bake.
279
280
Rozdział 9
Wypalanie
3. Upewnij się, że w oknie Navmesh Display (wyświetlanie siatki nawigacyjnej) widocznym w prawym dolnym rogu okna widokowego włączona jest opcja Show NavMesh (pokaż siatkę nawigacyjną). 4. Kliknij przycisk Bake, aby wygenerować siatkę nawigacyjną dla bieżącej sceny. Pojawi się ona tuż przy podłodze i będzie miała kolor niebieski. W trakcie gry nie będzie widoczna; teraz wyświetlamy ją tylko dla celów diagnostycznych (rysunek 9.35).
Rysunek 9.35. Wygeneruj wstępną siatkę nawigacyjną dla sceny Źródło: Unity Technologies.
Po wygenerowaniu siatki nawigacyjnej z domyślnymi parametrami przyjrzyj się jej uważnie. Mogą się pojawić dwa istotne problemy: ■ Siatka nawigacyjna może być przesunięta w pionie w stosunku do podłogi sceny, jakby unosiła się w powietrzu (rysunek 9.36).
Rysunek 9.36. Siatka nawigacyjna wydaje się unosić w powietrzu Źródło: Unity Technologies.
Wypalanie nawigacji
■ Siatka może za bardzo odsuwać się od ścian i w wąskich korytarzach może jej w ogóle nie być (rysunek 9.37).
Rysunek 9.37. Siatka odsunięta od ścian Źródło: Unity Technologies.
Aby obniżyć siatkę do poziomu podłogi, rozwiń w edytorze Navigation roletę Advanced (zaawansowane) i zmniejsz wartość parametru Height Inaccuracy (niedokładność wysokości). Ja zmniejszyłem ją z 8 do 1. W przypadku tej sceny taka wartość jest wystarczająca (rysunek 9.38).
Rysunek 9.38. Obniżanie siatki nawigacyjnej do poziomu podłogi Źródło: Unity Technologies.
281
282
Rozdział 9
Wypalanie
Uwa ga
Po zmodyfikowaniu parametrów siatki nawigacyjnej musisz ją na nowo wygenerować, klikając ponownie przycisk Bake. Drugi problem — odsunięcie siatki nawigacyjnej od ścian — można rozwiązać przez zmianę parametru Radius (promień) dostępnego na zakładce Bake edytora Navigation. Nie wolno jednak zapominać o powodach, dla których to odsunięcie w ogóle powstało. Otóż zewnętrzne krawędzie siatki nawigacyjnej wyznaczają skrajne możliwe położenia środków transformacji tych obiektów, które mają się poruszać w obszarze wyznaczonym przez siatkę, więc pewien odstęp między tymi krawędziami a ścianami jest konieczny, żeby obiekty podczas przemieszczania się nie przenikały częściowo przez ściany. Raczej rzadkie są przypadki, kiedy siatka może ściśle przylegać do stałych elementów środowiska. Niemal zawsze jakieś odsunięcie jest potrzebne. Pozostaje tylko pytanie: jak duże powinno być? Odpowiedź zależy od rozmiarów siatek NPC, struktury ścian i innych elementów środowiska, a także od sposobu, w jaki gra ma się toczyć. Krótko mówiąc: wartość parametru Radius trzeba dobierać metodą prób i błędów, aż znajdzie się taką, która będzie odpowiadała naszym potrzebom. Ja zmniejszyłem ją z 0,5 do 0,4 (rysunek 9.39).
Rysunek 9.39. Parametr Radius decyduje o wielkości przerwy między siatką nawigacyjną a ścianami Źródło: Unity Technologies.
Wygenerowanie siatki nawigacyjnej to dopiero pierwszy krok na drodze do skonstruowania mechanizmu wyznaczania tras. Następny będzie polegał na takim skonfigurowaniu obiektów NPC, aby z tej siatki faktycznie korzystały. W tym celu trzeba każdy taki obiekt wyposażyć w komponent o nazwie Nav Mesh Agent (agent siatki nawigacyjnej). W terminologii systemów wyznaczania tras pojęcie agent oznacza byt, który może w sposób inteligentny przejść z jednego punktu do drugiego, omijając napotkane po drodze przeszkody. Właśnie takimi inteligentnymi agentami potrafiącymi znaleźć wła-
Wypalanie nawigacji
ściwą drogę trzeba uczynić wszystkie cztery prostopadłościenne klocki. W Unity można to zrobić bardzo łatwo — wystarczy każdemu z klocków przypisać wspomniany komponent przez wybranie polecenia Component/Navigation/Nav Mesh Agent (rysunek 9.40).
Rysunek 9.40. Dodawanie komponentu Nav Mesh Agent potencjalnym wrogom Źródło: Unity Technologies.
Następnie trzeba te komponenty we wszystkich czterech klockach odpowiednio skonfigurować. Ja ustawiłem w nich Speed (szybkość) na 2 (jednostki globalne na sekundę) i Stopping Distance (dystans hamujący) na 3. Ten drugi parametr określa odległość (w jednostkach globalnych), na jaką agent może się zbliżyć do celu, zanim się zatrzyma. Wartość 3 zapobiegnie wchodzeniu klocków na postać gracza, gdy ją już dogonią. Po prostu klocek zatrzyma się, gdy jego odległość od postaci gracza stanie się równa bądź mniejsza od 3 jednostek (rysunek 9.41).
Rysunek 9.41. Ustawianie parametrów komponentu Nav Mesh Agent Źródło: Unity Technologies.
Jeśli teraz uruchomisz grę, okaże się, że mimo skonfigurowania komponentów agentowych klocki nadal stoją nieruchomo i wcale nie mają zamiaru ścigać postaci gracza. Nic nie zmusza ich do ruszenia w drogę. Żeby to zmienić, musisz napisać odpowiedni skrypt i przypisać go wszystkim klockom. Zawartość takiego skryptu przedstawia listing 9.1.
283
284
Rozdział 9
Wypalanie
Listing 9.1. Skrypt EnemyAI.cs — sztuczna inteligencja wroga using UnityEngine; using System.Collections; public class EnemyAI : MonoBehaviour { //Komponent Nav Mesh Agent. private NavMeshAgent NMAgent = null; //Referencja do postaci gracza. private Transform PlayerTransform = null; //Inicjalizacja komponentu agentowego i postaci gracza. void Start () { //Pobierz komponent Nav Mesh Agent.
NMAgent = GetComponent(); //Pobierz transformacje postaci gracza.
PlayerTransform = GameObject.FindGameObjectWithTag("Player").transform; } //Aktualizacja wywoływana w każdej klatce. void Update () { //Powiąż wroga z postacią gracza.
NMAgent.SetDestination(PlayerTransform.position); } }
W funkcji Update() wywoływana jest metoda NMAgent.SetDestination(), która śledząc zmiany położenia postaci gracza jako celu, ciągle aktualizuje prowadzącą do niego trasę. Powyższy skrypt przypisany do obiektu NPC spowoduje, że ten rozpocznie swój ruch w kierunku postaci gracza i będzie wszędzie za nią podążał. Na rysunku 9.42 widać, że to działa!
Rysunek 9.42. Naprzód! Agenci NPC ścigają gracza Źródło: Unity Technologies.
Podsumowanie
Uwa ga
Więcej informacji na temat komponentu Nav Mesh Agent znajdziesz w dokumentacji programu Unity pod adresem: http://docs.unity3d.com/Documentation/ScriptReference/NavMeshAgent.SetDestination.html.
Podsumowanie Głównym tematem tego rozdziału było pojęcie wypalania, czyli wcześniejszego przygotowywania takich elementów jak oświetlenie czy wytyczanie trasy w celu skrócenia obliczeń wykonywanych w czasie rzeczywistym podczas działania gry. Najczęściej wypalane są mapy światła i tekstury, ale w zasadzie wypalać można — jeśli nie całkowicie, to przynajmniej częściowo — wszystkie elementy gry, włącznie z oświetleniem dynamicznym i wyznaczaniem tras. Unity oferuje również możliwość wypalania widoczności obiektów z uwzględnieniem ich wzajemnego zasłaniania się (occlusion culling), ale to zagadnienie wykracza poza ramy książki. Wiele ważnych technik wypalania udało mi się jednak zaprezentować. Pokazałem też obszary, w których niezwykle pomocne mogą się okazać programy takie jak Blender i GIMP. Z tego wszystkiego każdy twórca gier powinien wyciągnąć dość istotny wniosek: jeśli tylko coś da się wypalić, to należy wypalać, bo niemal zawsze jest to korzystne ze względów wydajnościowych.
285
286
Rozdział 9
Wypalanie
R OZ DZ I AŁ 10
U NITY , B LENDER I INNE PROGRAMY
Jeśli patrzysz długo w otchłań, ona również zajrzy w ciebie. — Friedrich Wilhelm Nietzsche Po przeczytaniu tego rozdziału powinieneś: ■ znać dodatkowe programy mogące współpracować z Unity i Blenderem; ■ umieć ocenić przydatność innych programów jako pośredników między Blenderem a Unity; ■ znać jeszcze inne aspekty współpracy Blendera z Unity; ■ wiedzieć, na jakie narzędzia możesz jeszcze liczyć. Powoli zbliżamy się do zakończenia książki, ale zanim się rozstaniemy, chciałbym pokrótce omówić inne programy, które nie kosztują nic lub stosunkowo niewiele, a mogą stanowić znakomite uzupełnienie duetu Blender – Unity. A zatem zobaczmy!
Inne programy Jednym z głównych celów tej książki jest pokazanie olbrzymich możliwości, jakie Blender i Unity mają w zakresie tworzenia gier. Blender oferuje mnóstwo wysokiej jakości narzędzi do modelowania, renderowania i animowania elementów gry, które to elementy z kolei doskonale integrują się ze środowiskiem znakomitego silnika gier, jakim jest Unity. Przy użyciu formatów FBX i DAE można łatwo i pewnie przenosić między tymi aplikacjami niemal wszystkie rodzaje danych (siatki, mapy UV, szkielety, animacje itp.). Unity nawet obsługuje rdzenny format Blendera (.blend) i można z tego korzystać, ale w rozdziale 2. chyba dostatecznie jasno pokazałem, że raczej nie powinno się tego robić. Niezależnie od tego potężnego duetu istnieje wiele innych programów, które również 287
288
Rozdział 10
Unity, Blender i inne programy
mogą się okazać przydatne. W następnych podrozdziałach omówię krótko kilka takich pozycji i wskażę, w czym mogą być użyteczne. Przy wyborze tych programów kierowałem się tym, że mają to być programy tanie lub darmowe i powinny działać na większości platform komputerowych używanych współcześnie do tworzenia gier, a przede wszystkim na tych z systemami Windows, Mac i Linux. Nie chodziło mi też o promowanie takiego czy innego programu ze względów ideologicznych lub tylko dlatego, że jest darmowy. Starałem się być wierny idei niezależności, która przyświecała mi podczas pisania tej książki i kazała szukać narzędzi na poziomie profesjonalnym, a przy tym dostępnych dla wszystkich twórców niezależnie od zasobności ich portfela, działających na wielu platformach i dopuszczonych do użytku zarówno prywatnego, jak i komercyjnego.
MakeHuman MakeHuman (http://www.makehuman.org/) jest darmowym programem do generowania proceduralnych siatek humanoidalnych, włącznie z siatkami typu low-poly. Siatki te można przenosić do Blendera, gdzie mogą być optymalizowane, rigowane, mapowane i animowane, aby w końcu trafić do Unity jako pełnoprawne elementy gry. Krótko mówiąc: MakeHuman może służyć jako narzędzie do szybkiego tworzenia siatek dla postaci występujących w grze. Oczywiście program ten nie jest w stanie wygenerować każdej wymyślonej przez nas postaci (np. trójgłowego potwora), ale nawet w takich sytuacjach może być pomocny w przygotowaniu siatki stanowiącej punkt wyjścia do dalszego modelowania. W MakeHuman (rysunek 10.1) możesz kształtować model postaci za pomocą suwaków, parametrów i opcji. W ten sposób zmienisz wielkość głowy, rozmieścisz na niej elementy twarzy, ustalisz wiek postaci, płeć, wagę, wzrost itp. Program potrafi wyeksportować siatkę wraz z danymi mapowania i rigowania w większości popularnych formatów, włącznie z DAE i FBX.
GIMP GIMP (http://www.gimp.org/) to potężny edytor obrazów cyfrowych, który pod wieloma względami nie ustępuje słynnemu Photoshopowi. Oczywiście obie aplikacje różnią się wyglądem i sposobem obsługi, ale ich możliwości są porównywalne. Obie też nadają się do tworzenia tekstur, przygotowywania graficznych elementów interfejsu i opracowywania materiałów. GIMP ma kilka istotnych ograniczeń, przynajmniej na razie. Przede wszystkim nie obsługuje 16-bitowej głębi kolorów i modelu CMYK. Podobno w przyszłych wersjach ma się to zmienić. Nie są to jednak ograniczenia, które by uniemożliwiały stosowanie GIMP-a przy produkcji gier. Przykładowo: w większości gier używane są kolory zgodne z modelem RGB, a nie CMYK.
GIMP
Rysunek 10.1. Model postaci gotowy do eksportu Źródło: MakeHuman.
GIMP (rysunek 10.2) oferuje imponujący zestaw narzędzi do zaznaczania, malowania, retuszowania, modyfikowania tonów i kolorów, operowania maskami i warstwami itp.
Rysunek 10.2. Edycja obrazu w GIMP-ie na potrzeby gry Mega Bad Code tworzonej przez Wax Lyrical Games Źródło: GNU Project.
Uwa ga
GIMP był moim głównym edytorem obrazów, gdy pracowałem nad grami Bounders and Cads i Mega Bad Code.
289
290
Rozdział 10
Unity, Blender i inne programy
GIMP dobrze współpracuje zarówno z Blenderem, jak i z Unity. Obsługuje szeroką gamę popularnych formatów graficznych, takich jak: PNG, JPG, BMP, TIFF, TGA i wiele innych. Na dodatek Unity można tak skonfigurować, że każda próba otwarcia jakiegokolwiek obrazu (podwójne kliknięcie miniatury w panelu Project) spowoduje automatyczne otwarcie tego obrazu w GIMP-ie. Żeby coś takiego uzyskać, wybierz w programie Unity polecenie File/Preferences (plik/preferencje), aby otworzyć okno dialogowe z preferencjami programu. W oknie tym rozwiń kategorię External Tools (narzędzia zewnętrzne) i na rozwijanej liście Image application (edytor obrazu) zaznacz pozycję GNU Image Manipulation Program, tak jak na rysunku 10.3.
Rysunek 10.3. Ustawianie GIMP-a jako domyślnego edytora obrazów w Unity Źródło: Unity Technologies.
Uwa ga
GIMP obsługuje także mnóstwo dodatków, z których wiele przydaje się przy tworzeniu gier. Jednym z nich jest plugin o nazwie gimp-normalmap (http://code.google.com/p/gimp-normalmap/), który umożliwia generowanie map normalnych na podstawie obrazów w skali szarości. Uwa ga
Popularnym i również darmowym edytorem obrazów jest Paint.NET. Więcej informacji na jego temat znajdziesz pod adresem: http://www.getpaint.net/.
Inkscape Inkscape (https://www.inkscape.org/) stara się na różne sposoby, aby być darmową alternatywą słynnego Illustratora stworzonego i rozwijanego przez firmę Adobe. W przeciwieństwie do programów GIMP i Photoshop, które służą do edycji obrazów rastrowych, Inkscape i Illustrator oferują narzędzia do tworzenia i edycji grafik wektorowych, czyli obrazów będących zbiorami nie pikseli, lecz matematycznie zdefiniowanych kształtów. Obrazy takie, dzięki ich matematycznej naturze, można dowolnie powiększać i pomniejszać bez obawy o najmniejszą chociażby utratę jakości. W przypadku kształtów
Inkscape
wektorowych powiększanie i pomniejszanie sprowadza się do obliczania nowych wartości, ale zawsze według tych samych formuł matematycznych. Natomiast w obrazach rastrowych wiąże się to z próbkowaniem obrazu, czyli bezpośrednim manipulowaniem wartościami pikseli. Inkscape (rysunek 10.4) oferuje potężny arsenał narzędzi pozwalających tworzyć grafiki wektorowe w bardzo popularnym formacie SVG (Scalable Vector Graphic). Na pierwszy rzut oka wybór tego programu jako narzędzia do produkcji gier może wydawać się dziwny, ponieważ ani Blender, ani Unity nie obsługują grafiki wektorowej. Inkscape potrafi jednak konwertować takie grafiki na zwykłe obrazy pikselowe i zapisywać je w formatach czytelnych dla Blendera i Unity, np. PNG lub BMP. Może się więc przydać do tworzenia obrazów, które w przyszłości będą musiały być powiększone bądź zmniejszone, a nie powinny przy tym tracić na jakości.
Rysunek 10.4. Postać kosmicznego wroga stworzona za pomocą programu Inkscape Źródło: Inkscape.
Uwa ga
GIMP potrafi importować pliki SVG z programu Inkscape. Wystarczy taki plik przeciągnąć do okna GIMP-a, aby ten automatycznie przekonwertował grafikę wektorową na obraz pikselowy (rysunek 10.5).
Rysunek 10.5. Importowanie pliku SVG do GIMP-a Źródło: GNU Project.
291
292
Rozdział 10
Unity, Blender i inne programy
MyPaint i Krita Wielu ludzi tworzy obrazy za pomocą GIMP-a, ale są też tacy, którzy wolą używać dedykowanych programów malarskich ze specyficznymi pędzlami i innymi wyspecjalizowanymi narzędziami. Spośród wszystkich darmowych aplikacji tego typu najbardziej znane są obecnie MyPaint (http://mypaint.intilinux.com/; rysunek 10.6) oraz Krita (https://krita.org/; rysunek 10.7). Obie oferują szeroki wachlarz cyfrowych narzędzi malarskich zaprojektowanych tak, aby mogły współpracować z tabletami graficznymi. Aplikacje te potrafią doskonale symulować rzeczywiste pędzle, płótna, rozmaite rodzaje papieru itd.
Rysunek 10.6. Materiał marketingowy opracowany za pomocą MyPainta Źródło: MyPaint.
Rysunek 10.7. Materiał marketingowy opracowany za pomocą programu malarskiego Krita Źródło: Krita.
Synfig Studio
Synfig Studio Synfig Studio (http://www.synfig.org/; rysunek 10.8) jest darmową aplikacją typu open source, która stosunkowo niedawno trafiła na listę narzędzi przydatnych podczas tworzenia gier. Za jej pomocą można tworzyć dwuwymiarowe animacje, czyli wszelkiego rodzaju kreskówki, prezentacje i inne tego typu filmy animowane. Twórca opracowuje tylko klatki kluczowe, a generowaniem klatek pośrednich zajmuje się aplikacja. Gotowe animacje można zapisywać w filmowych formatach, takich jak AVI czy Theora, albo w formie sekwencji obrazów PNG.
Rysunek 10.8. Animowanie kosmicznego wroga w Synfig Studio Źródło: Synfig Studio.
Tiled Tiled (http://www.mapeditor.org/) jest darmowym edytorem graficznym typu open source wyspecjalizowanym w tworzeniu kafelkowych poziomów i map. Obsługuje rozmaite układy kafelków i potrafi tworzyć mapy zarówno ortogonalne, jak i izometryczne. Umożliwia importowanie gotowych kafelków w formatach PNG oraz JPG i ułatwia układanie z nich kompletnych poziomów. Tiled (rysunek 10.9) jest aplikacją niezależną od silnika gry, co oznacza, że jest przeznaczony wyłącznie do tworzenia map, które potem można zapisać w formacie XML, a ten jest akceptowany przez większość współcześnie używanych silników. Wiele silników, w tym Unity, ma już wbudowane mechanizmy pozwalające na odczytywanie takich plików i rekonstruowanie na ich podstawie map wygenerowanych za pomocą aplikacji Tiled.
293
294
Rozdział 10
Unity, Blender i inne programy
Rysunek 10.9. Edycja mapy w programie Tiled Źródło: Tiled.
Uwa ga
Unity nie tylko oferuje własne mechanizmy importowania map z programu Tiled, ale zezwala także na używanie narzędzi firm trzecich. Odpowiednie informacje, klasy i biblioteki można znaleźć pod następującymi adresami: ■ http://karnakgames.com/wp/unity-tiled-tilemaps/, ■ https://bitbucket.org/PolCPP/unitmx/overview, ■ www.wyrmtale.com/products/unity3d-components/orthello-pro.
MonoDevelop W pakiecie Unity znajduje się nie tylko silnik gier, ale również wieloplatformowe środowisko programistyczne (Integrated Development Environment — IDE) o nazwie MonoDevelop. Można go używać do edycji zarówno skryptów, jak i zwykłych tekstów, a nawet plików XML. Podczas tworzenia gier, a także innych programów, edytor kodu jest niezbędny. Jest on potrzebny do pisania skryptów oraz do przeglądania i edytowania plików konfiguracyjnych z danymi zapisanymi w formatach XML i JSON. Aby przystosować edytor MonoDevelop do pracy z plikami XML i innymi hierarchicznymi strukturami danych, włącz w nim funkcję odpowiedzialną za zwijanie i rozwijanie określonych fragmentów kodu. Możliwość rozwijania tylko interesujących nas węzłów znakomicie poprawia przejrzystość wyświetlanego pliku i ułatwia dotarcie do tych danych, które nas interesują. Wspomnianą funkcję można włączyć w oknie dialogowym Options (opcje) otwieranym za pomocą polecenia Tools/Options (narzędzia/opcje). W oknie tym
BMFont
należy z grupy ustawień Text Editor (edytor tekstu) wybrać kategorię General (ogólne) i zaznaczyć pole wyboru Enable code folding (włącz zwijanie kodu), tak jak na rysunku 10.10.
Rysunek 10.10. Włączanie funkcji zwijania i rozwijania kodu w edytorze MonoDevelop Źródło: MonoDevelop.
Uwa ga
Środowisko programistyczne MonoDevelop jest dostępne również jako samodzielna aplikacja, którą można pobrać z witryny: http://monodevelop.com/. Uwa ga
Alternatywnym rozwiązaniem może być Notepad++, darmowy edytor tekstu z funkcją wyróżniania składni języka C#. Działa tylko w systemach Windows i jest dostępny pod adresem: http://notepad-plusplus.org/.
BMFont Przy tworzeniu gier — szczególnie ich interfejsów — często zachodzi potrzeba wyświetlania tekstów zapisanych rozmaitymi czcionkami. Może to być imię i nazwisko gracza, które ten powinien wpisać, aby rozpocząć grę, lub nazwa pliku z zapisywanym właśnie stanem gry. Mogą to być także napisy ułatwiające użytkownikowi orientację w tym, co dzieje się nie tylko na ekranie, ale i poza nim. Krótko mówiąc: potrzebna jest możliwość szybkiego budowania dynamicznie zmieniających się tekstów.
295
296
Rozdział 10
Unity, Blender i inne programy
Podstawową metodą tworzenia takich napisów jest składanie ich ze znaków alfanumerycznych, którym wcześniej nadano formę małych obrazów bitmapowych. Zestaw takich znaków zwykle umieszcza się w jednym pliku (np. PNG), gdzie każdy znak zajmuje określone miejsce w jednorodnej siatce współrzędnych UV. Mając dostęp do takiej tablicy znaków, silnik gry może szybko zamienić pobrany z pamięci łańcuch tekstowy na ciąg pikselowych obrazów odpowiadających poszczególnym literom, cyfrom i innym znakom. Dopiero utworzony w ten sposób ciąg pikselowych obrazów jest wyświetlany na ekranie. BMFont (Bitmap Font Generator; http://www.angelcode.com/products/bmfont/) jest jednym z programów, które służą do renderowania tablic znakowych (rysunek 10.11). Jest darmowy, ale działa tylko na platformie Windows.
Rysunek 10.11. Renderowanie bitmapowej tablicy znaków alfanumerycznych Źródło: BMFont.
TexturePacker Jednym z najbardziej popularnych i efektywnych sposobów optymalizowania tekstur w Unity jest stosowanie tekstur atlasowych, czyli takich, które zawierają wiele mniejszych obrazów. Z punktu widzenia renderowania sceny korzystniejsze jest odwoływanie się do określonych prostokątnych fragmentów jednej tekstury niż do wielu odrębnych tekstur. Można oczywiście tworzyć takie atlasy ręcznie przez kopiowanie i wklejanie poszczególnych obrazów na jedno duże płótno w GIMP-ie, ale znacznie szybciej robią to specjalnie w tym celu zaprojektowane narzędzia. Jednym z nich jest aplikacja o nazwie TexturePacker (https://www.codeandweb.com/texturepacker; rysunek 10.12) działająca na większości platform desktopowych, takich jak Windows, Mac czy Linux.
LibreOffice
Rysunek 10.12. Tworzenie atlasu sprajtów w programie TexturePacker Źródło: TexturePacker.
LibreOffice Projektowanie to ważny etap procesu tworzenia gry. Jego rezultatem jest przecież dokument projektowy. To właśnie wtedy wiele rzeczy się planuje i testuje, zapisuje nowe pomysły, rysuje wykresy i schematy, ustala wersje językowe itp. Niezwykle pomocne okazują się wtedy programy biurowe, a szczególnie procesor tekstu, arkusz kalkulacyjny i edytor wykresów. Istnieje wiele pakietów biurowych z tego typu programami, a jednym z darmowych i działających na wielu platformach jest LibreOffice (http://pl.libreoffice.org/; rysunek 10.13).
Rysunek 10.13. Tworzenie wykresu za pomocą aplikacji LibreOffice Draw Źródło: LibreOffice.
297
298
Rozdział 10
Unity, Blender i inne programy
LibreOffice, poza tym że jest darmowy i wieloplatformowy, ma jeszcze wiele innych zalet. Może otwierać i zapisywać pliki w nowych macierzystych formatach pakietu Microsoft Office (takich jak .docx i .xlsx), a także eksportować dokumenty do formatu PDF. Zawiera też aplikację służącą do sporządzania rozmaitych diagramów, włącznie z diagramami UML.
Anime Studio Pro Jednym z komercyjnych, ale stosunkowo tanich programów do tworzenia animacji 2D jest Anime Studio Pro (http://anime.smithmicro.com; rysunek 10.14). Działa on podobnie jak opisywana już aplikacja Synfig Studio. Umożliwia animowanie dwuwymiarowych postaci za pomocą kości, szkieletów i kinematyki odwrotnej. Można go używać do tworzenia kreskówek i animowanych sprajtów dla gier typu platformówki 2D. Anime Studio Pro obsługuje import plików SVG z programu Inkscape i eksport do większości formatów filmowych i graficznych, włącznie z AVI i PNG.
Rysunek 10.14. Rigowanie postaci w Anime Studio Pro Źródło: Anime Studio Pro.
Audacity Audacity (http://audacity.sourceforge.net; rysunek 10.15) jest znanym i cenionym, a przy tym darmowym edytorem dźwięku działającym na wielu platformach, który był (i wciąż jest) używany przy produkcji olbrzymiej liczby gier. Popularne określenie „edytor audio” nie oddaje w pełni jego możliwości, gdyż jest programem, który potrafi nie tylko otwierać pojedyncze pliki audio, takie jak WAV, zmieniać w nich poziom dźwięku, wycinać i łączyć fragmenty oraz modyfikować je na wiele sposobów. Potrafi również działać jak kompletny wielościeżkowy sekwencer, umożliwiając komponowanie niepowtarzalnych nagrań muzycznych i ogólnie dźwiękowych.
SFXR
Rysunek 10.15. Tworzenie wielościeżkowej kompozycji dźwiękowej w Audacity Źródło: Audacity.
SFXR Jeśli tworzysz grę na platformę Windows i chcesz szybko wygenerować dźwięki znane z dawnych gier platformowych, to sięgnij po SFXR (http://www.drpetter.se/project_ sfxr.html; rysunek 10.16). Jest to darmowy generator proceduralnych efektów dźwiękowych, w którym za pomocą kilku suwaków możesz szybko uzyskać dźwięki nadające się do odtwarzania w takich sytuacjach jak atak wroga, zadanie ciosu, podskok itp. Każdy efekt można wyeksportować do formatu WAV, a więc da się go bezpośrednio zaimportować do Unity.
Rysunek 10.16. Tworzenie efektów dźwiękowych przy użyciu programu SFXR Źródło: SFXR.
299
300
Rozdział 10
Unity, Blender i inne programy
Podsumowanie Tym rozdziałem kończymy nasze rozważania nad praktycznym wykorzystaniem Blendera i Unity w tworzeniu gier. Dokonałem tu pobieżnego przeglądu programów, które mogą być przydatne dla twórcy gier, a przy tym dają się bezproblemowo włączyć w tok pracy oparty na Blenderze i Unity. Ważnym kryterium mojego wyboru była również dostępność na wielu platformach i niezbyt wygórowana cena. Jeśli do tych programów dołączyć Blendera i Unity, otrzymamy zestaw narzędzi, które w rękach utalentowanego i kreatywnego twórcy gier mogą zaowocować całkiem udanym produktem. Żeby narzędzie działało właściwie, trzeba się nauczyć nim posługiwać, i tak jest z większością narzędzi. Ale wystarczy trochę cierpliwości, ćwiczeń i ochoty do nauki, aby każdy z wymienionych programów uczynić narzędziem przydatnym w produkcji nawet najwyższej jakości gier. Nie wierz mi jednak na słowo, spróbuj sam się o tym przekonać!
DODATEK A
I NNE ŹRÓDŁA WIEDZY
Tworzenie gier komputerowych jest dziedziną pod wieloma względami bardziej otwartą niż inne sfery ludzkiej działalności. Żeby zostać lekarzem, prawnikiem lub pilotem samolotu, trzeba odbyć studia lub/i formalne szkolenie i uzyskać zezwolenie na wykonywanie zawodu. Podobnie jest w wielu innych dziedzinach. Bez pokonania mniejszych lub większych przeszkód formalnych nie da się nic zrobić. Na szczęście żeby zostać twórcą gier, wystarczy mieć komputer i może połączenie z internetem. Może. Gry możesz tworzyć przecież w biurze, w studiu, na lotnisku, w kawiarni, garażu, a nawet w sypialni. Ponadto ten, kto chciałby stworzyć grę, ma swobodny dostęp do wszelkich potrzebnych mu narzędzi. Dzisiaj niemal każdy, niezależnie od wieku i wykształcenia, może usiąść przy komputerze i zacząć tworzyć gry. Musi tylko wiedzieć, co i jak ma robić, ale wiedzę tę może zdobyć z książek takich jak ta, kursów internetowych i innych źródeł. Oczywiście sama możliwość użycia takiego czy innego narzędzia nie wystarczy. Trzeba jeszcze umieć się tymi narzędziami posługiwać, bo dopiero wtedy można za ich pomocą uzyskać to, czego się naprawdę chce. Możesz mieć najlepsze lekarstwo na świecie, ale jeśli nie potrafisz dotrzeć z nim do pacjentów, będzie bezużyteczne. Dlatego żeby pomóc Ci w jeszcze lepszym poznaniu narzędzi opisanych w tej książce, zamieszczam na koniec listę zasobów, dzięki którym będziesz mógł swoją wiedzę poszerzyć.
Witryny internetowe Dokumentacja pakietu Unity: http://unity3d.com/learn/documentation. Dokumentacja Blendera: http://wiki.blender.org/index.php/Doc:2.6/Manual. Dokumentacja GIMP-a: www.gimp.org/docs/. Artykuły szkoleniowe na temat Unity: http://unitygems.com/. Tutoriale z zakresu obsługi Blendera: www.blender.org/support/tutorials/. Tutoriale z zakresu obsługi GIMP-a: www.gimp.org/tutorials/. 301
302
Dodatek A
Inne źródła wiedzy
Książki Alan Thorn, Unity 4 Fundamentals. Get Started at Making Games with Unity. (ISBN: 978-0415823838). Alan Thorn, Learn Unity for 2D Game Development. (ISBN: 978-1430262299). Alan Thorn, Pro Unity Game Development with C#. (ISBN: 978-1-4302-6746-1). Roland Hess, Blender Foundations. (ISBN: 978-0240814308). Ben Simonds, Blender. Praktyczny przewodnik po modelowaniu, rzeźbieniu i renderowaniu. (ISBN: 978-8324685714). John M. Blain, The Complete Guide to Blender Graphics. Computer Modeling and Animation. (ISBN: 978-1466517035). Olivier Lecarme i Karine Delvare, The Book of GIMP. A Complete Guide to Nearly Everything. (ISBN: 978-1593273835). Jan Smith i Roman Joost, GIMP for Absolute Beginners. (ISBN: 978-1430231684).
Filmy wideo Alan Thorn, Complete Beginners Guide to Unity. Dostępny w 3DMotive.com. Adam Crespi, Materials and Lighting in Unity. Dostępny w Lynda.com. Alan Thorn, Modular Building in Unity and Blender. Dostępny w 3DMotive.com. Alan Thorn, Introduction to C# in Unity. Dostępny w 3DMotive.com. Jonathan Williamson, Blender Inside Out. Dostępny w Blender 3D Shop.
S KOROWIDZ
A agent, 282 animacja, Animation, 149, 152 automatyczne kluczowanie, 152 eksportowanie, 156 klatka kluczowa, 150 liczba klatek, 155 retargetowanie, 161 wypalanie, 166, 170 wzdłuż ścieżki, 166 animowanie ruchu, 166 animuj ścieżkę, Animate Path, 169 aplikacja Anime Studio Pro, 298 Audacity, 298 Blender, 14 BMFont, 295 GIMP, 14, 271, 288 Inkscape, 290 Krita, 292 LibreOffice, 297 MakeHuman, 288 MonoDevelop, 294 MyPaint, 292 SFXR, 299 Synfig Studio, 293 TexturePacker, 296 Tiled, 293 Unity, 14 aproksymowanie, 229 atak na obiekty, 201 atlas tekstur, 100
automatyczne generowanie współrzędnych, 72 kluczowanie, 152 awatar, 186
B Blender, 45 tworzenie animacji, 151 tworzenie terenu, 119 wypalanie map, 265 błędy, 30 błędy cieniowania, 55, 57 budowanie, 40 budowanie grafu animatora, 165 burza mózgów, 24
C cel, Target, 168 cel przyciągania, Snap Target, 225 chmury, Clouds, 141 ciemny motyw, 46 cienie, 55 cieniowanie, Shading, 56, 85 cień kontaktowy, contact shadow, 262 cofanie operacji, 92 cykliczność, 145
D dane kolizyjne, 105 dane trwałe, persistent data, 235 definiowanie kluczy kształtu, 174 DI, Dependency Injection, 190 303
304
Skorowidz długość, Length, 119 animacji, 155 łańcucha, Chain Length, 180 dodaj teksturę, Add Texture, 116 dodatek Texture Paint Layer Manager, 137 dodawanie tekstury, 116, 117 dołącz animację, Include Animation, 70 domyślna orientacja obiektu, 64 dopracowanie projektu, 30 droga, 143 duplikaty, 92 dzielenie siatki, 121 dziesiątkowanie, Decimate, 130, 231 dziesięcioetapowy tok pracy, 21
E edycja proporcjonalna, 121, 123 edytor współrzędnych UV, 103 wykresów, Graph Editor, 159 edytuj tekstury, Edit Textures, 116 eksport do FBX, 55 eksportowanie animacji, 156, 157 do formatu FBX, 66 do pliku FBX, 67 postaci, 157 zrigowanej siatki, 183, 184 zrigowanych postaci, 182 elementy gry, 33 etap budowanie, 40 burza mózgów, 24 dopracowanie projektu, 30 importowanie elementów gry, 34 kodowanie, 37 projekt wstępny, 27 projektowanie poziomów, 36 testowanie, 39 tworzenie elementów gry, 33 tworzenie prototypu, 29 etykietki, 48
F filtr siatki, Mesh Filter, 190 folder Editor, 74 Preferences, 239
format Collada, 182 DAE, 184 FBX, 55, 156, 183 JSON, 241 formaty dźwięku, 35 filmów, 35 obrazów, 35 plików, 35 siatek, 35 FSM, finite state machine, 164 funkcja ApplyDamage, 194 Auto-Key, 152 BroadcastMessage, 194 DealDamage, 189, 192 OnCastFireBallSpell, 189 Proportional Editing, 121 przyciągania, 85, 88 SendMessage, 194, 202 Sharp Edges, 59 X-Axis Mirror, 178, 179
G gatunek, 25 GDD, game design document, 27 generowanie chmur, 125 siatki nawigacyjnej, 279 tekstury, 124, 132, 140 terenu, 112 współrzędnych UV, 106 generuj zderzacze, Generate Colliders, 105 gęstość siatki, 120 tekselowa, 102 gizmo transformacji, 60, 62 gładkie, Smooth, 121 głębokość, Depth, 166 głębokość fazowania, 168 graf animatora, 165 grupa docelowa, 26 grupowanie wierzchołków, 94 gry czasu rzeczywistego, 208
H hierarchia obiektów, 203 hierarchie, 194
Skorowidz
I IDE, Integrated Development Environment, 294 importowanie elementów gry, 34 kluczy kształtów, 176 plików, 71 pliku, 54 zrigowanych postaci, 184 intensywność odbicia, Bounce Intensity, 261 interaktywne reorganizowanie siatki, 89 interfejs Blendera, 46
J język Python, 48
K kant, Crease, 219 kąt krawędzi, Edge Angle, 59 kinematyka odwrotna, 181 prosta, 180 klasa Enemy, 189 GUI, 197 PlayerPrefs, 237, 238 SaveState, 245–248 Wizard, 189, 194, 201 klatka kluczowa, keyframe, 70, 150 końcowa, End Frame, 155 początkowa, Start Frame, 158 pośrednia, tween, 150 klocek podstawowy, 82, 83 klucze kształtu, Shape Keys, 173 kluczowanie kształtów, 172 wizualne, Visual Keying, 171 kodowanie, 37 kolekcja prefabrykatów, 107 kolor światła firmamentu, 261 komponent Box Collider, 190 klasa Enemy, 192 Light Probe Group, 275 Mesh Filter, 190 Mesh Renderer, 190 Nav Mesh Agent, 283, 285
Transform, 190 Wizard, 192 komponowanie renderingów, 271 komunikat odrodzenia, 195 komunikaty, 191, 202 konfigurowanie interfejsu Blendera, 46 klipu, 163 kontroler animatora, 164 kontrolki, 49 kontury, Wire, 224 kopia bezpieczeństwa, 83 kości, 177 deformowane, 182 sterujące, 182 kratka UV, 103 krawędzie, 58 krycie, Opacity, 114 krzywa Beziera, 144 funkcyjna, F-curve, 150 zaniku, 122 kursor 3D, 80, 81
L liczba ścianek w siatce, 131 lustro, Mirror, 94 lustrzane odbicie, 218
Ł łączenie, Merge, 130
M malowanie teksturą, Paint Texture, 115, 131, 134, 140, 142 3D View, 138 UV/Image Editor, 134 mapa normalnych, 208 okluzji otoczenia, 270, 273 szachownicowa, 103 światła, 71, 257
kierunkowe, 260 podwójne, 259 pojedyncze, 259 światła dla siatki, 106 wysokości, 124
305
306
Skorowidz mapowanie normalnych, 208 światła, Lightmapping, 71, 252, 255, 259 UV, 98 Maya, 49 metoda edycji proporcjonalnej, 121, 123 kaskadowa, 24 modułowa, 36, 78, 79 pudełkowa, 209 rzeźbienia, 127 tekstury przemieszczeń, 124 zagęszczania siatki, 120 miękkie zaznaczanie, Soft Selection, 121 miksowanie kształtów, 172 mirroring, 93 mnożenie, Multiply, 272 model, 161 biznesowy, 26 szczegółowy, 209 uproszczony, 209 modelowanie 3D, 89 dróg, 144 pudełkowe, 205 terenu, 118–120 wysokorozdzielcze, 205 moduł, 77 modułowe środowiska, 77 modyfikator Decimate, 130, 232, 233 Displace, 125, 126 Edge Split, 58, 59, 60 Mirror, 94, 213 Multiresolution, 120, 215–217 Shrinkwrap, 227–231 Subdivision Surface, 213, 215 motyw Elsyiun, 47 motywy, Themes, 46
N nakład pracy, 30 narzędzia do retopologizacji modeli, 89 malarskie, 138 rzeźbiarskie, 127 narzędzie Backface Culling, 85 Beast Lightmapper, 71
Extrude, 144 Knife Project, 145, 147 Select Faces by Sides, 91 Subdivide, 119, 120 Transform, 87 Triangulate Faces, 90 natężenie światła firmamentu, 261 nazywanie obiektów, 67 n-kąty, 88 normalna, Normal, 84, 139 NotificationsManager, 198–201 numeracja klatek animacyjnych, 158
O obiekt nadrzędny, 197 obiekty aktywne, 202 dynamiczne, 109 statyczne, 109 oblicz, Calculate, 57 obracanie obiektów, 64 obsługiwane rozdzielczości, 26 odbicia, Bounces, 260 odczyt z pliku, 246 odejmowanie, Subtract, 129 odległość, 32 odłączanie tekstury, 143 odrzucanie ścianek, 85 odstęp między klatkami, Frame Step, 171 odtwarzanie, Frame Rate, 151 odwracanie normalnych, 84 odzyskiwanie rezultatów sesji, 52 ograniczenia Auto-Key, 154 mapy wysokości, 119 okluzja otoczenia, Ambient Occlusion, 260, 262, 269 okno 3D View, 138 Blender User Preferences, 50 Timeline, 155 UV/Image Editor, 134 okrąg, Circle, 228 opcja Alpha, 268 Connected, 121 Extend, 92 Generic, 162
Skorowidz Greater Than, 92 Use Light Probes, 278 opcje wypalania, 270 opis szczegółów, 29 opracowywanie modułów, 83 optymalizacja tekstur, 79 optymalność topologii, 118 ostre krawędzie, Sharp Edges, 58, 59 oś czasu, Timeline, 152 oświetlenie bezpośrednie, 260 dynamiczne, 252, 274 pośrednie, 260 statyczne, 252 outsourcing, 34 oznacz szew, Mark Seam, 99
P parametr Brush Size, 114 Edge Angle, 59 Frame Step, 171 Height, 119 Heightmap Resolution, 114 Length, 119 Opacity, 114 Preview, 216 Radius, 129, 282 Ratio, 232 Render, 216 Resolution, 257 Scale, 69 Scale Factor, 57, 73 Sculpt, 216 Smooth, 121 Start Frame, 158 Strength, 126 Texture, 220 View, 213 Width, 119 parametry komponentu Nav Mesh Agent, 283 mapowania światła, 261, 264 tekstury, 268 terenu, 113 wyświetlania siatki, 97 pędzel, Brush, 114, 128, 219
planowanie pracy, 33 platformy, 26 plik, 20 Enemy.cs, 193 EnemyAI.cs, 284 Wizard.cs, 193 z obrazem, 142 pliki .blend, 54, 55 binarne, 241 FBX, 70 JSON, 241 skryptowe, 192 XML, 240, 246 płaszczyzna, Plane, 222 pobieranie komponentu, 204 podgląd, Preview, 124, 216 podgląd awatara, 186 podziel, Subdivide, 119, 214 polecenie Add Texture, 116 Add/Curve/Path, 166 Add/Mesh/Plane, 119, 222 Animate Path, 169 Assign, 95 Bake Action, 170 Calculate, 57 Clear Constraints, 171 Draw All Edges, 224 Edit Textures, 116 Extrude, 144 Flip Normals, 84 Generate Colliders, 105 Include Animation, 70 Insert Single Keyframe, 153 Join, 101 Mark Seam, 99 Optimize Keyframes, 70 Origin to 3D Cursor, 80 Recover Last Session, 52 Remove Doubles, 92 Select, 95 Select All, 84 Smart UV Project, 125 Subdivide, 119, 120, 214 Undo, 92 Unlink datablock, 143 Unwrap, 99 User Preferences, 97, 137
307
308
Skorowidz popychanie, Nudge, 219 poszerz, Extend, 92 powierzchnia wielokrotnie dzielona, 206 prefabrykat, 107 preferencje gracza, 237, 238 użytkownika, User Preferences, 97 problem z cieniowaniem, 56 program Maya, 49 programowanie zdarzeniowe, 187 projekt wstępny, 27 projektowanie komponentowe, 190 poziomów, 36 promienie Final Gather, 262 promień, Radius, 129 prototyp, 29 próbnik światła, light probes, 253, 274 przekształcenie, Transform, 87, 190 przemieszczenie, Displace, 125 przenoszenie animacji, 158 modeli, 53 przesyłanie danych, 245 przezroczystość, 268 przyciąganie, snapping, 225 do powierzchni, 221, 226 przyrostowe, Incremental Snapping, 61, 81, 86 przypisywanie wierzchołków, 96 przypisz, Assign, 95 przyrost, Increment, 86 przywracanie poprzedniej sesji, 53 punkty kontrolne, 167 Python, 48
R raport operatorski, Dopesheet, 159 rejestr systemowy, 239 rejestracja klasy, 200 renderer siatki, Mesh Renderer, 190, 277 renderowanie, Render, 151, 216 renderowanie cieni, 56 rentgen, X-Ray, 224 retargeting, 185 retargetowanie animacji, 161 retopologizacja, 205, 208, 221, 227, 234 retopologizacja modeli, 89 rigi, 177
rozdzielczość mapy światła, 257, 258 siatki, 212 terenu, 130, 131 rozmiar pędzla, Brush Size, 114 wierzchołka, 97 rozpraszanie światła, 272 rozsyłanie komunikatów, 195, 203 rozwiń, Unwrap, 99 rysuj wszystkie krawędzie, 224 rzeźbiące wyciąganie, SculptDraw, 128 rzeźbienie, Sculpt, 127, 216 rzeźbienie terenu, 114, 123 rzutowanie elementów, 225 malowania, Project Paint, 139 terenu, 126 tnące, Knife Project, 145
S scena, 190 serializacja, 243, 244 danych, 240 klasy, 242 siatka, Mesh, 67, 99 high-poly, 206 low-poly, 227 nawigacyjna, 279, 280 nawigacyjna, navmesh, 254 niskorozdzielcza, 231 statyczna, 77 terenu, 127 wielokątna, 88 wysokorozdzielcza, 208 silnik, 34 siła, Strength, 126 singletony, 201 składniki kolizyjne, 106 skrypt, 48, 73, 192 sterownik, Controller, 165 sterownik animatora, Animator Controller, 162, 164 stosowanie prefabrykatów, 107 stożek, Cone, 67 symetria, Symmetry, 218 system bytów, entity system, 191
Skorowidz Mecanim, 162 NotificationsManager, 198 powiadomień, 197 sterowania, 50 szczypanie, Pinch, 219 szerokość, Width, 119 sześcian, 80 szkielet, Rig, 161 szkielety symetryczne, 178 sztuczna inteligencja, 284 szwy, seams, 98
Ś ścieżka animacji, 167 środek, 32 obiektu, 60 transformacji, 146 światło otaczające, 256
T tablet graficzny, 217 tekstura, Texture, 98, 100, 115, 132, 220 mapy światła, 266 przemieszczenia, 124, 125 typu Clouds, 124, 141 teren, 111 generowanie, 112 malowanie teksturami, 115 modelowanie, 119, 120 parametry, 113 rozdzielczość, 130 rzeźbienie, 114, 123 rzutowanie, 126 teselacja, 119 testowanie, 39 topologia siatki, 118 triangulacja ścianek, Triangulate Faces, 89 trwałość danych, 235 tryb Connected, 121 edycji, Edit Mode, 225 Increment, 86 mapowania światła, 259 mieszania, 272 rzeźbienia, 127 rzeźbienia, Sculpt Mode, 216 Single Lightmaps, 263
Subtract, 129 Texture Paint, 138 układania póz, 180 tryby mieszania, 135 tworzenie animacji, 151 dróg, 143 elementów gry, 33 grupy, 96 kluczy kształtu, 173 krzywej, 167 krzywej Beziera, 145 modeli, 55 płaszczyzn, 223 prefabrykatu, 108 prototypu, 29 scen, 196 skryptu, 192 tekstur, 98, 133 terenu, 111 tytuł, 25
U układ Animation, 152 ukrywanie deformowanych kości, 183 ścianek, 85 Unity, 35, 45 importowanie środowisk, 104 konfigurowanie środowisk, 104 tworzenie terenu, 111 ustalanie gęstości tekselowej, 102 ustawienia eksportu, 69 usuń więzy, Clear Constraints, 171 usuwanie, Delete, 130 usuwanie duplikatów, 92 używanie klocka podstawowego, 83
W warstwa okluzji otoczenia, 273 warstwy, Layers, 272 wczytywanie stanów gry, 248 węzeł BasicAnimation, 165 widoczność ścieżki, 168 widok, View, 213 widok z góry, 144 wielokrotność użycia, 79
309
310
Skorowidz wielorozdzielczość, 120 wierzchołek, Vertex, 97 więcej niż, Greater Than, 92 więzy, Constraints, 167 Follow Path, 168 kości, Bone Constraints, 180 wizualizacja gry, 30 właściwości, Properties, 151 włączanie trybu malowania, 135 rzeźbienia, 128 wsad statyczny, 109 współczynnik skali, Scale Factor, 73 współrzędne UV, 71 mapy światła, 105 terenu, 132 wstawianie klatek kluczowych, 153, 154 wstrzykiwanie zależności, DI, 190 wybieranie danych trwałych, 239 systemu sterowania, 51 typu pędzla, 128 wycinanie, Cut, 130 wydajność, 79, 118 wymiary, Dimensions, 82, 155 wymiary klocka podstawowego, 82 wypalanie, baking, 208, 251 animacji, 166, 169, 171 map, 265 map światła, 263 nawigacji, 253, 278 oświetlenia dynamicznego, 252, 274 oświetlenia statycznego, 252 próbek, Bake Probes, 276 tekstur oświetleniowych, 267 widoczności obiektów, 285
wyrównanie wierzchołków ściany, 87 wysokość, Height, 119 wysyłanie komunikatów, 195–197 wyszukiwanie n-kątów, 91 wyświetlanie informacji pythonowych, 49 siatki, 97 siatki nawigacyjnej, 280 wytłaczanie, Extrude, 92, 144, 225 wywołanie rysujące, 100 wyznaczanie szwów, 98 wzmocnienie odbicia, Bounce Boost, 261
Z zagęszczanie siatki, 120, 215, 217 zalecenia, 41 zależności zaprogramowane, 188 załączniki, 29 zamykanie bez zapisu, 51 zapętlenie czasu, Loop Time, 162 zapis do pliku, 245 mapy okluzji, 270 ręczny, 242 rozdzielczości, 237 stanu gry, 235, 240, 248 zarys gry, 28 zarządzanie projektami, 31, 33 zaznacz, Select, 95 zaznaczenie wierzchołka, 97 zderzacz prostopadłościenny, Box Collider, 190 siatkowy, 105 zestaw środowiska modułowego, 104 zgłoszenia, 33 złącz, Join, 101 zmiana typu tekstury, 141