Unity3D performans sorunu olup olmadığını nasıl anlarız?

706111

Hectopat
Katılım
28 Ağustos 2023
Mesajlar
6.020
Makaleler
1
Çözümler
29
Arkadaşlar merhaba.

Instiante ve Destroy metotları performansı düşürüyormuş. Ben de objectpool yaptım ama çok karmışık oldu. Destroy ve Instiante kullanmak daha cazip geliyordu. Peki bu haliyle performns sorunu olup olmadığını nasıl anlarım?

Oyunum ekranda çıkan balıkları vurma oyunu. Videosu burada;
Bu içeriği görüntülemek için üçüncü taraf çerezlerini yerleştirmek için izninize ihtiyacımız olacak.
Daha detaylı bilgi için, çerezler sayfamıza bakınız.


Kodlarım bunlar;
Fishmanager;
C#:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FishManager : MonoBehaviour
{

    public static FishManager INSTANCE;

    public List<GameObject> fishList = new List<GameObject>();
  

    [SerializeField] private GameObject fishes;


    // Balık havuzu
    public int initialFishSize = 0;
    public int maxPoolSize = 0;

    public Queue<GameObject> fishPool = new Queue<GameObject>();

    void initialPool()
    {

        initialFishSize = fishes.transform.childCount + 10;
        maxPoolSize = initialFishSize + 10;


        for (int i = 0; i < fishes.transform.childCount; i++)
        {
            int randomFish = Random.Range(0, fishes.transform.childCount);
            GameObject fish = Instantiate(fishes.transform.GetChild(i).gameObject);
            fish.GetComponent<FishMove>().init();
            fish.SetActive(false);
            fishPool.Enqueue(fish);
        }

        for (int i = 0; i < 10; i++)
        {
            int randomFish = Random.Range(0, fishes.transform.childCount);
            GameObject fish = Instantiate(fishes.transform.GetChild(randomFish).gameObject);
            fish.GetComponent<FishMove>().init();
            fish.SetActive(false);
            fishPool.Enqueue(fish);
        }


    }






    public GameObject getFish()
    {
        if (fishPool.Count > 0)
        {
            GameObject fish = fishPool.Dequeue();
            fish.SetActive(true);
            return fish;
        }
        else if (fishPool.Count < maxPoolSize)
        {
            int randomFish = Random.Range(0, fishes.transform.childCount);
            GameObject fish = Instantiate(fishes.transform.GetChild(randomFish).gameObject);
            fish.GetComponent<FishMove>().init();
            return fish;
        }
        return null;
    }

    public void returnFish(GameObject fish)
    {

        fishList.Remove(fish);

        if(fish.GetComponent<BaloonControl>() != null){
            BaloonControl sc = fish.GetComponent<BaloonControl>();
            sc.seperate = false;
        }

        fish.SetActive(false);
        fish.GetComponent<FishMove>().init();
        fishPool.Enqueue(fish);

        checkFish();

    }

    public void singliRetunrFish(GameObject fish)
    {
      
        if(fish.GetComponent<BaloonControl>() != null){
            BaloonControl sc = fish.GetComponent<BaloonControl>();
            sc.seperate = false;
        }
      
        fish.SetActive(false);
        fish.GetComponent<FishMove>().init();
        fishPool.Enqueue(fish);
    }

    public int fishCount = 4;

    // Start is called before the first frame update
    void Awake()
    {
        if (INSTANCE == null)
        {
            INSTANCE = this;

            initialPool();
            checkFish();


        }
        else
        {
            Destroy(gameObject);
        }
    }

  



    // Update is called once per frame
    public void checkFish()
    {
    

        while (fishList.Count < fishCount)
        {


            fishList.Add(getFish());
        }

        while (fishList.Count > fishCount)
        {

            fishList.RemoveAt(fishList.Count - 1);
            returnFish(fishList[fishList.Count - 1]);
        }


    }
}

FishMove
C#:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FishMove : MonoBehaviour
{


    [SerializeField] private GameObject fishes;

    [SerializeField] public FishScript fishScript;

    Vector2 finish;


    // Start is called before the first frame update
    void Start()
    {


    }



    // Update is called once per frame
    void Update()
    {

        fishMove();

    }


    void fishMove()
    {

        if (Vector2.Distance(transform.position, finish) > 0.1f)
        {

            transform.position = Vector2.MoveTowards(transform.position, finish, Time.deltaTime * fishScript.speed);
        }
        else
        {

            fishing();
        }

    }

    public void OnMouseDown()
    {
        if (!TimeManager.INSTACE.gameOver && !TimeManager.INSTACE.playUI && !TimeManager.INSTACE.pauseTime)
        {
            ScoreManager.Instace.addScore(fishScript.point);
            Color color = getFishColor();
            PointManager.INSTANCE.getPoint(fishScript.point, transform.position, color);
            fishing();
        }


    }

    public Color getFishColor()
    {

        Color color = new Color();
        switch (fishScript.fishName)
        {
            case "baloon":
                color = new Color32(255, 193, 70, 255);

                break;
          
            case "fosil":
                color = Color.white;

                break;



            case "red":
                color = new Color32(102, 0, 0, 255);
                break;

            case "green":
                color = new Color32(102, 153, 0, 255);
                break;
            case "blue":
                color = Color.blue;
                break;



            case "orange":
                color = new Color32(204, 102, 0, 255);
                break;

            case "pink":
                color = new Color32(204, 153, 255, 255);
                break;

            default:
                color = Color.white;
                break;
        }

        return color;

    }


    public void SingleOnMouseDown()
    {
        ScoreManager.Instace.addScore(fishScript.point);
    }





    public void init()
    {
        float y = Camera.main.orthographicSize;
        float x = Camera.main.aspect * y;

        if (transform.localScale.x < 0f)
        {
            Vector2 reverse = transform.localScale;
            reverse.x *= -1;
            transform.localScale = reverse;
        }

        if (Random.Range(0, 2) == 0)
        {

            Vector2 reverse = transform.localScale;
            reverse.x *= -1;
            transform.localScale = reverse;



            transform.position = new Vector2(x + 1f,
                                                    Random.Range(-y, y));


        }
        else
        {
            transform.position = new Vector2(-x - 1f,
                                                    Random.Range(-y, y));
        }


        if (transform.position.x > 0)
        {

            finish = new Vector2(-x - 1f, Random.Range(-y, y));
        }
        else
        {
            finish = new Vector2(x + 1f, Random.Range(-y, y));
        }


    }


    void fishing()
    {






        if (fishScript.fishName == "baloon" && Vector2.Distance(transform.position, finish) > 0.1f)
        {
            BaloonControl sc = gameObject.GetComponent<BaloonControl>();
            sc.seperate = true;

            this.wait(0.01f, () =>
            {
                returnFish();
            });



        }
        else
        {
            returnFish();
        }

















    }



    void returnFish()
    {

        FishManager.INSTANCE.returnFish(gameObject);
    }
}
Point Manager
C#:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

public class PointManager : MonoBehaviour
{
    // Start is called before the first frame update

    public static PointManager INSTANCE;

    [SerializeField] private GameObject point;
    [SerializeField] private GameObject bubble;

    // Point Pool
    private int poolSize = 0;
    private int maxPoolSize = 0;
    private Queue<GameObject> pointPool = new Queue<GameObject>();
    private Queue<GameObject> bubblePool = new Queue<GameObject>();

    // Point Buble

    private void intPool()
    {

        poolSize = 20;
        maxPoolSize = 30;

        for (int i = 0; i < poolSize; i++)
        {

            GameObject p = Instantiate(point);
            GameObject b = Instantiate(bubble);
            pointPool.Enqueue(p);
            bubblePool.Enqueue(b);

        }




    }




    private void Awake()
    {



        if (INSTANCE == null)
        {
            INSTANCE = this;
            intPool();



        }
        else
        {
            Destroy(gameObject);
        }
    }


    public GameObject getPoint(int score, Vector2 fishPos, Color color)
    {


        if (pointPool.Count > 0)
        {
            GameObject p = pointPool.Dequeue();
            p.GetComponent<TextMeshPro>().text = $"{score}";
            p.transform.position = fishPos;
            p.SetActive(true);

             GameObject b = getBubble(fishPos, color);



            this.wait(1f, () =>
            {
                returnPoint(p,b);
            });
            return p;
        }
        else if (pointPool.Count < maxPoolSize)
        {
            GameObject p = Instantiate(point);
            p.GetComponent<TextMeshPro>().text = $"{score}";
            p.transform.position = fishPos;
            p.SetActive(true);

            GameObject b = getBubble(fishPos, color);
        

            this.wait(1f, () =>
            {
                returnPoint(p,b);
            });
            return p;
        }
        return null;
    }


    public void returnPoint(GameObject p, GameObject b)
    {

        p.SetActive(false);
        pointPool.Enqueue(p);

        b.SetActive(false);
        b.GetComponent<ParticleSystem>().Stop();
        bubblePool.Enqueue(b);
    
    


    }


    public GameObject getBubble(Vector2 fishPos, Color color)
    {


        if (bubblePool.Count > 0)
        {
        

          
            GameObject b = bubblePool.Dequeue();
            b.transform.position = fishPos;
            var partColor = b.GetComponent<ParticleSystem>().main;
            partColor.startColor = color;
            b.SetActive(true);
            b.GetComponent<ParticleSystem>().Play();

        
            return b;
        }
        else if (pointPool.Count < maxPoolSize)
        {
            GameObject b = Instantiate(bubble);
            b.transform.position = fishPos;
            var partColor = b.GetComponent<ParticleSystem>().main;
            partColor.startColor = color;
            b.SetActive(true);
            b.GetComponent<ParticleSystem>().Play();

        
            return b;

        

          
        }
        return null;
    }


}

Arkadaşlar bu da 12.000 Frame'lik profile dosyası. ".data" uzantılı. Son 1000 frame'i boş verirsek iyi olur. Oyunda değildi o an.

@bitwise @Ömerrrrrr @TheAny @count
 
Instantiate ve Destroy performans sorunu yaratır evet. Bir objeyi Insantiate ile yarattığınızda bellekten bir yer ayırır (allocation). Sonra bu objeyi destroy etmek bellekte bu yeri bir süre boşa işgal eder ve çöp (garbage) oluşturur. Ardından C# garbage collector'un çalışıp bu bellek israfını temizlemesi gerekir. Bu 1-2 obje için önemli olmayacak kadar basit bir işlemdir. Ancak yüzlerce kez bu işlemi yapmak ciddi performans sorunlarına yol açar. Ürettiğiniz garbage miktarını görüntülemek için Window -> analysis -> Profiler'ı açın.
1721674781839.png

Orada GC (garbage collector) used memory ve GC allocated in frame miktarını gözlemleyin. Bu megabyte'lar seviyesinde çıkıyorsa performans sorunu yaratır.
 
Instantiate ve Destroy performans sorunu yaratır evet. Bir objeyi Insantiate ile yarattığınızda bellekten bir yer ayırır (allocation). Sonra bu objeyi destroy etmek bellekte bu yeri bir süre boşa işgal eder ve çöp (garbage) oluşturur. Ardından C# garbage collector'un çalışıp bu bellek israfını temizlemesi gerekir. Bu 1-2 obje için önemli olmayacak kadar basit bir işlemdir. Ancak yüzlerce kez bu işlemi yapmak ciddi performans sorunlarına yol açar. Ürettiğiniz garbage miktarını görüntülemek için Window -> analysis -> Profiler'ı açın. Eki Görüntüle 2260918
Orada GC (garbage collector) used memory ve GC allocated in frame miktarını gözlemleyin. Bu megabyte'lar seviyesinde çıkıyorsa performans sorunu yaratır.
Hocam top noktasında 97 KB.
1721675782323.png

Min noktasında 94 B.
1721675809507.png


GC Used memory ise sabit 0.65 GB.
 
İnanılmaz yüksek ya 650 MB. Hangi scriptler ne kadar garbage üretiyor görebiliyor musunuz?
Hocam birde bu var;
1721678151097.png


Hocam scripti nasıl bulabiliriz? Çünkü oyunu size attığım 3 koddan ibaret neredeyse. Birde şu kodum var, geciktirme yapmak için kullanıyorum;
Kod:
public static class Wait{

   public static void wait (this MonoBehaviour mono, float delay, UnityAction action) {

      mono.StartCoroutine (ExecuteAction (delay, action)) ;

   }

   private static IEnumerator ExecuteAction (float delay, UnityAction action) {

      yield return new WaitForSecondsRealtime (delay) ;

      action.Invoke () ;

      yield break ;

   }

}

Herhangi bir yerde corouinter ve yield metodu oluşturmadan kullanıyorum.
 
Geç cevap için üzgünüm. İşler yoğundu epey. Muhtemelen editörün kullandığı kaynakları da gösteriyor ya. Çok sorun yok. Garbage allocated in frame değeri bizim için önemli olan o halde. O da sorun çıkartmayacak seviyede duruyor. Yine de object poolu öğrenmenizde fayda var. Bu işin standardı bu çünkü
 
Geç cevap için üzgünüm. İşler yoğundu epey. Muhtemelen editörün kullandığı kaynakları da gösteriyor ya. Çok sorun yok. Garbage allocated in frame değeri bizim için önemli olan o halde. O da sorun çıkartmayacak seviyede duruyor. Yine de object poolu öğrenmenizde fayda var. Bu işin standardı bu çünkü
Hocam benim yazdığım object pool'a baktınız mı? Ne gibi yorumlarınız olur?
 

Technopat Haberler

Yeni konular

Geri
Yukarı