Strona 1 z 1

Prefaby ;-;

PostWysłany: 05 Lut 2019, 16:28
przez MrNolife12
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.

Re: Prefaby ;-;

PostWysłany: 05 Lut 2019, 17:22
przez Wichur
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ł

Re: Prefaby ;-;

PostWysłany: 06 Lut 2019, 11:41
przez Hostur
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.

Re: Prefaby ;-;

PostWysłany: 10 Lut 2019, 18:57
przez MrNolife12
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

Re: Prefaby ;-;

PostWysłany: 11 Lut 2019, 07:11
przez Hostur
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ć.

Re: Prefaby ;-;

PostWysłany: 13 Lut 2019, 14:28
przez patrykas1000
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)

Re: Prefaby ;-;

PostWysłany: 13 Lut 2019, 20:30
przez Marcin
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.

Re: Prefaby ;-;

PostWysłany: 13 Lut 2019, 21:18
przez MrNolife12
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);
      }
   }

 }

Re: Prefaby ;-;

PostWysłany: 14 Lut 2019, 02:07
przez patrykas1000
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$)

Re: Prefaby ;-;

PostWysłany: 14 Lut 2019, 11:08
przez Hostur
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