Unity3d FPS Tutorial, czyli tworzymy własną grę FPS od podstaw z wykorzystaniem silnika Unity3d.

Temat: Rzut granatem i seria z karabinu

Spis treści

Jeżeli nie używałeś do tej pory Unity3d:

#0 – Podstawy Podstaw

FPS Tutorial:

#1 – Tworzenie nowego projektu i narzędzie terenu

#2 – Sterowanie postacią

#3 – Życie, pancerz i wytrzymałość postaci

#4 – Regeneracja życia i energii. Efekty trafienia

#5 – Kamera z bronią i strzelanie

#6 – Przeładowanie i amunicja

#7 – Zbieranie przedmiotów

#8 – Druga broń

#9 – Rzut granatem i seria z karabinu

#10 – Celowanie i dziury po kulach

#11 – Przeciwnik z prostym AI, sztuczna inteligencja

#12 – Animacja postaci przeciwnika. Animator

#13 – Menu główne gry. GUI

#14 – Ostatnie szlify i budujemy projekt

Teoria

Ostatnio dodaliśmy sobie drugą broń, jaką był karabin. Teraz zmienimy go w prawdziwy karabin, dodając opcję strzelania serią. Dodatkowo wzbogacimy grę o rzut granatem!

Co musisz sobie przygotować? Model granatu oraz dźwięk wybuchu: FPSTutorial#9 – Assets

Strzelanie serią

Jednak zaczniemy od poprawy systemu strzelania. Wystarczy kilka drobnych zmian w kodzie skryptu Shooting.cs. Najpierw dodajemy sobie kilka zmiennych pomocniczych:

Zmienna boolowska określa, czy dana broń może strzelać serią. Ustawiona na publiczną, żeby każdej broni, móc dobrać ten parametr niezależnie. Pozostałe dwie to liczniki odpowiadające za opóźnienie. Ich zastosowanie wyjaśnię, gdy pojawią się w kodzie.

Jedyne co zmienimy w skrypcie to funkcja Update, gdzie znajdował się kod odpowiedzialny za wystrzał. Wygląda on teraz tak:

Zmieniły się w sumie tylko dwie pierwsze linię. Rozbiłem naszego ifa, na dwa, tylko po to, żeby kompletny if nie miał 120 znaków długości. Posiadanie conajmniej jednej kuli i brak przeładowywania jest niezależnym od trybu strzelania warunkiem, więc wyciągnąłem go na zewnątrz.

Warunek z kolejnego ifa, omówimy częściami. Może nie widać tego od razu, ale if składa się z jednego złożonego warunku && prosty warunek. Warunek prosty to:

Czyli licznik opóźnienia musi wynosić 0. Złożony warunek natomiast:

Posiada dwa warunki oddzielone znakiem OR (a angielskiego: lub), który reprezentują dwie pionowe kreski (||). Aby taki warunek został spełniony, co najmniej jedna ze stron musi być prawdą. Po lewej został nasz stary kod, mówiący o pojedynczym naciśnięciu przycisku. Drugi warunek znów jest złożony. Jest realizowany gdy przycisk strzału jest wciśnięty, oraz gdy broń zezwala na strzał serią.

Ostatni zmieniony kod, to dodanie do licznika opóźnienia, czasu opóźnienia dla danej broni. Musimy dodać opóźnienie z jednego powodu. Gdy tego nie zrobimy, funkcja GetButton, wykona się praktycznie w każdej klatce gdy mamy wciśnięty klawisz myszy. Oznacza to tyle, że amunicja z karabinu skończy się w kilka sekund. Dodając opóźnienie, zwiększamy realizm działania broni.

Na koniec zostało minimalizować ten licznik. Gdzieś w funkcji Update należy dopisać:

Jeżeli licznik jest większy od zera, to go zmniejszamy. Nic trudnego. Teraz wystarczy wrócić do Unity i ustawić pistoletowi i karabinowi odpowiednie wartości nowych zmiennych. U mnie sprawdziło się: Opóźnienie dla pistoletu: 0.1 i opóźnienie dla karabinu 0.5 oraz oczywiście opcja strzelania serią.

Przygotowanie granatu

Na samym początku musimy sobie dobrze przygotować prefabrykant granatu. Dodajemy obiekt do sceny, następnie dodajemy mu dwa komponenty: Rigidbody [Component -> Physics -> Rigidbody] oraz collider [Component -> Physics -> Sphere Collider]. Dzięki temu, granat będzie pod wpływem fizyki, oraz nie będzie przenikał przez ściany.

Mając taki granat, przeciągamy go do pola Project, tworząc Prefabrykant. Można teraz usunąć granat ze sceny. Operować będziemy już tylko na prefabrykancie. Teraz czas stworzyć skrypt, który obsłuży granat. Tworzymy skrypt: Grenade.cs i przypisujemy go do prefabrykantu (wystarczy przeciągnąć).

Kod ma tylko 4 pomocnicze zmienne:

Pierwsza to licznik, odliczający po jakim czasie od rzutu granat wybuchnie. Druga, to emiter cząsteczek, który zagwarantuje efektowny wybuch. Trzecia to dźwięk wybuchu, a ostatnia to emiter dźwięku (zaraz do tego dojdziemy). Skrypt nie obsługuje póki co obrażeń wroga. Zajmiemy się tym później. Teraz czas na kod w funkcji Update, czyli faktyczne działanie granatu:

Po utworzeniu, timer zaczyna odliczać w dół. Jeśli licznik zejdzie do zera, oznacza to, że granat ma wybuchnąć. Korzystamy tutaj, aż dwukrotnie z funkcji Instantinate, która pozwala utworzyć dynamicznie dowolny obiekt. Pierwsza zmienna to obiekt, który tworzymy, druga to pozycja w jakiej ma się pojawić, a trzecia to rotacja.

Na samym początku tworzymy sobie instancję obiektu AudioSource – teraz wyjaśnia się explosionHolder – ustawiamy go w miejscu gdzie znajduje się granat. Jako clip ustawiamy mu dźwięk wybuchu i uruchamiamy ten dźwięk. Później również tworzymy instancję emitera cząsteczek wybuchu. Na koniec niszczymy obiekt.

Nasuwa się pytanie: „Po co tyle zachodu?”. Musimy stworzyć emiter dźwięku i cząstek w ten sposób, ponieważ gdyby były zależne od samego obiektu granatu, to w momencie wywołania funkcji Destroy, emiter i dźwięk urwałyby się nagle. Taki ruch sprawia, że dźwięk odegra do końca, tak jak emisja cząsteczek.

Teraz czas uzupełnić dane skryptu z poziomu Unity: GrenadeSound to zwykły dźwięk wybuchu (dostępny w paczce dostarczonej przeze mnie na początku wpisu), explosion, znajdziesz w paczce standardowych assetów: Standard Assets -> Particles -> Legacy Partilces -> Explosion. Explosion Holder, musisz sobie utworzyć. Będzie to o tyle proste, że jest to zwykły pusty gameObject (Ctrl + Shift + N), który ma dodany komponent AudioSource [Component -> Audio -> AudioSource].

Granat powinien pięknie działać. Czas nim rzucić.

Rzut granatem

Aby wykonać rzut stworzymy prosty skrypt (GrenadeThrow.cs) przypisany do obiektu MainCamera. Jeżeli chodzi o zmienne, znów jest prosto:

Pierwsza zmienna jest typu Rigidbody, bo oczekujemy takiego komponentu w prefabie, który chcemy rzucić jako granat. Druga, to zwykły int trzymający maksymalną liczbę granatów.

Sam kod do rzutu jest banalnie prosty. Gdy gracz naciśnie F i ma jakieś granaty, tworzymy sobie dobrze znaną nam funkcją Instantinate granat (Dzięki przypisaniu skryptu do kamery, możemy skorzystać z rotacji i pozycji obiektu, do którego jest przypisany skrypt, co w efekcie da rzut granatu w dobrym kierunku). Drugi krok to rzucenie granatu. Dzięki temu, że granat ma Rigidbody, możemy użyć funkcji AddForce, która go „pchnie” w odpowiednim kierunku. AddForce, można sobie wyobrazić jak kopnięcie piłki. Jednorazowo nadaje kierunek i moc poruszania obiektu.

Przez to, że mamy dobrze ustawiony skrypt, możemy użyć Vector3.forward, który wskazuje przód, bez zastanawiania się, w którą stronę mamy pchnąć obiekt. Na koniec zmniejszamy zapas granatów.

Teraz wystarczy w Unity, jako zmienną Greanade, przypisać wcześniej przygotowany prefab.

Podsumowanie

Dzisiaj zmieniliśmy nasz jednostrzałowy karabin, w strzelającą seriami maszynę do zabijania. Dodatkowo stworzyliśmy opcję rzutu granatem. Granat póki co nie zadaje obrażeń, a gracz posiada tylko 2 sztuki.

Jako zadanie domowe, możesz teraz dorobić skrypt zbierania dodatkowych granatów, na podstawie tutoriala, gdzie zbieraliśmy amunicję lub broń. Powodzenia!

Poprzednia część <- #8 – Druga broń

Kolejna część -> #10 – Celowanie i dziury po kulach

  • Pingback: Unity3d FPS Tutorial #12 - Animacja postaci przeciwnika. Animator | mWin GameDev()

  • Pingback: Unity3d FPS Tutorial #6 - Przeładowanie broni i amunicja | mWin GameDev()

  • Pingback: Unity3d FPS Tutorial #3 - Życie, pancerz i wytrzymałość postaci | mWin()

  • TetraHydroSoft

    Dziękuję, Zarąbisty skrypt nawet nie wiesz jaki miałem zaciesz na mordzie jak zadziałał :D tylko mam dylemat pomógł byś mi zdefiniować animacje w tym skrypcie do rzutu granatem? :D i jeszcze chciał bym również zrobić zmiane owych granatów, przemyślałem nawet jak ten skrypt powinien działać ale nie wiem jak go napisać. Otóż chciał bym żeby po kliknięciu odpowiedniego przycisku załużmy 8 i 9 zmieniał mi się automatycznie prefab granatu powiedzmy mam w skrypcie GrenadeThrow mam przypisany granat wybuchowy i kiedy klikne 9 zmienia mi się na prefab na granat dymny a kiedy spowrotem kliknie 8 zmienia mi się znów na granat wybuchowy. czy da się coś takiego zrobić? próbowałem jeszcze skopiować owy kod żeby on działał na zadzie że pod F mam granat odłamkowy a pod G mam dymny ale wywalało mi błędy i koniec końców się poddałem bo nie wiedziałem w czym jest problem. Z góry dziękuję za pomoc ! :D

    • Cieszę się, że mogłem pomóc :)

      Jeśli chodzi o takie przełączanie się między obiektami, to zobacz sobie na skrypt zmiany broni. Możesz sobie granaty przypisać w tamtym miejscu i powinno to hulać. Skopiowanie skryptu też powinno być dobrą metodą, tylko pewnie pojawiły się jakieś duplikaty – przykładowo jak skopiowałeś całkiem kod, to już na wstępie masz duplikat, że oba skrypty mają taką samą nazwę, a nie możesz mieć 2 skryptów o tej samej nazwie. Usuwając błędy, powinieneś znaleźć rozwiązanie. :)

  • TetraHydroSoft

    Właściwie to nie wiem o co chodziło skopiowałem ten kod i stworzyłem te dwie nowe zmienne i zmieniłem nieco kod że on wyglądał w ten sposób lecz nadal nie działa :/ KOD:
    Mono blablblalblablabla… {
    public Rigidbody grenade;
    public Rigidbody grenadesmoke;

    private int currentgrenades;
    private int currentgrenadesmoke;

    void Start() {

    }

    void Update() {
    if(Input.GetKeyDown(KeyCode.F) && currentgrenades > 0) {
    Rigidbody clone = Instantiate(grenade, transform.position, transform.rotation) as Rigidbody;
    clone.AddForce(transform.TransformDirection(Vector3.forward * 1000));
    currentGrenades–;
    }
    if(Input.GetKeyDown(KeyCode.G) && currentgrenadessmoke > 0) {
    Rigidbody clone = Instantiate(grenadesmoke, transform.position, transform.rotation) as Rigidbody;
    clone.AddForce(transform.TransformDirection(Vector3.forward * 1000));
    currentGrenadesmoke–;
    }
    }
    }

    • Bez treści błędów, albo opisania co znaczy, że nie działa, nie będę w stanie pomóc – nie mam pojęcia czego szukać. ;)
      Btw. Jak wrzucasz kod, to najlepiej przez pastebin, wtedy lepiej go widać i łatwiej czytać. :)