Prefaby ;-;

Ogólne pytania na temat Unity3D

Prefaby ;-;

Postprzez MrNolife12 » 05 Lut 2019, 16:28

Unity 2D
Więc mam sobie wroga, podpięty pod niego skrypt szukający i podążający za graczem a w nim pole <public GameObject Gracz;>
do którego przeciągam gracza ze sceny. Jeśli i gracza i wroga wrzucę na scenę a następnie przeciągnę gracza do tego właśnie pola, jest wszystko dobrze (wróg podąża). Ale gdy do prefabu wroga przeciągnę prefab gracza właśnie do pola GameObject to po przeciągnięciu obu prefabów do sceny wróg leci do początkowej pozycji gracza i się zatrzymuje.
MrNolife12
 
Posty: 3
Rejestracja: 05 Lut 2019, 16:22
Has thanked: 0 time
Been thanked: 0 time

Re: Prefaby ;-;

Postprzez Wichur » 05 Lut 2019, 17:22

Jeśli dobrze zrozumialem to probujesz przeciągać prefab do prefaba, i to nie zadziala. Bo w momencie gdy wstawiasz na scene to tworzy sie jego klon.
Aby to zadzialalo musisz stworzyc jakis skrypt co będzie tym kontrolował
Awatar użytkownika
Wichur
 
Posty: 118
Rejestracja: 19 Gru 2017, 10:33
Miejscowość: Pruszcz Gdański
Has thanked: 0 time
Been thanked: 0 time

Re: Prefaby ;-;

Postprzez Hostur » 06 Lut 2019, 11:41

Prefab to tylko template obiektu, a Ciebie w grze interesuje konkretna instancja tego prefaba.
Przeciąganie targetu dla stworka w edytorze jest bardzo złym pomysłem musisz to robić dynamicznie w praktyce najprościej Ci będzie zrobić to na OnTriggerEnter stworka, używając jakiegoś collidera o wielkości odpowiadającej zasięgowi wzroku wroga.

Tak na prawdę można to zrobić na masę różnych sposobów ale taki podstawowy na start powinien być ok.
Zobacz jakiś tutorial z Triggerów i pamiętaj o rigidbody.
Hostur
 
Posty: 682
Rejestracja: 05 Sie 2015, 07:36
Has thanked: 0 time
Been thanked: 0 time

Re: Prefaby ;-;

Postprzez MrNolife12 » 10 Lut 2019, 18:57

Więc generalnie to mój prefab gracza jest ciągle na planszy a wrogowie są generowani Instantiate.
Chciałbym zrobić to jakoś w ten sposób żeby generowani wrogowie mieli jako swój cel przypisaną tą właśnie " konkretną instancję prefaba gracza". Tylko jak? Jestem zielony i sklejam grę ze skrawków kodu z internetu
MrNolife12
 
Posty: 3
Rejestracja: 05 Lut 2019, 16:22
Has thanked: 0 time
Been thanked: 0 time

Re: Prefaby ;-;

Postprzez Hostur » 11 Lut 2019, 07:11

No i robisz prawie dobrze od czegoś trzeba zacząć, tylko jak już sklejasz to staraj się rozumieć wszystko czego używasz.
Tak najprościej na start dodaj graczowi jakiś unikalny tag np Player.


Tam gdzie potrzebujesz zdobyć referencję na gracza dodaj w starcie taki fragment

Kod: Zaznacz wszystko
private Transform _targetPlayer;
private void Start()
{
  _targetPlayer = GameObject.FindGameObjectWithTag("Player").transform;
}


jako że jest to FindGameObject a nie FindGameObjects to zwróci Ci pierwszy znaleziony na scenie obiekt z tagiem Player lub nulla jeśli nic nie znajdzie.

Nie nadużywaj jednak takich funkcji jak Find bo wszystkie one są kosztowne.
Pobieram jeszcze transform (co pod spodem jest wywołaniem GetComponent<Transform>()), jeśli używasz transformu np w pętli update żeby podążać stworkami za graczem to pobieraj ten transform wcześniej tak jak w przykładzie żeby unikać GetComponent wewnątrz funkcji Update.

To jeden z bardzo wielu sposobów w jaki możesz to zrobić.
Hostur
 
Posty: 682
Rejestracja: 05 Sie 2015, 07:36
Has thanked: 0 time
Been thanked: 0 time

Re: Prefaby ;-;

Postprzez patrykas1000 » 13 Lut 2019, 14:28

Pobieram jeszcze transform (co pod spodem jest wywołaniem GetComponent<Transform>()), jeśli używasz transformu np w pętli update żeby podążać stworkami za graczem to pobieraj ten transform wcześniej tak jak w przykładzie żeby unikać GetComponent wewnątrz funkcji Update.


A to czasem nie jest tak że już to poprawili?

Kiedyś był szeroki wybór funkcji np. GameObject.renderer, GameObject.rigidbody, GameObject.light, itp... wszystkie, razem z transform, korzystały z GetComponent. Ale (na całe szczęście) oznaczyli je jako przestarzałe a potem nawet usunęli. Gdy to się stało zostało tylko GameObject.transform i z tego co wiem to aktualnie jego wywołanie nie korzysta z GetComponent tylko z przygotowanej zmiennej ponieważ transform jest jedynym komponentem który ma każdy GameObject na scenie i być inaczej nie może.

EDIT:
Wykonałem pomiary i szczerze jestem trochę zaskoczony.
Oto pomiary a moja analiza i interpretacja poniżej
Kod: Zaznacz wszystko
Ilość wywołań na jeden test: 2 000 000
Ilość testów: 100
Jednostka czasu: tick
Użyta funkcja pomiarowa: Stopwatch (z System.Diagnostics)



GameObject.transform: AVG:       1968191,82
GameObject.transform: min:       1936875
GameObject.transform: max:       2129108

var transform: AVG:          1590517,08
var transform: min:          1564476
var transform: max:          1751290

GetComponent<transform>: AVG:    2399476,96
GetComponent<transform>: min:    2365499
GetComponent<transform>: max:    2530745


var transform: AVG:          1590517,08
GameObject.transform: AVG:       1968191,82
GetComponent<transform>: AVG:    2399476,96


Do wykonania tych pomiarów skorzystałem z kodu:
Kod: Zaznacz wszystko
using UnityEngine;

public class PerformanceTest : MonoBehaviour
{
   int iter = 2_000_000;
   int test_count = 100;
   int test = 0;
   long sum = 0;
   long max = 0;
   long min = long.MaxValue;
   //Transform trans;

    void Start()
    {
      //trans = transform;
    }

    void Update()
    {
      Vector3 move = new Vector3(.1f, .1f, .1f);
      System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
      sw.Start();

      for(int i = 0 ; i < iter ; i++){
         //trans.position += move;
         //transform.position += move;
         GetComponent<Transform>().position += move;
      }

      sw.Stop();
      long t = sw.ElapsedTicks;
      if(min>t) min = t;
      if(max<t) max = t;
      sum += t;

      Debug.Log(string.Format("{0}. GetComponent<transform>: (iter: {1}: {2} ticks", test, iter, t));

      if(++test==test_count){
         double avg = 1.0*sum / test_count;
         Debug.Log(string.Format("GetComponent<transform>: AVG: {0}", avg));
         Debug.Log(string.Format("GetComponent<transform>: min: {0}", min));
         Debug.Log(string.Format("GetComponent<transform>: max: {0}", max));
         UnityEditor.EditorApplication.isPlaying = false;
      }
    }
}

Dla łatwości przekazu
sposób z polem GameObject.transform nazwę sposobem z polem
sposób z zmienną transform przypisaną w Start() nazwę sposobem ze zmienną
sposób z GetComponent po prostu - sposób z GetComponent

Zastosowanie pola okazuje się być mniej wydajne niż skorzystanie ze zmiennej , ale co ciekawsze, rozwiązanie z polem nadal jest wydajniejsze od GetComponent.

Największy czas testu dla zmiennej jest znacząco mniejszy niż najkrótszy dla testu z polem.
Największy czas testu dla pola jest również znacząco mniejszy niż najkrótszy dla testu z GetComponent.


Wnioski:
Użycie GameObject.transform co prawda jest wydajniejsze od GetComponent<Transform> ale jednak najwydajniej stworzyć jest zmienną która przechowuje transform. Lecz pamiętajmy że czasem trzeba też troszczyć się bardziej o pamięć niż moc obliczeniową ;)


Test przeprowadzony na pustej scenie, bez kamery, jedyny obiekt na scenie to pusty obiekt z komponentami (by spowolnić ewentualne działanie GetComponent) (komponenty nałożyłem losowo: EventSystem,, WorldAnchor, AudioListener, Animation, Grid, ScaleConstraint i oczywiście PerformanceTest załączony wyżej)
patrykas1000
 
Posty: 334
Rejestracja: 16 Sie 2013, 21:35
Has thanked: 0 time
Been thanked: 0 time

Re: Prefaby ;-;

Postprzez Marcin » 13 Lut 2019, 20:30

patrykas1000 napisał(a):
Pobieram jeszcze transform (co pod spodem jest wywołaniem GetComponent<Transform>()), jeśli używasz transformu np w pętli update żeby podążać stworkami za graczem to pobieraj ten transform wcześniej tak jak w przykładzie żeby unikać GetComponent wewnątrz funkcji Update.


A to czasem nie jest tak że już to poprawili?

Kiedyś był szeroki wybór funkcji np. GameObject.renderer, GameObject.rigidbody, GameObject.light, itp... wszystkie, razem z transform, korzystały z GetComponent. Ale (na całe szczęście) oznaczyli je jako przestarzałe a potem nawet usunęli. Gdy to się stało zostało tylko GameObject.transform i z tego co wiem to aktualnie jego wywołanie nie korzysta z GetComponent tylko z przygotowanej zmiennej ponieważ transform jest jedynym komponentem który ma każdy GameObject na scenie i być inaczej nie może.

EDIT:
Wykonałem pomiary i szczerze jestem trochę zaskoczony.
Oto pomiary a moja analiza i interpretacja poniżej
Kod: Zaznacz wszystko
Ilość wywołań na jeden test: 2 000 000
Ilość testów: 100
Jednostka czasu: tick
Użyta funkcja pomiarowa: Stopwatch (z System.Diagnostics)



GameObject.transform: AVG:       1968191,82
GameObject.transform: min:       1936875
GameObject.transform: max:       2129108

var transform: AVG:          1590517,08
var transform: min:          1564476
var transform: max:          1751290

GetComponent<transform>: AVG:    2399476,96
GetComponent<transform>: min:    2365499
GetComponent<transform>: max:    2530745


var transform: AVG:          1590517,08
GameObject.transform: AVG:       1968191,82
GetComponent<transform>: AVG:    2399476,96


Do wykonania tych pomiarów skorzystałem z kodu:
Kod: Zaznacz wszystko
using UnityEngine;

public class PerformanceTest : MonoBehaviour
{
   int iter = 2_000_000;
   int test_count = 100;
   int test = 0;
   long sum = 0;
   long max = 0;
   long min = long.MaxValue;
   //Transform trans;

    void Start()
    {
      //trans = transform;
    }

    void Update()
    {
      Vector3 move = new Vector3(.1f, .1f, .1f);
      System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
      sw.Start();

      for(int i = 0 ; i < iter ; i++){
         //trans.position += move;
         //transform.position += move;
         GetComponent<Transform>().position += move;
      }

      sw.Stop();
      long t = sw.ElapsedTicks;
      if(min>t) min = t;
      if(max<t) max = t;
      sum += t;

      Debug.Log(string.Format("{0}. GetComponent<transform>: (iter: {1}: {2} ticks", test, iter, t));

      if(++test==test_count){
         double avg = 1.0*sum / test_count;
         Debug.Log(string.Format("GetComponent<transform>: AVG: {0}", avg));
         Debug.Log(string.Format("GetComponent<transform>: min: {0}", min));
         Debug.Log(string.Format("GetComponent<transform>: max: {0}", max));
         UnityEditor.EditorApplication.isPlaying = false;
      }
    }
}

Dla łatwości przekazu
sposób z polem GameObject.transform nazwę sposobem z polem
sposób z zmienną transform przypisaną w Start() nazwę sposobem ze zmienną
sposób z GetComponent po prostu - sposób z GetComponent

Zastosowanie pola okazuje się być mniej wydajne niż skorzystanie ze zmiennej , ale co ciekawsze, rozwiązanie z polem nadal jest wydajniejsze od GetComponent.

Największy czas testu dla zmiennej jest znacząco mniejszy niż najkrótszy dla testu z polem.
Największy czas testu dla pola jest również znacząco mniejszy niż najkrótszy dla testu z GetComponent.


Wnioski:
Użycie GameObject.transform co prawda jest wydajniejsze od GetComponent<Transform> ale jednak najwydajniej stworzyć jest zmienną która przechowuje transform. Lecz pamiętajmy że czasem trzeba też troszczyć się bardziej o pamięć niż moc obliczeniową ;)


Test przeprowadzony na pustej scenie, bez kamery, jedyny obiekt na scenie to pusty obiekt z komponentami (by spowolnić ewentualne działanie GetComponent) (komponenty nałożyłem losowo: EventSystem,, WorldAnchor, AudioListener, Animation, Grid, ScaleConstraint i oczywiście PerformanceTest załączony wyżej)


To podaj informację, czy to edytor czy build. Jeżeli nie build, to zrób testy z builda.
Marcin
 
Posty: 295
Rejestracja: 25 Lip 2016, 10:44
Has thanked: 0 time
Been thanked: 0 time

Re: Prefaby ;-;

Postprzez MrNolife12 » 13 Lut 2019, 21:18

Zamieszczam tutaj skrypt wroga. Są tutaj dwie funkcje, w Update() znajduje się działający, skomplikowany jak dla mnie i średnio zrozumiały skrypt odpowiadający za podążaniem za Graczem, za to w OnTriggerEnter znajduje się skrypt odpowiadający za wykrywanie kolizji z Pociskiem gracza i mały system punktów życia. Gdybyście byli w stanie, oczywiście w miarę możliwości pomóc mi skrypty uprościć (szczególnie ten od podążania za graczem), gdyż wydają mi się one nazbyt zagmatwane. Co do _targetGracz, jak go użyć? Czym w ogóle jest _target?

Kod: Zaznacz wszystko
Using System.Collections;
using UnityEngine;


public class WrógScr : MonoBehaviour
{
    Transform my;
    Rigidbody2D body;

    public GameObject Gracz;
    public GameObject Wróg;
    public float speed = 2f;
    private float minDistance = 1f;
    private float range;
   private float healthPoints = 100;
    private Transform _targetGracz;

    private void Start()
    {
        _targetGracz = GameObject.FindGameObjectWithTag("Gracz").transform;
    }

    void Awake()
    {
        my = GetComponent<Transform>();
        body = GetComponent<Rigidbody2D>();
    }


    void Update()
    {
        float AngleRad = Mathf.Atan2(Gracz.transform.position.y - my.position.y, Gracz.transform.position.x - my.position.x);
        float angle = (180 / Mathf.PI) * AngleRad;

        body.rotation = angle;

        transform.Translate(Vector3.forward * 5 * Time.deltaTime);

        range = Vector2.Distance(transform.position, Gracz.transform.position);

        if (range > minDistance)
        {
            Debug.Log(range);

            transform.position = Vector2.MoveTowards(transform.position, Gracz.transform.position, speed * Time.deltaTime);
        }
    }


    public void OnTriggerEnter2D(Collider2D hit) {
   
      if (hit.tag == "Pocisk") {
         healthPoints -= 25;
         Destroy (hit.gameObject, 0);
         if (healthPoints <= 0) {
            Destroy (gameObject, 0);
         }
      } else if (hit.gameObject.tag == "Player") {
         Destroy (gameObject, 0);
      }
   }

 }
MrNolife12
 
Posty: 3
Rejestracja: 05 Lut 2019, 16:22
Has thanked: 0 time
Been thanked: 0 time

Re: Prefaby ;-;

Postprzez patrykas1000 » 14 Lut 2019, 02:07

Kod: Zaznacz wszystko
    void Update()
    {
      //To trudno nazwać czymś skomplikowanym w pojęciu programowania
      //to jest zwykła matematyka - trygonometria
      // angleRad = atan2( y1 - y2, x1 - x2)
      // angleDeg = (180/PI) * angleRad
      // 180/PI to magiczna wartość którą jak pomnożysz kąt w stopniach to otrzymasz kąt w radianach
        float AngleRad = Mathf.Atan2(Gracz.transform.position.y - my.position.y, Gracz.transform.position.x - my.position.x);
        float angle = (180 / Mathf.PI) * AngleRad;
      
      //ustawienie obrotu obiektu
        body.rotation = angle;
      
      //Również matematyka - wektory
      //Przesuń obiekt o wskazany wektor
      //Vector3.forward to wektor [0,0,1]
      //Time.deltaTime to różnica czasu między klatkami - dzięki temu otrzymujesz prędkość na sekundę
      //Zmieniasz pozycję obiektu o wektor: [0,0,1]*5 na sekundę = [0,0,5] na sekundę
        transform.Translate(Vector3.forward * 5 * Time.deltaTime);

      //odległość jednego punktu od drugiego
        range = Vector2.Distance(transform.position, Gracz.transform.position);

      //jeżeli ta odległość jest większa niż minDistance
        if (range > minDistance)
        {
         //wypisz w konsoli
            Debug.Log(range);
         
         //Ustaw pozycje obiektu na wartość funkcji Vector2.MoveTowards( ... )
         //parametry: Pozycja początkowa, pozycja docelowa, maksymalne przesunięcie
            transform.position = Vector2.MoveTowards(transform.position, Gracz.transform.position, speed * Time.deltaTime);
        }
    }

Ten obrazek chyba najlepiej wyjaśni ci czym jest atan2:
Image

Co do większości, tak da się to wyjaśnić tak by zrozumiała osoba która nie ma dużo do czynienia z matematyką, ale szczerze to nie chce mi się tego rozpisywać, mógłbym to rozrysować i pogadać jakbym miał czas. Ale na ten moment dam ci radę jak czytać taki kod bo to ci się bardziej przyda niż moje niezrozumiałe wyjaśnienia ;)

Plik kodu jak książka czytasz od góry do dołu zawsze w tej kolejności, ale jak w książkach mogą pojawić się odniesienia/referencje do innych miejsc w książce, tak samo jest w kodzie. Każde wywołanie funkcji (np. Destory( gameobject, 0) ) to nic innego jak coś w stylu takiego odnośnika - w tym miejscu zostanie wykonany kod który jest napisany w metodzie Destroy.

Staraj się wypatrzeć wyrażenia matematyczne. Jasne łatwo wypatrzeć takie "x+y" i jest to jasne, ale w sytuacji w której nie znasz niektórych pojęć matematycznych (a niektóre potrafią być bardzo skomplikowane) a zmienne to nie są zwykłe "x" czy "y" to zauważenie że
Mathf.Atan2(Gracz.transform.position.y - my.position.y, Gracz.transform.position.x - my.position.x);
to proste wyrażenie matematyczne może być trudne. tutaj ci je rozpiszę:
Kod: Zaznacz wszystko
//Uznajmy że
Gracz.transform.position.y to y1
Gracz.transform.position.x to x1
my.position.y to y2
my.position.x to x2
//to wyrażenie wygląda tak:
atan2( y1-y2 , x1-x2 )
//bardziej szczegolowo:
dy = y1 - y2
dx = x1 - x2
atan2(dy, dx)

Nagle wszystko jest prostsze

Tłumacz na język polski, Gdy widzisz takie Vector2.Distance( position1, position2 ) to gdy przetłumaczysz to na polski:
Odległość (pozycja1, pozycja2) to okazuje się ze nawet nie trzeba wyjaśniać co to robi, bo wszystko jest podane jak na tacy -odległość między pozycja1 a pozycja2.

Staraj się pisać jak najwięcej samemu bez pomocy gotowców, jakoż że możesz nie znać wszystkich funkcji na pamięć polecam ci mieć otwartą oficjalną dokumentację w której masz wszystko ładnie opisane co jak i dlaczego, nawet przykłady się znajdą. Ale lepiej żebyś zaczął od prostych rzeczy i na początku nie szukaj gotowych rozwiązań na siłę - nie wiesz jak sprawdzić odległość między punktem A a punktem B? nie szukaj gotowej funkcji tylko sam napisz wzór matematyczny, a jeżeli nie znasz to może lepiej już znajdź gotową funkcję albo jako wyzwanie sobie znajdź wzór i go zastosuj.

Mógłbym ci wyjaśnić te wszystkie zapisy matematyczne które tutaj wystąpiły ale to się mija z celem ponieważ programując jednak musisz mieć jakiś mały gram wiedzy, a co najważniejsze to umieć samemu wymyślić cuda. Oczywiście kto pyta ten nie błądzi więc mam nadzieję że przynajmniej częściowo coś wyjaśniłem czy pomogłem

Edit:
dodam że na humblebundle jest aktualnie (6dni od kiedy to piszę) promocja na kursy online, jeden z nich to kurs do game devu w Unity (za 1$)
patrykas1000
 
Posty: 334
Rejestracja: 16 Sie 2013, 21:35
Has thanked: 0 time
Been thanked: 0 time

Re: Prefaby ;-;

Postprzez Hostur » 14 Lut 2019, 11:08

Patrykas wystarczy zerknąć na implementację, bez resharpera można ją bez problemu znaleźć na necie

transform
Kod: Zaznacz wszystko
/// <summary>
///   <para>The Transform attached to this GameObject.</para>
/// </summary>
public extern Transform transform { [FreeFunction("GetTransform", HasExplicitThis = true, ThrowsException = true), MethodImpl(MethodImplOptions.InternalCall)] get; }


_transform.position:
Kod: Zaznacz wszystko
 [SpecialName]
 [MethodImpl(MethodImplOptions.InternalCall)]
 private extern void get_position_Injected(out Vector3 ret);

Wszystko rozbija się o MethodImplOptions.InternalCall
Hostur
 
Posty: 682
Rejestracja: 05 Sie 2015, 07:36
Has thanked: 0 time
Been thanked: 0 time


Wróć do Wsparcie Unity

Kto jest na forum

Użytkownicy przeglądający to forum: Brak zarejestrowanych użytkowników oraz 13 gości