Unity3d QuickTip – czyli szybkie porady, rozwiązania częstych problemów i sztuczki w Unity3d!

Dzisiejszy odcinek: Jak zrobić cutScene? Part 1

Uwaga! Jest to poradnik typu QuickTip. Zatem skupia się on na osiągnięciu założonego celu. Zatem zakładamy że użytkownik zna na tyle program Unity3d, aby samodzielnie wykonać najprostsze czynności, jak np. dodanie modelu kostki do sceny czy dodanie modelowi jakiegoś komponentu. Jeżeli brakuje Ci tej podstawowej wiedzy, zapraszam do tutoriala:
Unity Tutorial – Podstawy

Teoria

CutScene, czyli po naszemu catscena albo fimik przerywnikowy. Krótka bądź długa animacja, która odbywa się czasami bez udziału gracza pomiędzy elementami interaktywnej rozgrywki. Najczęściej służy do opisania fabuły. Intuicyjnie wszystko wiemy. Jednak rodzajów cutscene jest wiele. Wszystkie omówimy, ale ten tutorial pokaże tylko jedną z nich. Dlaczego? Dowiecie się czytając opisy.

Prerenderowany CutScene.

Dość często używany. Sprowadza się to do tego, że scena tworzona jest w zewnętrznym programie, a gotowy filmik odpalany jest przy wywołaniu konkretnego eventu. Metoda bardzo popularna dawniej, kiedy silniki gier były słabe i stosując taką metodę w filmikach, dało się osiągnąć lepszą jakość grafiki i animacji.

Technika dobra kiedyś. Obecnie silniki graficzne pozwalają na dość sporo i nie ma potrzeby mieszać w stylach graficznych. Osobiście nie przypominam sobie gry z ostatnich miesięcy, która operowałaby takim schematem.

Przykład: Scenki z serii Gothic.

Przygotowana wcześniej scena motion capture

Kolejna metoda, to zastosowanie silnika gry, do uruchomienia pewnej sekwencji. Jednak sama animacja, również tworzona jest zewnętrznie. Tzn. mamy tu do czynienia z motion capture. Scena wygląda jak wbudowana w grę, ale jest również przygotowana wcześniej z udziałem aktorów. W tej kategorii mamy najczęściej gry, gdzie ich animację są oparte o mo-cap.

Jest to chyba najlepsza metoda robienia scenek, bo daje wysoką jakość animacji i grafiki, przy czym nie jesteśmy wybijani ze świata gry nagłą zmianą stylu graficznego. Jednak by przygotować coś takiego, potrzebujemy sprzętu do motion capture, aktorów i całej reszty sprzętu, przez co jest to raczej rozwiązanie dla dużych firm.

Przykład: Last of Us

Film

Technika dość stara, stosowana w sumie przez dwie serie gier. Command & Conquer: Red Alert oraz Wing Commander. O co chodzi? O wrzucenie najzwyklejszego filmu jako cutscene.

Rozwiązanie dość ciekawe. Wyglądające fajnie. Jednak przy obecnych możliwościach motion capture, raczej niewykorzystywana, bo zaburza immersję całkowitą zmianą światów. Jest też dość droga do wykonania, bo ponownie potrzeba nam aktorów i sprzętu do nagrywania.

Przykład: Wing Commander IV:

InGame CutScene

Bardzo ciekawe rozwiązanie, które daje nam najmniej władzy reżyserskiej, ale w zamian daje największą immersję graczowi. Tzn. w czasie trwania scenki gracz nie traci kontroli nad postacią.

Są one dość trudne z punktu widzenia scenarzysty. Jeśli scena będzie mało interesująca, gracz pójdzie się bawić gdzie indziej, co też może zepsuć klimat. Jednak mimo to, pozwala pozostać w skórze postaci którą gramy. W tej dziedzinie prekursorem był Half Life. W sumie nie za często spotykam się z podobnymi rozwiązaniami, prawdopodobnie w dużej mierze wynika to z faktu, że reżyser scenki nie ma za dużej władzy nad tym co się dzieje i co gracz widzi. W zamian, gracz może aktywnie uczestniczyć w scenie.

Przykład: Half Life 2:

Prosta cutscene

Chyba najbardziej powszechna w mniejszych grach metoda robienia scenek. Po prostu wykorzystując dostępne dla danych modli animacje i operowanie kamerą tworzymy krótkie scenki, pozwalające pchnąć fabułę naprzód. Nawet rozmowę dwóch postaci możemy podpiąć pod tego rodzaju cutscene. Przykładowo w serii Gothic, w czasie rozmowy postacie gestykulowały, ruszały ustami, a gdy obdarowaliśmy jakąś postać listem lub napojem, oglądaliśmy jak czyta lub spożywa.

Możemy dojść do wniosku, że to o czym mówię tutaj, w sumie jest tym samym co punkt poprzedni. Różnica jednak jest, ale dość subtelna. W tym wypadku gracz nie kontroluje ruchów swojej postaci.

Metoda najpowszechniejsza, najtańsza i najprostsza do zrobienia, bo nie wymaga od nas dodatkowych nakładów pracy. Oczywiście nie ma nic za darmo. W zamian tracimy nieco możliwości, bo sami ograniczamy się zasobem animacji danych modeli. Mimo to, właśnie tego typu scenkę sobie wykonamy.

Przygotowanie

Ja pracuje obecnie na Unity3d 5, dzięki czemu wszystko co mi potrzebne jest w standardowych assetach. Przede wszystkim musimy sobie wymyślić scenariusz cutscenki. Ja proponuje coś takiego: Nasza postać sobie idzie i gdy dojdzie do pewnej postaci nawiązuje się między nimi dialog. W pewnym momencie wbiega drugi NPC, który rzuca parę słów i kończy scenę. Mamy tu chyba wszystko. Poruszanie postaci, zmiana dialogów – nie będziemy tutaj robić systemu dialogów, teksty będą podane na sztywno. Spróbujemy też obsłużyć kamery.

Tworzymy nowy projekt importując sobie pakiety: Cameras, Characters, Prototyping.

Uwaga! Polecam pracować na tych samych obiektach co ja, ponieważ zawierają wiele elementów, które ułatwiają pracę i pozwalają zrozumieć proces. Korzystanie z innych modeli, może wymagać dodatkowego nakładu pracy.

Teren scenki

Ja wykorzystując obiekty z zestawu Prototyping stworzyłem sobie małą salę do tekstów. Z pakietu Characters dodałem sobie 2 modele AIThirdPerson i jeden model ThirdPerson. Modelem AI nadałem nazwy GreyOne i RedOne, oraz zmieniłem im materiał na szary i czerwony (Czerwony trzeba utworzyć, bo nie jest domyślnie dostępny). Jeśli uda się to osiągnąć, mamy solidne podstawy.

Wygląd sceny testowej
Wygląd sceny testowej

Moją salę testową wykonałem z 4 modeli FloorPrototype08x01x08 – podłoga, kilku modeli WallPrototype08x08x01 – ściany i dwóch modeli PillarPrototype02x08x02 – widoczne filary. Warto zacząć budowanie od punktu 0, 0, 0 i przesuwać obiekty o stałe wartości, dzięki temu wszystko będzie równe. Ściany zamykające obszar są u mnie po za podłogą. Oczywiście do każdego modelu należy dodać box Collider.

Postać gracza

Teraz musimy nieco zadbać o naszego gracza. Wyciągamy z pakietu Cameras kamerę MultipurposeCameraRig i przypisujemy ją jako Child Gracza. Jeśli jeszcze tego nie zrobiłeś usuń obiekt Main Camera. Teaz trzeba sobie nieco dostosować naszą nową kamerę. W tym celu aktywujemy skrypt Protect Camera From Wall, który jest do niej przypisany – dzięki niemu kamera nie przenika przez ściany. Dodatkowo zmieniamy kamerze ustawienia na: Position: 0, -1, 1 ; Rotation: 20, 0, 0. Ostatecznie otrzymując coś takiego:

Ustawienia kamery gracza
Ustawienia kamery gracza

Ale to nie koniec. Zmieniamy jeszcze obiektowi gracza tag na: „Player”.

NPC

Teraz tworzymy sobie 3 kamery. [GameObject -> Camera]. Każdą z kamer przypisujemy (jako child) do jednego z modeli postaci. Tzn. obaj NPC i gracz ma posiadać taką dodatkową kamerę. Jedyne co z kamerami robimy to: Usuwamy im komponent Audio Listener, oraz ustawiamy Position: 0.26, 1.33, 0.44; Rotation: 0, 210, 0. Kamery nazwałem: GreyOneCam, RedOneCam, PlayerCam.

Po tych zabiegach, każda z kamer powinna ukazywać twarz danej postaci. Wykorzystamy to sobie do pokazywania twarzy osoby, która aktualnie się wypowiada.

Z racji, że jeden z NPC będzie dramatycznie wbiegał, stworzymy sobie dodatkową kamerę, która pokaże tą scenę. Dlatego tworzymy kolejną kamerę, znów usuwamy jej komponent Audio Listener i ustawiamy ją tak, żeby dobrze uchwyciła miejsce wbiegania. U mnie sprowadza się to do: Position: 6.66, 5.15, 12.77; Rotation: 35, 160, 0. Kamerę nazwałem RunCam.

Wbieganie

Teraz coś nieco trudniejszego. Chcemy, żeby jeden z NPC – u mnie czerwony – wbiegł w czasie scenki. Aby to sobie ułatwić wykorzystać NavMeshe.

Dodatkowa Wiedza
NavMesh – Czyli Mesh nawigacyjny. Wykorzystywany w Unity do tworzenia poruszającej się sztucznej inteligencji, a dokładniej PathFindingu – czyli wyszukiwania ścieżki.

No to jak zrobić takie coś? Ogólnie potrzebujemy dwóch rzeczy. Punktu docelowego – co wystarczy, dzięki temu, że skrypt AI Character Controll dodany domyślnie do obiektów AIThirdPerson pozwala na przemieszczenie postaci do wskazanego punktu. Dlatego teraz utworzymy sobie ten punkt. Czym on będzie? Zwykły pusty gameObject [GameObject -> Create Empty]. Teraz należy ustawić ten punkt. Ma to być miejsce gdzie nasz NPC skończy bieg. U mnie wypada to w punkcie o Position: 7, 0, 7, a sam punkt nazwałem RedDest – warto zadbać o to, żeby punkt znajdował się w zasięgu kamery RunCam.

Teraz czas przygotować nasz Mesh. Otwieramy sobie nowe okienko: [Window -> Navigation]. Mając je otwarte zaznaczamy sobie nasze obiekty będące podłogą. Zaznaczamy opcję Navigation Static. W opcji Navigation Area wybieramy „Walkable” – dająca się poruszać i klikamy Bake. Po tym zabiegu powinniśmy zobaczyć niebieski obszar na ziemi.

Wyapalamy NavMesha
Wyapalamy NavMesha

Zabieg powtarzamy na wszystkich elementach podłogi, które musi pokonać NPC aby dotrzeć do punktu RedDest. Jednak, może zdarzyć się tak, że obszar będzie się stykał z jakąś ścianą. Jeśli pozwolilibyśmy naszemu NPC biec mimo to, prawdopodobnie zgubiłby się w ścianie – u mnie takie coś może się zdarzyć w pobliżu filara. Dlatego mając dalej otwarte okno Navigation, zaznaczamy wszystkie ściany po drodze i znów zaznaczamy Navigation Static, jednak tym razem w opcji Navigation Area wybieramy: „Not Walkable” i znów naciskamy bake. Dzięki temu powinny się pojawić marginesy przy ścianach. Poprawnie wykonany NavMesh powinien wyglądać tak:

Poprawny NavMesh
Poprawny NavMesh

Gadka szmatka

Nasze postaci mają rozmawiać. Oczywiście nie będę tu nagrywał voice actingu. Dlatego tekst rozmów trzeba wyświetlić. Wykorzystamy do tego kontrolkę UI. Tworzymy sobie obiekt text: [GameObject -> UI -> Text]. Należy oczywiście dostosować jego ustawienia.

  • Zmieniłem opcję Left i Right na 5 – są to marginesy dla tekstu.
  • Opcję PosY na -480 – po prostu wtedy tekst pojawił się na dole ekranu.
  • Height na 60 – dzięki temu tekst może mieć kilka linijek
  • Zaznaczyłem opcję Best Fit – dzięki temu tekst powinien być zawsze dobrze czytelny pod względem rozmiaru czcionki.
  • Wybrałem wyśrodkowanie tekstu

Ogólnie stworzyłem coś takiego:

Ustawienia komponentu tekstowego
Ustawienia komponentu tekstowego

Nazwę zostawiłem bez zmian. Przy dobieraniu parametrów najlepiej mieć podgląd na okienko Scene, a do pola Text wprowadzić jakiś dłuższy tekst. Wtedy będziemy wiedzieć jak się nasz pasek zachowa.

Zarządzanie kamerą

Gdy mamy tyle kamer może się porobić mały kocioł. Tzn. jeśli teraz uruchomisz grę, jest duża szansa, że nie będziesz spoglądał z kamery z której oczekujesz spoglądać. Dlatego tworzymy sobie pusty GameObject oraz skrypt. Skrypt nazwałem: CameraController.cs. GameObjet chowamy gdzieś (ja dałem mu ujemną wartość parametru position.y, aby był schowany pod całą konstrukcją, aczkolwiek nie ma to większego znaczenia, gdzie ten obiekt będzie). Sam GameObject nazywamy również CameraController i przypisujemy do niego nasz skrypt.

Sam skrypt wygląda tak:

Co tu się dzieje? Na starcie tworzymy sobie tablicę kamer, której wielkość jest równa wszystkim kamerom jakie mamy w scenie. Następnie pętlą foreach przechodzimy wszystkie kamery, które zwraca nam funkcja Camera.allCameras i zapisujemy je sobie w tablic (ułatwia i skraca to czas dostępu do nich później).

Od razu wykonujemy funkcję SelectCamera z parametrem 0. Jeżeli nie zmieniłeś kolejności dodawania kamer, to powinna być nasza kamera zza pleców gracza.

Sama funkcja SelectCamera sprawdza wszystkie kamery w tablicy. Jeśli trafiamy na kamerę o wskazanym indeksie ustawiamy jej enabled na true, jeśli nie to na false. Możesz się tu domyślić, że kamera mająca ten parametr ustawiony na true będzie kamerą aktywną. Samą funkcję, wykorzystamy później do zmiany kamer.

Wywołujemy cutscene

Scena gotowa. Zostało wywołać cutscene i zrobić całą magię. Aby wszystko ruszyło, potrzebujemy jakiś Trigger. Dlatego znów tworzymy pusty gameObject [GameObject -> Create Empty]. Swój nazwałem CutSceneTrigger. Dodajemy do niego Sphere Collider z włączoną opcją IsTrigger oraz ze zwiększonym promieniem tak, żeby zajmował całe przejście. U mnie wyszło: Position: 5, 0, 9; Radius: 3.

Tworzymy sobie nowy skrypt C#: Cutscene.cs i przypisujemy do obiektu CutSceneTrigger. Co dajemy w kodzie?

Początek jest chyba nam dobrze znany. Jeśli coś wejdzie w Trigger i jeśli ma tag Player, to uruchamiamy funkcję startCutScene, która w zamyśle ma zawierać całą magię scenki. Jednak, żeby ten tutorial nie miał miliona linii, całą magię zrobimy sobie w drugiej części. Póki co, kod który się tam znajduje kolejno: Znajduje obiekt CameraController – ten nasz schowany. Wyciąga z niego komponent CameraController – czyli nasz wcześniejszy skrypt, a na koniec wybiera inną kamerę.

Teraz możesz przetestować grę. Jeśli po wbiegnięciu w Trigger zmieni się kamera to znaczy, że wszystko zrobiłeś dobrze i możesz cierpliwie czekać na drugą część.

Część druga QuickTipa!