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

Temat: Proste AI przeciwnika, sztuczna inteligencja

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 – Przybliżenie i dziury po kulach

#11 – Proste AI przeciwnika

#12 – Animacja postaci przeciwnika, Animator

#13 – Menu główne gry. GUI

#14 – Ostatnie szlify i budujemy projekt

Teoria

Dzisiaj przyjrzymy się prostej sztucznej inteligencji. Nie będzie tutaj bardzo zaawansowanych wariantów i interaktywności. Przeciwnik będzie jedynie podążał za graczem, aby mu przywalić z bliska. Oraz będzie miał funkcję śmierci – cudowna funkcjonalność.

Co będzie nam potrzebne? Model jakiegoś przeciwnika, ja pobrałem sobie darmowy model zombie z AssetStora.

Dodatkowo przyda się efekt cząsteczkowy krwi, ale tutaj nie ma problemu, bo przerobimy sobie jeden z efektów ze Standardowych Assetów.

Ruchawy zombie

Pierwszy krok to zaimportowanie modelu do projektu. Nie będę się rozwodził jak to zrobić, w końcu to 11 odcinek! Tym bardziej, jeśli pobieracie go z AssetStore, bo tam wystarczy znaleźć model i kliknąć pobierz.

Teraz czas przygotować nasze zombie. Po pierwsze dodajemu mu tag „Enemy”. Powinniśmy dysponować tym tagiem, jeśli nie, można go łatwo dodać w menu: [Edit -> Project Settings -> Tags nad Layers]. Po drugie, dodajemy naszemu zombie Box Collider, który ustawiamy na trochę większy od modelu, oraz Sphere Collider, który ustawiamy w trybie IsTrigger. Dodatkowo okrągły Collider, powinien być znacznie większy od postaci. Będzie to nasz „zasięg wzroku i słuchu”. Czyli obszar, w którym zombie dostrzega gracza. Na koniec tworzymy sobie nowy skrypt (u mnie EnemyAI.cs) i dodajemy go do modelu. Całość powinna wyglądać mniej więcej tak:

Przykład dobrze przygotowanej postaci przeciwnika.
Przykład dobrze przygotowanej postaci przeciwnika.

Mogło cię przerazić, że skrypt na obrazku, ma już kilka publicznych zmiennych. Nie martw się, nie przegapiłeś połowy poradnika! Po prostu screeen jest z końcowego etapu, tej części.

Jeżeli jesteśmy już przy skrypcie. Czas dodać poruszanie się. Wchodzimy wiec do skryptu EnemyAI.cs. Standardowo, zaczynamy od kilku zmiennych pomocniczych:

Jak łatwo się domyślić, pierwsza to szybkość poruszania się zombie, a druga to odległość z jakiej może zaatakować. Co ciekawe, większość funkcjonalności, nie pojawi się w funkcji Update, a w funkcji OnTriggerStay, czyli tej, która jest wykonywana do póki nasz Trigger wykrywa kolizję. Nasz proces poruszania będzie bardzo prosty. Zombie obraca się w stronę gracza, po czym za nim podąża. O ile skrypt podążania jest banalny, skrypt obrotu jest nieco bardziej skomplikowany. Pierwszy ruch jaki się nasuwa to funkcja LookAt. Spełni swoją funkcję, tyle że obrót będzie natychmiastowy, przez co nierealistyczny. Dlatego, my zrobimy to tak:

Dokumentacja: Quaternion.LookRotation ; Quaternion.Slerp

Klasycznie sprawdzamy, czy to co jest z nami w kolizji to gracz, jeśli tak, to dokonujemy obliczeń. Pierwsza linia, to funkcja LookRotation, która określa jaka musi być rotacja obiektu, tak aby wskazany wektor, był wektorem przodu (Vector3.forward). Dzięki odjęciu od pozycji gracza, pozycji zombie, mamy punkt pomiędzy nimi, dzięki czemu przód, dla zombie, będzie zwrócony w kierunku gracza.

Przez typ modelu jaki wybrałem, był mały problem, otóż jego centrum znajduje się przy ziemi, przez co zombie odchylał się, a przy poruszaniu zdarzało mu się wisieć w powietrzu. Dlatego zablokujemy jego rotację w osiach x i z, tak aby obracał się tylko lewo-prawo. Aby to zrobić, zapamiętujemy sobie pozostałe 2 osie.

Teraz obliczamy sobie końcową rotację. Służy do tego funkcja Slerp, która stopniowo zwiększa wartość rotacji, od startowej (pierwszy parametr), do końcowej (drugi parametr), o wartość podaną w trzecim. Pierwszy parametr, to obecna rotacja zombie, drugi parametr to rotacja, którą liczyliśmy wcześniej, a trzeci parametr to szybkość obrotu razy czas, tak aby obrót był maksymalnie płynny.

Następny krok, to do finalnej rotacji, przywrócić rotację w osiach x i z, sprzed obrotu. Po czym cały wektor podmieniamy. Obecnie zombie powinien być w stanie się obrócić.

Czas dołożyć ruch, który jak wspominałem jest bardzo prosty:

Dokumentacja: Vector3.Distance ; Transform.Translate

Pierwsza linia oblicza dystans pomiędzy dwoma punktami. U nas jest to pozycja zombie i gracza. Później w ifie, sprawdzamy czy zombie jest dostatecznie blisko, aby atakować. Jeżeli dystans jest większy od dystansu wymaganego do ataku, zombie podąża za graczem, co rozwiązuje funkcja Translate. Nie ma tu nic skomplikowanego. Zostawiamy sobie elese, ponieważ wykorzystamy je przy robieniu zadawania obrażeń.  Obecnie zombie powinien już podążać za graczem, będąc obróconym do niego przodem.

Zombie atakuje

Na początek kilka przydatnych zmiennych:

Zadawane obrażenia, opóźnienie ataku (w końcu ręka zombie to nie karabin, żeby zadawać obrażenia bez przerwy), oraz timer.

Wracamy do porzuconego w poprzednim rozdziale else i wzbogacamy je o kod:

Dokumentacja: GameObject.SendMessage

Jeżeli timer jest mniejszy lub równy zero, czyli opóźnienie minęło, to wykonujemy atak, który sprowadza się do wysłania do obiektu gracza wywołania funkcji „takeHit”, za pomocą funkcji SendMessage. Funkcję takeHit napisaliśmy bardzo dawno temu, więc powinna istnieć i działać poprawnie. Dodatkowo dodajemy timerowi opóźnienie. Jednak by wszystko działało, musimy ten timer zmniejszać. Dlatego dodajemy 3 linijki, kompletując funkcję OnTriggerStay:

Jak widać, zwykłe zmniejszanie timera o czas, jeśli jest on większy od zera, czyli jest co zmniejszać.

Zombie ginie

Nasze zombie już podąża za graczem i może mu zrobić krzywdę. Ale co to za zabawa, gdy tylko jedna strona cierpi? Czas oddać zombiakowi. Zaczniemy od ostatniej pomocniczej zmiennej:

Już bardziej oczywistej linijki, chyba nie dało się napisać. Aby wszystko śmigało, robimy sobie bliźniaczą funkcję, do funkcji takeHit stworzonej dla gracza:

Odejmujemy od życia obrażania. Jeśli hp spada poniżej zera, usuwam obiekt zombie. Później można tutaj dodać krzyki agonii przeciwnika i animację śmierci. Nam na tą chwilę tyle wystarcza.

Ale to nie koniec, bo trzeba poprawić nieco skrypt Shooting.cs. Co jest nieco śmieszne, w skrypcie dotyczącym strzelania, nie ma nic o obrażeniach, dlatego na samym początku dodajemy sobie dwie zmienne:

Pierwsza da efekt tryskającej z zombie krwi, a druga zapewni informację ile życia odebrać. Przenosimy się w dół, do kodu, gdzie tworzyliśmy promień strzału i atakowaliśmy przeciwnika. Powinna być tam linijka:

Zmieniamy ją na:

Jest to kod dość podobny do kodu z części 10 tutoriala, gdzie robiliśmy dziurę po kuli, z dodatkowym SendMessage, aby zadać obrażenia.

Została jedna rzecz do poprawy. Otóż domyślnie Raycast zatrzymuje się na każdym colliderze, czyli nawet tym w trybie IsTrigger. Przez co gdy strzelamy do zombie z daleka, trafiamy w collider, odpowiedzialny za wykrywanie gracza. Co jest bez sensu. Profesjonalnym rozwiązaniem, byłoby stworzenie warstwy, na której będzie ten collider, oraz dodanie do raycasta maski, która pominie tę warstwę. Jednak to dużo zachodu, a nasza gra jest prosta, więc zrobimy to w trochę łatwiejszy sposób. Wejdziemy w menu: [Edit -> Project Settings -> Physics]. W panelu Inspector odznaczymy opcję „Raycast Hit Triggers”.

Sprawiamy, że rzucanie promienia pomija Collidery w trybie IsTrigger.
Sprawiamy, że rzucanie promienia pomija Collidery w trybie IsTrigger.

Gotowe! Dla pewności, wrzucam oba modyfikowane dziś skrypty w całości:

Shooting.cs

EnemyAI.cs

 

Poprzednia część <- #10 – Przybliżenie i dziury po kulach

Następna część -> #12 – Animacja postaci przeciwnika, Animator

  • Pingback: Unity3d FPS Tutorial #1 - Tworzymy nowy projekt i przygotowujemy teren | mWin GameDev()

  • Pingback: Unity3d FPS Tutorial #8 - Druga broń | mWin GameDev()

  • Pingback: Unity3d FPS Tutorial #13 - Menu główne gry. GUI | mWin GameDev()

  • bardzonigdy

    1.Czy Twój zombie też przenika przez ściany? Bo ja mam taki problem, dodalem oczywiscie wszedzie collidery.
    2. Pokazesz jak robisz ten efekt tryskającej krwi? :)

    • 1) Szczerze, nie testowałem tego przy kolizji ze ścianami. Jest to najbardziej podstawowe AI i nie uwzględnia ścian itp. Bardziej pokazywałem zasadę, niż tworzyłem kompletne AI. Na pewno zajmę się tym jakoś oddzielnie.
      2) Postaram się to dopisać w weekend. :)

    • Vindict

      Wystarczy, że dodasz zombie rigidbody i zablokujesz obrót w osiach X i Z i zombie nie będą przechodzić przez ściany oraz będą spadać. Oczywiście taki zombie nie ominie ściany ale to przecież zombie – jest głupi XD

  • Pingback: Unity3d FPS Tutorial #4 - Regeneracja życia i energii. Efekty trafienia | mWin GameDev()

  • alpinus4

    Mój potwór coś nie działa. Podchodzi do gracza, zadaje mu jeden cios, a potem stoi lub chodzi za graczem nie zadając ciosów. Help

  • Jacob

    A co w przypadku Zombie 2D? Tworzę grę 2D i tam mój zombie nie potrzebuje wolnego obrotu. Następuje to w ułamku sekundy względem osi X ( 1 lub -1 ). Po dodaniu skryptu do mojego zombi niestety on się nie porusza. Nie podąża za graczem, który ma tag Player. Nie jestem pewien, czy to wina tego obrotu, czy tego, że gra jest konstruowana w 2D. Jak zmienić ten skrypt, aby działał na obiekty 2D?

    • Jacob

      Głównie chodzi mi o cześć z obrotem postaci, cała reszta m.in. poruszanie się, atak, życie, animacje to dla mnie nie problem. Problem pojawia się w obrocie względem osi X. Domyślnie postać patrzy w prawo ( X: 1 ) tak jak postać gracza, czyli jeżeli gracz patrzy w prawo ( X: 1 ) to zombi powinien patrzeć w lewo ( X: -1 ) i podążać za postacią. Niestety nie wiem jak pobrać odpowiednie wartości i skonstruować tę cześć kodu.

    • Jeżeli nie potrzebujesz animacji obrotu ani nic takiego, możesz to zrobić bardzo prosto. Porównaj sobie pozycję zombie i gracza.
      Tzn.:
      PozGracza – PozZombie = X
      I teraz jeśli X > 0 to gracz jest po prawej stronie zombie
      Jeśli X < 0 to gracz jest po lewej stronie zombie.
      Przykład:
      Pozycja gracza: 200, Pozycja zombie: 300
      200 – 300 = – 100
      -100 < 0 – gracz jest po lewej.
      Wtedy obracasz zombie. Nie do końca jestem pewny czy odpowiedziałem na Twoje pytanie.

    • Jacob

      Póki co wszystko rozumiem, jutro przetestuje i dam znać jak ewentualnie będę miał z czymś problem. Dzięki za odpowiedź.

    • Jacob

      Hmmm nie wiem w czym dokładnie leży problem, lecz dla testu usunąłem cały środek, albo inaczej, skrypt wygląda tak:

      using UnityEngine;

      using System.Collections;

      public class EnemyAI : MonoBehaviour {

      public float walkSpeed = 5.0f;

      public float attackDistance = 3.0f;

      public float attackDemage = 10.0f;

      public float attackDelay = 1.0f;

      public float hp = 20.0f;

      private float timer = 0;

      void takeHit(float demage)

      {

      hp -= demage;

      if(hp 0) {

      timer -= Time.deltaTime;

      }

      }

      }

      }

      Problem w tym, że nawet kiedy w obecności collidera znajduję się postać z tagiem „Player” consola nie wysyła wiadomości „OK!”. Czyli problem nie leży w samym obracaniu ( oczywiście trzeba to zmienić, ale z tym sobie poradzę ), ale z tym, że mój zombie nie wykrywa gracza.

    • A jeśli wstawisz Debug.Log tylko do OnTriggerStay, ale przed ifem, to wykrywa w ogóle kolizję?
      – Jeśli nie znajduje nawet kolizji, to polecam sprawdzić czy oba obiekty na pewno mają Collidery i czy obiekt z podanym skryptem ma swój ustawiony IsTigger. Może też być tak, że collider jest lekko przesunięty i postać gracza w niego nie trafia. Np. jeśli gracz i zombie mają wartość w osi z na 0 i 1, to Ty tego nie zauważysz, ale kolizja nie będzie wykryta. Tak samo, jeśli rotacja obiektu z colliderem będzie nieco zmieniona, to collider może prześlizgnąć się obok postaci gracza.
      – Jeśli znajduje kolizje, to problemem może być tag. Np. postać gracza, jednak nie ma go ustawionego, albo czymś się różni, np. nadmiarowa spacja, wielkość liter itp.

    • Jacob

      Mam! Problem nie leżał w colliderach, tagach, czy rotacji. Problem leżał w linijce void OnTriggerStay(Collider other), którą wystarczyło zamienić na void OnTriggerStay2D(Collider2D other) i teraz wszystko działa, jak należy :) Mój zombie podąża za graczem, wszystkie animacje dodane, wszystko działa jak należy :). A wystarczyło tylko zajrzeć do dokumentacji: http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnTriggerStay2D.html

  • xSmouok

    Mój zombie dziwnie kręci się we wszystkie strony.
    Zamiast obracać się jedynie wokół osi Y.
    Proszę o szybką pomoc
    Skrypt skopiowany, działa, Przeciwnik podąża za graczem.

    • A wykorzystany został ten sam model i wszelki ustawienia były zgodne z tutorialem? Jeżeli relacje między obiektami są inne niż powinny, lub użyty jest inny model (np. mający punkt ciężkości w innym miejscu) mogą dziać się dziwne rzeczy.
      Jednak to są strzały, bo bez zobaczenia czegokolwiek, nie wiem co może powodować błąd.

    • xSmouok

      Model jest inny.
      Środek ciężkości nie za bardzo wiem gdzie jest.
      A reszta identycznie jak w tutorialu.

    • Środek ciężkości reprezentuje ten jak by sześcian z osiami, widoczny po zaznaczeniu obiektu. Normalnie powinien być w centrum obiektu, ale zdarzają się modele gdzie jest gdzie indziej (np. gdy tworzyłem tutorial pierwszy obiekt miał go w stopach). Jeżeli jest gdzie indziej, mogą dziać się dziwne rzeczy. Przy okazji warto też sprawdzić czy osie ma normalnie. Domyślnie Oś Z to przód-tył, oś Y to góra-dół i oś X to lewo-prawo. Jeśli model ma obrócone osie, znów będzie zachowywał się dziwnie.

  • Pingback: Unity3d FPS Tutorial #5 - Kamera z bronią i strzelanie | mWin()

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

  • Pingback: Unity3d FPS Tutorial #7 - Zbieranie przedmiotów | mWin()

  • makox

    mój zombie nic nie robi a uzyłem modelu Ethana

    • Wykonałeś wszystkie kroki z całego tutoriala czy dodałeś tylko skrypt do obiektu? Jeżeli tylko dodałeś kod, to raczej nie będzie działało.

  • wojak

    Cześć. Mam taki problem: chciałem aby zombi wykrywał gracz jedynie z przodu (collider przegotowany) ale gdy gracz wyjdzie poza jego zasięg widzenia a ostatnio był widziany z lewej strony (odpowiedni collider) to będzie się obracał w lewo i analogicznie dla prawej strony. Jest jeszcze kwestia hałasu (collider onTrigger na graczu lub granacie), zombie ma jedynie popatrzeć się w tą strone nie iść (przynajmniej na razie). I do tego myślę przyda się rozróżnianie colliderów – dla jednego collidera inne onTriggerStay a dla drugiego inne. Dodam że wszystkie trzy (pole widzenia, lewo, prawo) są dziećmi zombie (zombie -> [dziecko] look (ten głowny) -> [dzieci] left, right). Więc w jaki sposób rozróżnić te wszystkie collidery lub w jaki inny sposób to załatwić?

    • Zmysły (zasięg wzroku, słuch, dotyk) w sztucznej inteligencji robi się nieco inaczej. Nie ma tutaj tak prostego rozwiązania jak wstawić 10 colliderów i je jakoś rozróżniać. Jednak ten temat podejmę w okolicach wakacji tego roku.

    • wojak

      No ok, ale chociaż jakieś naprowadzenie na ten moment? Jakiś toturial? Jakieś klasy? Jeśli byś coś podesłał chętnie popróbuje :)

    • Tutoriala nie podrzucę raczej, ale np. wzrok postaci, przez taki stożek, oblicza się bardziej licząc kąt pomiędzy kierunkiem obserwacji NPC, a postacią gracza.

      Z tego możliwe że wrzucę tutorial z końcem tygodnia albo na początku kolejnego, bo to w sumie fajny temat. ;)

      Słuch możesz faktycznie stworzyć przez triggerowy collider. Po prostu jak postać się w nim znajdzie to stwierdzasz, że jest słyszana.

    • wojak

      Ok wielkie dzięki. Więc do wzroku użyć Quaternion.Angle + Raycast? Raycast aby sprawdzić czy postaci coś nie przysłania.

    • W poniedziałek będzie o tym QuickTip ;)

    • wojak

      Czekam z niecierpliwością :)

  • Maciek

    Hej.
    Pracuję na Unity 5.3, więc tego modelu na którym Ty robiłeś nie dało rady zaimportować do mojego środowiska, dlatego pobrałem inny, zgodny z moją wersją. Cały skrypt pisałem krok po kroczku i jest zgodny w 100% z Twoim, nie wywala żadnych błędów, lecz niestety w żaden sposób nie oddziałuje on na mój model zombie. Ani na poruszanie, ani na zadawanie obrażeń. W czym leży problem, czy kod do mojej wersji zombiaka będzie się różnił jakoś diametralnie, dałoby to radę jakoś ogarnąć?

    • W większości powinno się to zgadzać. Jeżeli po wpisaniu kodu, uruchomisz ponownie Unity i wykryje ono niezgodności między wersjami 4 i 5 to powinien sam to naprostować.

      A jak dokładnie wygląda Twój problem? Zombie się nie porusza czy w ogóle nie reaguje na nic? Czy możesz za pomocą Debug.Log sprawdzić, czy skrypt się w ogóle uruchamia?

    • Maciek

      Witam,
      Dziękuję za odpowiedź, już znalazłem rozwiązanie, skrypt nie działał przez moje niedopatrzenie – funkcja onTriggerStay, zamiast OnTriggerStay. W tym przypadku, wielkość ma znaczenie ;). Szkoda, że unity sam nie koryguje tego typu błędów lub chociaż nie podkreśla, no ale nic, ważne, że problem już zażegnany :)
      Pozdrawiam,
      P.S. Planujesz może kolejną serię poradników z unity?

    • Staram się przynajmniej raz w tygodniu publikować jakiś poradnik. Wydaje mi się to o tyle lepsze, że się wolniej dezaktualizuje.

      Ale rozważam napisać pełny kurs specjalnie pod Unity 5. :)

    • Maciek

      No to świetnie, powodzenia! :)

  • Epos2110

    Hej.
    Ostatnio udało mi się ożywić jednego NPC w mojej grze, ale problem z nim jest taki, że idąc za bohaterem, „wtapia się” we wzgórza i inne wzniesienia stworzone za pomocą standardowego narzędzia terenu . Korzystam z Unity 5.3.
    Jest jakaś sprytna metoda na to, by na uczył się wchodzić na górki zamiast je przenikać ? :)

    • Tutaj dużo zależy od tego jak wygląda Twoja postać i skrypt jej przemieszczania. Najprościej wykorzystać NavMesha. No i ważne, żeby poruszająca się postać miała CharacterController jako Collider.

    • Marcin

      ja odnalazłem sposób musisz nałożyć na twojego zombiaka rigidbody zablokować mu oś X i Z w constrains i zaznaczyć use Gravity :)

  • kubastick

    Skrypt działa,ale mam jeden problem z nim.
    Ponieważ gra jest multiplayer podczas gdy w zasięgu zombi znajdą sie 2 lub więcej graczy ,zombi przestaje sie obracać i zaczyna sunąć prosto przed siebie.

    • W funkcji OnTriggerStay ma problem z określeniem kto jest faktycznie wrogiem. Musisz sobie wymyślić jak ten problem obejść. Np.: Obliczyć, który przeciwnik jest bliżej i tego obrać na cel. Tylko nie obliczaj tego w OnTriggerStay (tylko np. w każdym OnTriggerEnter i OnTriggerExit), bo niepotrzebnie obciążysz za bardzo procesor.

    • kubastick

      A naprowadzisz mnie jak to zrobić ?
      Bo kompletnie nie mam pojęcia jak rozróżnić dane pochodzące od kilku graczy.

    • SI nie musi wiedzieć w sumie za dużo. Ja bym sobie zrobił tak:
      1) Deklarujesz tablicę/listę dla SI
      2) OnTriggerEnter jeśli wykryjesz obiekt o tagu „gracz” dodajesz ten obiekt do listy
      3) OnTriggerExit jeśli wykryjesz obiekt o tagu „gracz” to go usuwasz z listy
      4) W OnTriggerEnter i OnTriggerExit po dodaniu/usunięciu obiektu przeszukujesz sobie listę i szukasz tego „najbliżej” i jego obierasz na cel.

    • kubastick

      Dobra wielkie dzięki poradziłem sobie

  • Marcin

    Witam, to znów ja mam właściwie 2 problemy pierwszy taki że zombie nie zadaje mi obrażeń po drugie zombie nadal mi lewituje w powietrzu i wchodzi pod ziemię nie wiem co zrobić pomoże ktoś?? by the way wywala mi 1 błąd w skrypcie Enemy AI a dokładniej:
    if (timer <= 0)
    {
    other.SendMessage("takeHit", attackDemage);
    timer = attackDelay;
    }
    mimo to gra się uruchamia…

  • Piotrow

    Witam, robię AI dla przeciwnik, który ma znaleźć gracza potem w niego strzelać, większość problemów rozwiązałem, zostało tylko omijanie przeszkód. Chodzi o to że gdy gracz schowa się za rogiem, albo skrzynią, oponęt ją ominął jednocześnie nie gubiąc gracza. Jak to zrobić – nie wiem. Dodam że do wykrywania kolizji dokładnie przed obiektem użyłem RayCasta(co chyba nie było zbyt rozsądne), a gra jest idealnie płaska więc można pominąć rotacje w osi y(jak w grze 2D).

    • Prawdopodobnie najprostszym mechanizmem, który by to rozwiązał to użycie NavMesha. Wtedy mechanizm Unity zajmie się omijaniem przeszkód. Ty mu jedynie ustalasz cel, czyli w tym przypadku pozycję gracza. Jedyne co trzeba zrobić, to dobrze dostosować środowisko – tzn. poustawiać ściany na statyczne, ruchomym przeszkodom dodać komponent NavMesh Obstacle. Bez tego agent będzie przechodził przez ściany, albo zachowywał się w inny nieprzewidziany sposób.

      Jeżeli chcesz zostać przy swoim rozwiązaniu to technika wygląda tak, że:
      – Agent zna położenie gracza i stara się iść w jego kierunku
      – Gdy raycast informuje go, że na trasie będzie przeszkoda, to odbijasz kierunek ruchu nieco w bok, aż przeszkody nie będzie. Ale efekt, będzie taki, że agent będzie dziwnie szedł wzdłuż ściany, co nie wygląda fajnie, dlatego polecam jednak NavMesh.

    • Piotrow

      Zastosowałem agenta i wywala mi błąd

      „SetDestination” can only be called on an active agent that has been placed on a NavMesh.
      UnityEngine.AI.NavMeshAgent:SetDestination(Vector3)

    • Twój agent to pewnie prefab i wywołujesz na nim setDestination zanim pojawi się na scenie, czego nie da się zrobić.

    • Piotrow

      co powinienem zrobić?

    • Pokaż przez pastebin skrypt którym spawnujesz przeciwnika oraz sam skrypt AI. :)

    • Piotrow

      Otóż nie, przeciwnik się nie spawnuje, on, jak i skrypt w nim istnieje od uruchomienia gry

    • Hmmm…
      A wypaliłeś NavMesh? Agent znajduje się na nim? Agent ma komponent NavMeshAgent i ten komponent jest aktywny? W której funkcji ustawiasz setDestination?

    • Piotrow

      Już wiem – problem w tym moja gra składa się głównie z objektów 2D, z tego co widzę NavMesh tu nie zadziała. Jest jakiś sposób żeby to sprawnie obejść(pomijając podłożenie bloku pod powierzchnie), albo czy istnieje jakaś inna „ładna” metoda

    • Możesz podesłać screen swojej gry, wtedy będzie łatwiej się zorientować w temacie, ale nie mogę obiecać, że coś wymyślę, jeśli gra będzie bardzo nietypowa.

    • Piotrow
    • Sprawdzi się tutaj algorytm A*, jednak nie jest to na tyle proste zagadnienie, żebym wyjaśnił jak to zrobić w komentarzu.

    • Piotrow

      Bardzo dziękuje.
      Chyba dam sobie rade.

  • Andrzej Morański

    Cześć. Trochę z innej beczki tutaj wskoczę bo piszę skrypt AI do platformy 2D. Mam głównego bohatera który sobie może zbierać monety na planszy 2D. Wszystko na Triggerach wyzwalam bo sprawa ogólnie prosta. Bohater ma pomocnika, który wiernie się za nim porusza po planszy. Pomocnik ma za zadanie zbierać również monety ale tylko w momencie kiedy osiąga on pozycję x = targetowie zamieszczonemu w Player. Ogólnie z kodem sobie już poradziłem bo wszystko pięknie się wyzwala tylko problem jest z podlatywaniem do każdej monety kiedy wejdzie w kolizję z pomocnikiem. Myślałem o pętli for ale nie mam pomysłu jak to zapisać tak dobrze aby działało w odpowiedni sposób jak na filmiku niżej: https://www.youtube.com/watch?v=BpnvtIrcdNI

    Na filmie działa to tak jak chcę ale to był błąd dla mnie bo zastosowałem rodzic/dziecko przez co nie mogłem sobie później poradzić z animacją bo Animator i tak czyta z rodzica pozycje lokalną przez co moja animacja nigdy się nie odtwarzała na miejscu. Będę też wdzięczny za jakiekolwiek podpowiedzi gdyż jestem samoukiem i od dwóch lat już pracuję nad tą grą po godzinach więc doceniam każdą krytykę czy dobre zdanie. Pozdrawiam, Andrzej.

    • Cześć, wybacz, ale nie do końca z opisu rozumiem gdzie dokładnie leży Twój problem? Widzę na filmie jak chcesz żeby działało, ale nie do końca rozumiem, jak to działa obecnie i z czym jest problem?

    • Andrzej Morański

      Więc udało się rozwiązać problem, dopracowałem animacje i chyba niepotrzebnie zawracałem głowę :) Czasem wystarczy odpocząć i rozwiązanie samo przyjdzie w głowie :)