Unity3d QuickTip – czyli szybkie porady, rozwiązania częstych problemów i sztuczki w Unity3d!
Dzisiejszy odcinek: Jak zrobić pauzowanie gry
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
Chyba rzecz podstawowa. W 99% gier, gdy naciśniemy Esc, gra zatrzymuje się, a naszym oczom ukazuje się menu. Dziś postaramy się uzyskać taki efekt.
Przygotowanie
Tak naprawdę, potrzebujemy tylko czegoś, co udowodni nam, że gra została zatrzymana. Dlatego ja dodałem do gry Cuba, któremu przypiszemy prosty skrypt na poruszanie się. Oraz dwa źródła dźwięku. Można wrzucić tam dowolną muzykę, przy czym tylko jeden otrzyma parametr Play On Awake.
Skrypt dla Cuba wygląda w całości tak:
using UnityEngine; using System.Collections; public class CubeMove : MonoBehaviour { private bool goingUp = true; // Update is called once per frame void Update () { if(goingUp) { gameObject.transform.Translate(Vector3.up * Time.deltaTime); } else { gameObject.transform.Translate(Vector3.down * Time.deltaTime); } if(gameObject.transform.position.y >= 3) { goingUp = false; } if(gameObject.transform.position.y <= -3) { goingUp = true; } } }
Jeżeli chodzi o dźwięki, do pobrania są na samym dole w kompletnym projekcie. Nie wrzucam ich na siłę, bo zakładam, że każdy ma kilka plików .mp3 na dysku i nie będzie z tym problemu.
Pamiętaj o dobrym rozstawieniu obiektów. Tzn. niech cube będzie widoczny w kamerze, a źródła dźwięku (dwa EmptyGameObjecty z komponentami AudioSource), będą na tyle blisko kamery, aby dźwięk był słyszany. U mnie wygląda to tak: Kamera(0, 1, 0), Cube (0, 1, 8), Źródła dźwięku (5, 1, 0) i (-5, 1, 0).
Przyda nam się też dodatkowa muzyczka, która będzie muzyką w menu. Dlatego, dodajemy też komponent AudioSource do naszej kamery i również odznaczamy opcję Play On Awake.
Uruchom teraz grę, jeśli Cube lekko faluje w górę i w dół i słyszysz jedną z piosenek, to znaczy, że wszystko zrobiłeś dobrze i możemy przystąpić do pracy nad pauzą.
Tworzymy pauze
Oczywiście tworzymy nowy plik skryptu C#, u mnie: InGameMenu.cs i dodajemy go do MainCamera. Od razu wchodzimy w edycję pliku i zaczynamy zabawę.
Najpierw stworzymy samo pauzowanie. Czyli dodajemy sobie jedną zmienną, która przyda się przy wyświetlaniu menu:
private bool gamePaused = false;
A teraz zatrzymujemy grę i wyświetlamy menu:
void Update() { if(Input.GetKeyDown(KeyCode.Escape)) { if(gamePaused) { Time.timeScale = 1; gamePaused = false; } else { Time.timeScale = 0; gamePaused = true; } } }
Sprawa jest banalnie prosta. Po naciśnięciu Esc, sprawdzamy czy gra jest zapauzowana. Jeśli nie, to ustawiamy naszą flagę na true, aby funkcja OnGUI wiedziała, czy gra jest zapauzowana, oraz ustawiamy Time.timeScale na 0. Sprawia to, że upływ czasu w grze jest zerowy. W przypadku Time.timeScale = 1, 1 sekunda w grze to 1 sekunda rzeczywista, jeśli ustawimy wartość na 0, to jedna sekunda rzeczywista, równa jest 0 sekund w grze. Jeśli wstawimy tam jakiś ułamek, gra tylko zwolni.
void OnGUI() { if(gamePaused) { Color background = Color.black; background.a = 0.3f; DrawQuad(new Rect(0, 0, Screen.width, Screen.height), background); if(GUI.Button(new Rect(Screen.width / 2 - 50, 20, 100, 20), "Play")) { } if(GUI.Button(new Rect(Screen.width / 2 - 50, 50, 100, 20), "Score")) { } if(GUI.Button(new Rect(Screen.width / 2 - 50, 80, 100, 20), "Quit")) { } } }
W funkcji GUI, intrygujące mogą być tylko zaznaczone linie. Pierwsza z nich tworzy nam kolor czarny. Druga, zmienia jego kanał alfa (czyli przeźroczystość) na 0.3. Dzięki temu, uzyskamy efekt wyszarzenia tego co będzie w tle. Ostatnia funkcja, to zaczerpnięta z forum Unity funkcja do stworzenia kolorowego prostokąta. Wygląda ona tak:
void DrawQuad(Rect position, Color color) { Texture2D texture = new Texture2D(1, 1); texture.SetPixel(0,0,color); texture.Apply(); GUI.skin.box.normal.background = texture; GUI.Box(position, GUIContent.none); }
Tworzymy tutaj teksturę 2D o rozmiarze 1 na 1px, a następnie wypełniamy naszym kolorem. Później do GUI dodajemy skórkę, gdzie boxy mają ustawione tło na nasz kolor. Na koniec rysujemy pustego Boxa. Przez to, że funkcji podaliśmy jako parametry nasz przeźroczysty czarny i rozmiar całego okna, dostaniemy lekkie przyciemnienie ekranu w tle.
Możesz teraz, przetestować skrypt i pewnie od razu zauważysz, że muzyka gra dalej!
Wyciszenie muzyki.
Aby to zrobić, przy pauzowaniu, trzeba wyłączyć wszystkie AudioSource w grze.
void Update() { if(Input.GetKeyDown(KeyCode.Escape)) { if(gamePaused) { Time.timeScale = 1; gamePaused = false; } else { Time.timeScale = 0; gamePaused = true; AudioSource[] aSources = FindObjectsOfType(typeof(AudioSource)) as AudioSource[]; foreach(AudioSource source in aSources) { source.Pause(); } } } }
Dlatego dodajemy zaznaczony fragment. Najpierw tworzymy tablicę obiektów typu AudioSource, gdzie zapisujemy wszystkie występujące w grze, a następnie dzięki pętli foreach, pauzujemy każdy z nich. Oczywiście, po wyłączeniu pauzy, trzeba je włączyć ponownie:
void Update() { if(Input.GetKeyDown(KeyCode.Escape)) { if(gamePaused) { Time.timeScale = 1; gamePaused = false; AudioSource[] aSources = FindObjectsOfType(typeof(AudioSource)) as AudioSource[]; foreach(AudioSource source in aSources) { source.Play(); } } else { Time.timeScale = 0; gamePaused = true; AudioSource[] aSources = FindObjectsOfType(typeof(AudioSource)) as AudioSource[]; foreach(AudioSource source in aSources) { source.Pause(); } } } }
Ponownie uruchom skrypt. Niby wszystko fajnie, ale po wejściu do gry, uruchamiają się dźwięki, które wcześniej nie grały! Aby to naprawić, zastosujemy prosty trik.
void Update() { if(Input.GetKeyDown(KeyCode.Escape)) { if(gamePaused) { Time.timeScale = 1; gamePaused = false; foreach(AudioSource source in pausedSources) { if(source) { source.Play(); } } } else { Time.timeScale = 0; gamePaused = true; AudioSource[] aSources = FindObjectsOfType(typeof(AudioSource)) as AudioSource[]; pausedSources = new AudioSource[aSources.Length]; for(int i = 0 ; i < aSources.Length ; i++) { AudioSource source = aSources[i]; if(source.isPlaying) { source.Pause(); pausedSources[i] = source; } } } } }
Co zrobiliśmy? W momencie pauzowania gry, przelatujemy całą listę naszych źródeł i sprawdzamy czy dane źródło odtwarza muzykę. Jeśli tak, to pauzujemy je i dodajemy do dodatkowej tablicy, którą musimy sobie wcześniej zadeklarować, koło naszej zmiennej bool:
private AudioSource[] pausedSources;
Teraz w momencie odpauzowania, przelatujemy listę i jeśli znajduje się w niej jakieś źródło dźwięku, to je uruchamiamy. Niestety kod nie jest zbyt estetyczny, bo obiekty listowe, np. ArrayList, nie chcą przyjąć zmiennej typu AudioSource, dlatego musimy operować na zwykłych tablicach.
Ostatnie co zostało to uruchomić naszą muzyczkę dla menu, co będzie banalnie proste.
void Update() { if(Input.GetKeyDown(KeyCode.Escape)) { if(gamePaused) { gameObject.audio.Stop(); Time.timeScale = 1; gamePaused = false; foreach(AudioSource source in pausedSources) { if(source) { source.Play(); } } } else { Time.timeScale = 0; gamePaused = true; AudioSource[] aSources = FindObjectsOfType(typeof(AudioSource)) as AudioSource[]; pausedSources = new AudioSource[aSources.Length]; for(int i = 0 ; i < aSources.Length ; i++) { AudioSource source = aSources[i]; if(source.isPlaying) { source.Pause(); pausedSources[i] = source; } } gameObject.audio.Play(); } } }
Zwyczajnie uruchamiamy lub stopujemy AudioSource przyczepione do kamery. Kluczowe jest tylko to, aby w przypadku uruchamiania zrobić to po funkcji stopującej. W innym wypadku, muzyka z menu zostanie od razu zatrzymana i uruchomi się gdy wyjdziemy z menu.
Koniec. Przetestuj teraz swoje menu.
Download – Kompletny projekt
[to_like]
[/to_like]