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

Dzisiejszy odcinek: Jak zrobić cykl dnia i nocy

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

Poniższy tutorial jest pracą konkursową, a jej autorem jest: Paweł Jamroziak. 

Teoria

Stworzymy dziś cykl dnia i nocy który może przydać się nam przy tworzeniu rasowego RPG -a. Będą bardzo proste operacje na obiektach, oraz skrypt który zrozumie go nawet najbardziej początkujący.

Co nam będzie potrzebne? W zasadzie nic oprócz Unity oraz standardowych Assetów :) Wszystko mamy już podane razem z Unity.

Wykorzystane Assety ( Unity 5 ):

  • Characters – zawiera prefab “gracza”;
  • Effects – zawiera potrzebne “flary” (efekty świetlne które nadamy słońcu)
  • Environment [NIE WYMAGANY] – zawiera rzeczy takie jak prefaby drzew i wody;

Budujemy fundamenty

Gdy mamy utworzony projekt, oraz zaimportowane Assety możemy przystąpić do budowania fundamentów naszego projektu.

Na potrzeby poradnika zbudowałem prostą scenę.
Na potrzeby poradnika zbudowałem prostą scenę.

Dodałem FPSController i usunąłem MainCamera (nie jest potrzebna, oraz dublowała Audio Listener przez co czasem nie było słychać odgłosów kroków lub skoku).

Zmieniłem również nazwy obiektów (dla własnej wygody) :

  • Terrain – Wyspa
  • FPSController – Gracz
  • Directional light – Słońce

Niech stanie się światłość!

Następnym krokiem jest zmodyfikowanie naszego Słońca. Na razie wygląda tak:

Zwykły efekt słońca przed dodaniem flary.
Zwykły efekt słońca przed dodaniem flary.
Efekt słońca po dodaniu flary.
Efekt słońca po dodaniu flary.

Wybieramy obiekt Słońce i przechodzimy do zakładki Inspector.

Panel Inspector dla obiektu słońce.
Panel Inspector dla obiektu słońce.

Zmieniamy parametry tak jak podałem na zdjęciu:

  • Position – inaczej położenie na scenie naszego obiektu (pozwoli go nam lepiej zlokalizować)
  • Rotation – kąt nachylenia naszego Słońca, omówię to przy pisaniu skryptu.
  • Flare – tu mamy efekt wyglądu dla naszego słońca. Możecie wybrać jaki wam się podoba. Mi najbardziej przypadł do gustu 50mmZoom.

Uwaga! Światło na scenie powinno zmienić się na żółte. (Wyjaśnienie w “Ruszmy tym słońcem!”)

Testujemy efekt naszej pracy.. ale zaraz… Nasze słońce nie zmieniło wyglądu! Stało się tak, ponieważ prefab FPSController -a nie posiada ważnego komponentu odpowiedzialnego za wyświetlanie “Flar”.

Wybieramy nasz prefab, rozwijamy listę “dzieci” (naciskamy na strzałkę obok niego) i wybieramy FirstPersonCharacter. W zakładce Inspector dodajemy komponent Flare Layer ( Add Component -> Rendering -> Flare Layer ). Teraz powinno wszystko działać :)

Ruszmy tym słońcem!

W typie oświetlenia Directional Light zaimplementowano prostą funkcję, dzięki której stworzenie cyklu dnia i nocy jest banalne. Położenie samego słońca (Nie obiektu!) na scenie zależne jest od kąta nachylenia obiektu. Zwiększając, lub zmniejszając wartości parametru osi X możemy uzyskać pełny cykl (od wchodzu do zachodu, wliczając noc).

Tworzymy nowy skrypt (Create -> C# Script) o dowolnej nazwie (ja nazwałem go DNCycle) i otwieramy go w dowolnym edytorze (domyślnie MonoDevelop).

  1. “using” służy do dołączania bibliotek zawierających klasy, obiekty, funkcję które wykorzstujemy podczas pisania skryptu (np. transform.Rotate). Unity generuje nam tą część automatycznie.
  2. Tworzymy dwie zmienne typu zmiennoprzecinkowego (float):  speed i mn. Zmiennej speed nadajemy wartość 1 (to od niej będzie zależna prędkość naszego słońca). (Jesteś ciekaw co oznaczają dopiski public i private? Poczytaj o tym tutaj!)
  3. Wybrałem funkcję Update, ponieważ szybkość wykonywanie instrukcji w niej zawartych zależna jest od szybkości działania naszego procesora.
  4. Podstawiamy pod zmienną mn wynik mnożenia zmiennej speed i funkcji Time.deltaTime ( odmierza realne sekundy w grze ). 
  5. Zaczynamy od wybory naszego komponentu do którego się odwołujemy, w tym przypadku tansform. Następnie odnosimy się Rotation, a na końcu do jego parametrów (Vector3 to nic innego jak położenie 3D: X, Y, Z). My chcemy aby przemieszczało się w prawą stronę o czym świadczy dopisek .right.
    Mnożymy oś X z wartością zawartą w zmiennej mn. Dzięki temu zabiegowi nasze słońce będzie płynnie zmieniać swoje położenie na scenie.

Zapisujemy nasz skrypt i dodajemy go do obiektu Słońce. Testujemy czy wszystko działa. Nasze słońce jest za wolne lub za szybkie? Wystarczy że zmienisz wartość zmiennej “speed”.

Kto ukradł księżyc?!

Co to za noc bez księżyca! Stworzymy jeden prosty model w Unity i pobawimy się nim trochę tak, aby uzyskać miły dla oka efekt :)

Tworzymy nowy obiekt, sfere (GameObject -> 3D Object -> Sphere) i zmieniamy jej nazwę na np. Księżyc. Przechodzimy do zakładki Inspector:

Panel Inspector dla obiektu księżyc.
Panel Inspector dla obiektu księżyc.

Jest to ustawienie mojego księżyca. Na moją mapę jest odpowiednie, ale radzę dopasować go do własnych potrzeb. Specjalnie nie dodałem żadnych efektów świetlnych, ponieważ są zbędne.

Następne co musimy zrobić to ustawić Księżyc jako “dziecko” Słońce. Dzięki temu będzie przemieszczać się tak samo jak nasze słońce po scenie. Jak to zrobić? Przechodzimy do zakładki Hierarchy i przeciągamy Księżyc na Słońce.

Ustawienie księżyca jako dziecko obiektu słońce
Ustawienie księżyca jako dziecko obiektu słońce
O to nasz księżyc :)
O to nasz księżyc :)
  • Paweł Lis

    Witam

    Mam
    pytanie odnośnie cyklu dnia i nocy:

    Czy jest
    możliwość edycji tekstur nieba tak żeby rano niebo było błękitne a wieczorem
    gdy słońce zachodzi niebo zmieniało by się płynnie w czerwone, pomarańczowe,
    różowe i żółte itd odcienie?

    Jak stworzyć płynne
    przejście pomiędzy dniem a nocą?

    Drugie pytanie, ambitne ale
    trudne:

    Jak stworzyć pory roku i strefy klimatyczne?

    O ile pory roku da się
    zaimplementować przez upływający czas i „daty” o tyle problem pojawia się przy
    strefach klimatycznych. Chodzi mi o to żeby w jednym miejscu na mapie była
    strefa umiarkowana (4 pory roku, śnieg, deszcz, wiatr itd.) a np. w innym miejscu
    klimat pustynny czy nawet dżungla (pora deszczowa i pora sucha) –no bo kto
    widział śnieg na pustyni…

    Problem mam jak można taki
    system zaimplementować w grze (nie chodzi mi o pisanie skryptu) i co zrobić
    żeby w jakiś płynny sposób ze sobą funkcjonował. Myślałem nad dodaniem dużego Colidera
    obszarowego ale nie wiem czy to dobry pomysł.

    I trzecie pytanie:

    Jak stworzyć system flory w którym drzewa by rosły i rozsiewały się po mapie?

    • Dużo pytań postaram się jakoś uporządkować:
      1) Czy jest możliwość zrobienia płynnego przejścia?
      Jeżeli chodzi o podmianę Skyboxa w trakcie gry, to wystarczy taki kod:
      Material mat;
      RenderSettings.skybox = mat;

      Jednak jeżeli chciałbyś modyfikować kolory itp. to wchodzą już shadery, gdzie modyfikujesz wartości shadera. Przykładowy shader masz tutaj:
      https://github.com/keijiro/UnitySkyboxShaders/blob/master/Assets/Skybox%20Shaders/Horizon%20With%20Sun%20Skybox.shader

      2) Jak stworzyć płynne przejście między dniem i nocą?
      Oprócz ruchu słońca/księżyca, trzeba modyfikować skyboxy. Albo mieć ich dość dużo, tak aby zmiana z jednego na drugi nie była bardzo widoczna i zmieniasz je w pewnych odstępach czasu, aż do uzyskania skyboxa nocy, potem to samo w drugą stronę. Albo bawisz się shaderami.

      3) Strefy klimatyczne
      Tak naprawdę sprowadza się to do level designu i oprogramowania opadów. W przypadku np. pustyni i lodowej puszczy. Na pustyni na ziemi ułożysz piaszczystą teksturę, w puszczy leśne runo. Na pustyni nie dasz drzew, w lesie dasz. Zostają Ci na głowie tylko warunki atmosferyczne. Np. na pustyni burza piaskowa, a w lesie deszcz.

      I to osiąga się prosto. Bo nie potrzebujesz deszczu na całej mapie, bo gracz i tak go nie zauważy. Więc trzymasz tylko jakiś emiter warunków przy graczu i jeśli stwierdzisz, że pada deszcz to symulujesz opad deszczu. Przy czym tu musisz zadbać o to, że jeśli np. postać gracza ma mokre ubranie, to NPC też powinni mieć. Potem gdy jesteś na pustyni, ten sam emiter może emitować podmuchy piachu.

      Ostatnia sprawa, to przypilnowanie, żeby gracz przechodząc z puszczy na pustynię, nie zabrał ze sobą deszczu. Tutaj, po pierwsze warto mieć strefy przejściowe, gdzie będziesz mógł łagodnie „wyciszyć” deszcz. Jedynie zostaje tu problem wyłapania w jakiej strefie jest gracz.

      Można np. wyliczyć sobie punktowo rozmiary stref (uprościć je do prostokątów lub kół) i tak wyliczać czy gracz przekroczył strefę. Może też być jakiś collider przy przejściach, co się fajnie sprawdzi, kiedy do strefy masz kilka wąskich przejść, a nie kompletnie otwarty świat. Ogromny Collider będzie złym pomysłem, bo bardzo obciąży komputer.

      4) Flora…
      Zakładam, że masz model np. drzewa, które może rosnąć od małego pędu do normalnego drzewa.

      Teraz co jakiś czas trzeba by zrobić rozsiew. Czyli drzewo może wystrzelić w swojej okolicy X promieni np. Jeśli promień trafi w ziemię, to obliczasz procentowo czy się zasadziło. Np. dajesz 5% szans na zasadzenie. Przy dużej szansie, wystrzelisz np. 10 promieni i zasadzisz 9 drzew. Jeśli zasadzenie się uda, to robisz tam instację modelu drzewa i jego wewnętrzny skrypt zajmuje się wzrostem.

      Problem jest taki, że jak masz dużo drzew, które zrobią to na raz, to zabijesz procesor. Możliwe, że jest lepszy sposób na zrobienie tego, ale nie przychodzi mi teraz do głowy.

    • Paweł Lis

      Wielkie dzięki za odpowiedź
      O ile pierwsze pytania nieco się dla mnie rozjaśniły o tyle flora to wciąż problem.
      Sposób z promieniami fajny ale tak jak napisałeś może zabić procesor. Myślałem raczej o czymś w stylu zasięgu każdego drzewa, w którym może co jakiś czas pojawić się nowe drzewo. Jeżeli w pobliżu „drzewa1” jest dużo innych drzew (np środek lasu) to rozsiewanie nie jest możliwe. Jeżeli natomiast liczba drzew nie przekracza jakiejś z góry określonej liczby powstanie nowego drzewa jest możliwe w miarę równym odstępie od już istniejących drzew. Skrypt może wydawać się skomplikowany ale przy określonym czasie wzrostu roślin i określonym momencie ich rozsiewu myślę że nie było by to tak problematyczne.

      Kolejne pytanie; co z wielkością mapy? przy zbyt wielkiej momentalnie wyskakują różne błędy (o niektórych była mowa już w innych postach). Z kolei nie chciałbym tworzyć zbyt wielu leveli map i klimatów z drugiej strony stworzenie jednej wielkiej mapy mocno ograniczy procesor. Jakie ustawienia terenu mogą optymalnie poprawić pracę silnika i nie wpłynął znacząco na jakość gry?

      Jeszcze jedno pytanie;
      Morze (woda), w jaki sposób stworzyć odpowiednie warunki (fale, sztorm czy spokojna woda) i w jaki sposób zaimplementować łodzie statki itp. chodzi mi przede wszystkim o w miarę wiarygodne oddanie poruszania się po wodzie np statkami. Czy możliwe jest poruszanie się po Morzu które „wystaje” poza obszar terenu?

      Pozdrawiam

    • 1) Rozwój fauny chyba nie został zaimplementowany w żadnej grze na wysokim poziomie, więc może to być ogólnie dość problematyczna kwestia. Są gry gdzie drzewa odrastają na swoim miejscu, ale taki rozwój jaki opisujesz na większą skalę może być niemożliwy.

      2) Najczęściej ogranicza się pracę komputera przez modyfikację kamery, czyli skrócenie zasięgu rysowania. Im mniej komputer ma do narysowania, tym lepiej dla niego.

      3) Woda w Unity nie ma nic wspólnego z terenem, więc można ją zaprogramować w dowolny sposób, tak jak poruszanie się statkami. Jeśli chodzi o wiarygodną implementację, tutaj chyba Ci nie pomogę, bo wypadałoby poddać analizie samo zachowanie wody oraz danego typu statku. Jeżeli chodzi o tworzenie sztormów czy fal, to zależy od tego jak robisz wodę, ale przykładowe rozwiązanie problemu, masz tutaj:
      http://answers.unity3d.com/questions/443031/sinus-for-rolling-waves.html

  • Skydrovski

    Dlaczego jak próbuje dodac skrypt do komponentów do wyskakuje mi error can’t add script because class cannot be found ?

    • Nie ma błędu:
      „Tworzymy nowy skrypt (Create -> C# Script) o dowolnej nazwie (ja nazwałem go DNCycle) ”

      „public class DNCycle : MonoBehaviour”

      I taka jest zasada w Unity, że nazwa pliku ze skryptem musi być identyczna jak nazwa klasy w skrypcie. Do tego wielkość liter ma znaczenie.

  • Konrad Kurowski

    Czy da się zrobić taki cykl dnia i nocy by czas był ze synchronizowany podczas przejścia po miedzy scenami? Czyli na przykład jeżeli na jednej scenie robi się noc i przechodzimy na kolejną scenę był ten sam czas co na pierwszej scenie?

  • kubastick

    Da sie jakoś zsynchronizować to słonce na multi ?
    Chodzi o to żeby skrypt był wykonywany wyłącznie na serverze

    • kubastick

      Dobra poradziłem sobie, najpierw słońce dałem jako publiczny gameobject.
      Następnie zrobiłem child i nazwałem go rotator dodałem tam skrypt na dzień noc i ustawiłem na słońce.
      Aby rotował tylko server dodałem komponent network identify i dałem server only przez co objekt rotator istnieje tylko na serverze.
      Została jeszcze kwestia synchronizacji,więc wbiłem na słońce i dałem network identify i network transform.
      Synchronizacje ustawiłem na 1/s w celu oszczędności łącza i przy obrocie 0.25 nie czuć żadnych lagów na kliencie.