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

Dzisiejszy odcinek: Jak zrobić wyświetlanie podpowiedzi?

Teoria

Sprawa jest dość prosta więc nie będę się rozpisywał. W praktycznie każdej grze, mamy do czynienia z podpowiedziami wyświetlanymi na ekranie. Zakładam że każdy kojarzy sytuację, kiedy podchodzi do jakiegoś obiektu i pojawia się informacja “Naciśnij E aby podnieść”, lub coś w tym rodzaju. Dziś zajmiemy się wyświetleniem właśnie takiego tekstu!

Komunikat o zwycięstwie w grze Sanctum 2
Komunikat o zwycięstwie w grze Sanctum 2

Oczywiście jak widać na załączonym obrazku, taki tekst możemy wykorzystać na wiele sposobów.

Więc co będzie naszym celem? Stworzymy sobie collider. Po wejściu w niego ma się nam pojawić podpowiedź, która zniknie po pewnym czasie. Do dzieła!

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 – Podstawy 

Przygotowanie

Co będziemy potrzebowali żeby stworzyć nasze pole testowe?

  • Kontroler postaci, którą sterujemy
    Ja użyłem First Person Controllera, dostępnego ze standardowych assetów Unity. Można go zaimportować za pomocą [Asset -> Import Package -> Character Controller]
  • Jakiś dowolny obiekt (Ja użyłem zwykłej kostki (Cube)).
    Dla jej komponentu Collider musimy ustawić parametr isTrigger, oraz nieco zwiększyć jego rozmiar.
  • Podłoże po którym będziemy chodzić. U mnie znów jest to kostka.
  • Directional Light. Tylko po to żeby coś widzieć.
  • Dwa skrypty: U mnie TipDisplayer – przypisany do obiektu gracza, oraz TipCollider – przypisany do obiektu z Colliderem.
  • Oraz obiekt typu GUI Texture. Domyślnie ustawiamy mu wartość atrybutu Text na puste. Oraz ustawiamy jego pozycję na kamerze według naszych upodobań.

Wykonanie

Zaczniemy od kodu TipDisplayer’a. I od zdefiniowania zmiennych:

private bool showTip = false;
private float timer = 0;
public float tipTime = 5;
public GUIText tipGUI;

Szybkie wyjaśnienie. Zmienna showTip, odpowiada za pamiętanie czy podpowiedź ma się wyświetlić.  Timer to zmienna, którą sobie wykorzystamy wewnętrznie do odliczania czasu, do “zniknięcia” podpowiedzi. Zmienna tipTime, to czas, jak długo ma się wyświetlać podpowiedź. Jest publiczna żeby można było z poziomu Unity dostosować sobie ten czas. Ostatnia zmienna to tipGUI, która jest naszym obiektem typu GUIText. Teraz należy przejść do Unity, zaznaczyć nasz obiekt gracza i w pole zmiennej tipGUI przeciągnąć nasz dodany komponent GUIText, który dodaliśmy do sceny gry.

void displayTipMessage(string tipText) 
{
	tipGUI.text = tipText;
	tipGUI.enabled = true;
	this.showTip = true;
}

Teraz stworzymy funkcję, która komunikat wyświetli. Jak widać jako parametr przyjmuje zmienną tekstową, która będzie komunikatem. Odwołując się do naszej zmiennej tipGUI, ustawiamy wartość jej parametru text na otrzymaną przez funkcję. Następnie ustawiamy parametr enabled na true. Sprawia to że jeśli kontrolka byłby ukryta, pojawi się. Na koniec zmieniamy naszą zmienną na true, aby zakomunikować całemu skryptowi że mamy wyświetlić podpowiedź.

void Update () {
	if(showTip) {
		if(timer < tipTime) {
			timer += Time.deltaTime;
		} else {
			tipGUI.enabled = false;
			showTip = false;
			timer = 0;
		}
	}
}

Ostatni kod dodaliśmy w funkcji Update. Najpierw sprawdzamy czy wyświetlana jest jakaś podpowiedź. Jeśli jest to sprawdzamy czy nasz timer jest mniejszy od czasu maksymalnego wyświetlania się podpowiedzi. Jeśli tak jest, znaczy że powinna się jeszcze wyświetlać, ale by nie trwało to w nieskończoność dodajemy do kontrolki sekundę.

Możecie się zastanawiać czemu nie zrobiliśmy po prostu timer += 1 albo timer++. Odpowiedź jest prosta. Funkcja update wykonuje się co klatkę. Przez co dodawalibyśmy “sekundę” w każdej klatce. Czyli nasze 5 sekund trwało by 5 klatek. Przy odtwarzaniu 20 klatek na sekundę, jest to 1/4 sekundy. Do tego, nasze 5 sekund trwało by różny czas, w zależności od szybkości komputera, na którym gra jest uruchomiona. Raczej małe szansę że ktoś to przeczyta. Dlatego konstrukcja Time.deltaTime, pozwala dodać faktyczną sekundę, niezależną od klatek.

Jeżeli czas upłynął, (przechodzimy do tego co jest w else), chowamy nasz komponent GUIText, “ogłaszamy” skryptowi że nie wyświetlamy podpowiedzi przez zmianę kontrolki showTip na false, oraz zerujemy timer, aby był gotowy do użycia gdy nadejdzie kolejny komunikat do wyświetlenia.

Cały kod wygląda tak:

using UnityEngine;
using System.Collections;

public class TipDisplayer : MonoBehaviour {

	private bool showTip = false;
	private float timer = 0;
	public float tipTime = 5;
	public GUIText tipGUI;

	// Update is called once per frame
	void Update () {
		if(showTip) {
			if(timer < tipTime) {
				timer += Time.deltaTime;
			} else {
				tipGUI.enabled = false;
				showTip = false;
				timer = 0;
			}
		}
	}

	void displayTipMessage(string tipText) 
	{
		tipGUI.text = tipText;
		tipGUI.enabled = true;
		this.showTip = true;
	}

}

Czas na drugi plik, który przypisaliśmy do obiektu z Colliderem.

using UnityEngine;
using System.Collections;

public class TipCollider : MonoBehaviour 
{

	void OnTriggerEnter(Collider other) 
	{
		other.SendMessage("displayTipMessage", "Your Tip!");
	}
}

Jak widać tutaj kodu nie ma za dużo. Mamy funkcję OnTriggerEneter, która wykonuje się po wejściu w Collider z zaznaczoną opcją isTrigger. Co ważne wykonuje się on tylko w momencie wejścia. Zatem to że ktoś stoi dłużej wewnątrz Collidera, nie sprawi że tekst podpowiedzi będzie się wyświetlał w nieskończoność.

Jedyną linijką wewnątrz funkcji jest wykonanie funkcji SendMessage dla obiektu other. Zacznijmy od tego czym jest obiek other, który otrzymujemy jako parametr? Odpowiedź jest banalnie prosta, jest to obiekt, z którym weszliśmy w kolizję. (Musi posiadać Collider!).

Za to jak działa funkcja SendMessage? Jest to wysłanie komunikatu do obiektu o poszukiwanie pewnej funkcji. Można to zobrazować tak, że teraz TipCollider, mówi do obiektu other, że szuka funkcji “displayTipMessage”, oraz że chce ją wykonać z parametrem “Your Tip!”. Jeśli taką funkcję znajdziemy, zostanie ona wykonana. A jak ona wygląda wiemy z poprzednich części kodu.

I tak. To wszystko! Efekt powinien być następujący:

Efekt działania programu
Efekt działania programu

Podsumowanie

Dzisiaj zobaczyliście jak prosto i szybko zrobić prosty system wyświetlający podpowiedzi dla gracza. Oczywiście można system łatwo rozbudować przez np. dodanie możliwości, żeby każdy obiekt wysyłający prośbę o wyświetlenie komunikatu, sam decydował ile sekund ma się on wyświetlać.

Mam nadzieję że ten poradnik komuś się przydał i był dla was prostą i ciekawą lekcją. Jeśli macie jakieś pytania lub sugestię, zapraszam do komentowania.

Pobierz pliki – Kompletny projekt

[to_like] QucikTip1 – Marek Winiarski [/to_like]

21 thoughts

  1. Świetny poradnik jak i cały blog, cieszę się że na niego trafiłem. Mam 17 lat i chciałbym tworzyć gry – masz jakieś rady jak zacząć, co przeczytać, czego się nauczyć?

    1. Bardzo się cieszę, że podobają Ci się moje tutoriale. :) Zacząć najlepiej od nauki jakiegoś języka programowania. Jeśli nie znasz żadnego, proponuję C#, bo będzie jak znalazł do Unity. Ew. możesz zacząć od C++ czy Pascala, które są bardziej wymagające, więc lepiej Cię przeszkolą.

      Jak chodzi o samo Unity, to zacząłbym od jakiegoś prostego tutoriala, który pokaże Ci najważniejsze i najczęściej używane funkcję w Unity. Potem wymyśl sobie prostą grę typu Arkanoid czy Pong i spróbuj ją napisać, szukając pomocy w sieci gdy się zatniesz.

      Potem zostaje rozwijać wiedzę. Jednak w Polsce niestety nie ma literatury na temat Unity. Jest tylko książka Projektowanie gier w środowisku Unity 3.x, ale z racji, że mamy Unity 4.6, a piątka się szybko zbliża, jest dość nieaktualna, ale jak nie szkoda Ci pieniędzy, solidne podstawy tam znajdziesz.

      I w sumie tyle jak chodzi o samo programowanie gier. Bo wiadomo, że jeszcze można tworzyć grafikę czy dźwięki do nich. ;)

      A jeżeli chcesz być na bieżąco z moimi poradnikami i wpisami, to polecam polubienie fanpage’a na FB:
      https://www.facebook.com/mwinblog

  2. Mam problem, bo ja mam GUIText jako komponent, więc gdzie go dodać, a jeśli nie dodawać to co dać innego za GUIText?

    1. chodzi oto, że zrobiłem dziennik zadań oto skrypt:

      var Dziennik: GUIText;
      var Timer: float;
      var Tresc: String = “Moje pierwsze zadanie!”;

      function Start ()
      {
      Timer = 1;
      }

      function Update ()
      {
      Timer += Time.deltaTime;
      Dziennik.text = Tresc;
      if(Input.GetButton(“Dziennik”)&& Timer > 1)
      {
      Debug.Log(“Działa dziennik w ogole”);
      if(!Dziennik.enabled)
      {
      Dziennik.enabled = true;
      Timer = 0;
      Debug.Log(“Działa dziennik otwieranie”);
      }else
      {
      Dziennik.enabled = false;
      Timer = 0;
      Debug.Log(“Działa dziennik zamykanie”);
      }
      }
      }
      Nie wiem, dlaczego nie działa.

    2. W jakim sensie nie działa? Nie wyświetla się nic, czy są błędy?
      W ogóle w kodzie masz trochę za dużo moim zdaniem. Timer w ogóle tutaj nie wydaje się potrzebny. A ustawianie tekstu w funkcji Update mija się z celem, wystarczy ją ustawić raz. Niepotrzebnie obciążasz procesor.

      Możesz też sobie skrócić kod, rezygnując z ifa na rzecz: Dziennik.enabled = !Dziennik.endabled;

      Jeżeli nie pojawia się treść, to wprowadź jakąś na sztywno i zobacz czy w ogóle tekst się wyświetla. Jeśli nie, to problemem nie jest kod, a sam GUIText.

    3. Tak własnie w tym problem. Wpisalem byle co i nic sie nie wyswietla. Myślałem, że może wyswietli się w okolicy gameobject ale nie.

  3. w skrypcie nie wykrywa błędów ale w grze po prostu nie działa…, tzn. nic nie wyświetla się gdy wejde w collider. btw gra 2d ale to nie powinno mieć znaczenia

  4. Przy próbie wykorzystania udostępnionych tutaj skryptów. Tip pojawia się na mniej niż sekundę po czym znika, gdy collider ustawiony był na odpowiedniej wysokości, zacząłem skakać postacią, co pozwoliło utrzymać mi podpowiedź na dłużej, ale jak wiadomo nie tak powinno to działać. Myślisz Marku, że dlaczego tak się dzieje?

    1. Podpowiedź wyświetla się w zależności od długości timera jaki ustawimy:
      if(timer < tipTime) {
      timer += Time.deltaTime;

      tipTime w kodzie, ustawiony jest na 5, ale jeśli dla Ciebie jest to za krótki okres, to możesz dowolnie liczbę zmieniać.

      W przypadku skakania, prawdopodobnie wychodziłeś po za collider i wchodziłeś w niego ponownie resetując licznik.

      Innym rozwiązaniem może być usunięcie licznika i zostawienie samego ustawiania wyświetlania na true przy OnTriggerEnter i wyłączenie go w funkcji OnTriggerExit, wtedy do póki jesteś w Triggerze, będziesz miał widoczną podpowiedź. Przy czym Timer jest często bardziej przydatny.

    2. Ustawienie timera nawet na 50 nie zmieniło działania skryptu. Spróbowałem przerobić go w ten sposób:
      TipDisplayer:

      using UnityEngine;
      using System.Collections;
      public class TipDisplayer : MonoBehaviour {

      public bool showTip = false;
      private float timer = 0;
      public float tipTime = 5;
      public GUIText tipGUI;

      // Update is called once per frame

      void Update () {
      }

      Drugi plik:

      using UnityEngine;
      using System.Collections;

      public class IdzDoSali304 : MonoBehaviour
      {
      void OnTriggerEnter(Collider other)
      {
      other.SendMessage(“displayTipMessage”, “Dostań się do sali 304 na trzecim piętrze!”);
      }

      void OnTriggerExit(Collider other)
      {
      other.SendMessage (“stopTipMessage”);
      }
      }

      void displayTipMessage(string tipText)
      {
      tipGUI.text = tipText;
      tipGUI.enabled = true;
      this.showTip = true;
      }

      void stopTipMessage()
      {
      tipGUI.enabled = false;
      }

      }

      Żadnej zmiany

    3. Oczywiście wolałbym opcję z timerem, tą spróbowałem czy rozwiąże to jakoś mój problem, ale efekt ten sam

  5. witam, mam pytanie otóż zastanawiam się jak wyświetlić zmienne typu string za pomocą funkcji OnGUI jakieś sugestie???

    1. Możesz wykorzystać funkcję: GUI.Label
      Czyli ogólnie wyjdzie coś takiego:
      void OnGUI() {
      GUI.Label(new Rect(x, y, w, h), TwojString);
      }

      Gdzie x i y to inty, z położeniem pola tekstowego, w i h to szerokość i wysokość pola tekstowego, a TwojString to string z tekstem, który chcesz wyświetlić.

      Więcej info o funkcji w dokumentacji:
      https://docs.unity3d.com/ScriptReference/GUI.Label.html

      A więcej o zastosowaniu klasy GUI tutaj:
      https://mwin.pl/klasyczne-unity3d-1-gui/

    2. A jak to włąsciwie będzie działać bo chce abym mógł z poziomu gry wpisywać jakieś słowo lub cyfry jak jest to w przypadku “codelocka” na drzwi w Rust Experimental

    3. Żeby użytkownik mógł wprowadzać dane na bieżąco masz dwie opcję:
      1) Wykorzystać pole tekstowe:
      https://docs.unity3d.com/ScriptReference/GUI.TextField.html
      Problem jest taki, że jego samo wyświetlenie nic nie da, bo… musisz dać temu polu “focus”, żeby dało się w nim wprowadzać dane.

      2) Zostać przy labelu i wtedy symulować focusa, tzn. póki gracz jest w zasięgu pola, to naciskanie przycisków wprowadza tam jakąś treść.

      W obu przypadkach masz problem. Jeśli gracz steruje WSAD i będzie chciał odejść od pola, to będzie dalej wprowadzał tekst.

      Dodatkowo Unity zaleca nie stosować funkcji GUI.Costam w samej grze, jest to raczej klasa dla tworzenia narzędzi i do debugowania. Jak chcesz pracować na elementach z klasy UI.

  6. Można spróbować z flagą.

    W Twoim skrypcie kompletnie nie zgadzają się nawiasy. Posprawdzaj sobie, bo masz pełno otwartych, a nie zamykasz, np.: else nie ma zamknięcia, OnTriggerStay też nie ma zamknięcia.

    Mało tego, ten kod w OnTriggerStay jest zły, bo wg. niego, póki obiekt o tagu Player jest wewnątrz Triggera, to w kółko zmieniasz jego wartość z true na false i z false na true bez przerwy.

    1. Wiem pisalem go na szybko na potszeby kmentarza jeśli będę pisał właściwy to napewno pomyśle jak to zrobic wiem że zrobiłem błąd bo powninenem wywołać jeszcze jedną funkcje OnTriggerExit i w nim ustawić warość na false ale propo wcześniejszego komentarza chyba wiem o co ci chdziło. Czy miałeś na myśli to że jeśli wejdziesz w trigger i otwoży ci się ten string do wpisania i kiedy grasz bedzie chciał wyjść z triggera to będzie nadal spisywał litery Typu “sssssssssssssss” lub “ddddddddddddddddd” i tak dalej. jeśli o to chodziło to w jaki sposób to zrobić odrazu mi przychodzi na myśl zdefiniowanie w skrypcie 2 zmiennych publicznych GameObject jeden z nich posłuży do nowo utworzonej kamery w której właściwie bedzie siedział cały skypt a drugi za to do przechowania naszej kamery gracza.A w funkcji OnGUI dodać przycisk “Exit” następnie pod przyciskiem napisać Camera.SetActive(false); a Maincamera.setActive(true); nastepnie w funkcji OnTriggerEnter ustawić Camera.SetActive(true) a Maincamera.setActive(false) dzięki czemu powinno się przełączać pomiędzy dwoma camerami czy to będzie działało jak myślisz?

Leave a Reply

Your email address will not be published. Required fields are marked *