खोज…


ऑब्जेक्ट पूल

कभी-कभी जब आप एक खेल बनाते हैं तो आपको एक ही प्रकार की कई वस्तुओं को बार-बार बनाने और नष्ट करने की आवश्यकता होती है। आप बस इसे प्रीफ़ैब बनाकर कर सकते हैं और जब भी आपको ज़रूरत हो, इसे तुरंत / तबाह / नष्ट कर सकते हैं, हालाँकि, ऐसा करना अक्षम है और आपके खेल को धीमा कर सकता है।

इस मुद्दे के आसपास जाने का एक तरीका ऑब्जेक्ट पूलिंग है। मूल रूप से इसका मतलब यह है कि आपके पास एक पूल है (किसी राशि के लिए या सीमा के बिना) उन वस्तुओं का जिन्हें आप पुन: उपयोग करने जा रहे हैं जब आप अनावश्यक तत्काल या विनाश को रोक सकते हैं।

नीचे एक साधारण ऑब्जेक्ट पूल का एक उदाहरण है

public class ObjectPool : MonoBehaviour 
{
    public GameObject prefab;
    public int amount = 0;
    public bool populateOnStart = true;
    public bool growOverAmount = true;

    private List<GameObject> pool = new List<GameObject>();

    void Start() 
    {
        if (populateOnStart && prefab != null && amount > 0) 
        {
            for (int i = 0; i < amount; i++) 
            {
                var instance = Instantiate(Prefab);
                instance.SetActive(false);
                pool.Add(instance);
            }
        }
    }

    public GameObject Instantiate (Vector3 position, Quaternion rotation) 
    {
        foreach (var item in pool) 
        {
            if (!item.activeInHierarchy) 
            {
                item.transform.position = position;
                item.transform.rotation = rotation;
                item.SetActive( true );
                return item;
            }
        }

        if (growOverAmount) 
        {
            var instance = (GameObject)Instantiate(prefab, position, rotation);
            pool.Add(instance);
            return instance;
        }

        return null;
    }
}

पहले चर पर चलते हैं

public GameObject prefab;
public int amount = 0;
public bool populateOnStart = true;
public bool growOverAmount = true;

private List<GameObject> pool = new List<GameObject>();
  • GameObject prefab : यह वह GameObject prefab है जो ऑब्जेक्ट पूल नई वस्तुओं को पूल में GameObject prefab करने के लिए उपयोग करेगा।
  • int amount : यह अधिकतम राशि है जो पूल में हो सकती है। यदि आप किसी अन्य आइटम को इंस्टेंट करना चाहते हैं और पूल पहले से ही अपनी सीमा तक पहुँच गया है, तो पूल से किसी अन्य आइटम का उपयोग किया जाएगा।
  • bool populateOnStart : आप पूल को शुरू या नहीं करने के लिए चुन सकते हैं। ऐसा करने से प्रीफ़ैब के उदाहरणों के साथ पूल भर जाएगा ताकि आप पहली बार Instantiate कॉल करें तो आपको पहले से मौजूद ऑब्जेक्ट मिल जाएगा
  • bool growOverAmount : इसे सही पर सेट करने से पूल को बढ़ने की अनुमति मिलती है जब एक निश्चित समय सीमा में राशि से अधिक का अनुरोध किया जाता है। आप हमेशा अपने पूल में डालने के लिए मदों की मात्रा का सटीक अनुमान लगाने में सक्षम नहीं होते हैं, ताकि जरूरत पड़ने पर यह आपके पूल में और अधिक जोड़ देगा।
  • List<GameObject> pool : यह पूल है, वह स्थान जहाँ आपके सभी तात्कालिक / नष्ट किए गए ऑब्जेक्ट संग्रहीत होते हैं।

अब चलिए Start फंक्शन की जाँच करते हैं

void Start() 
{
    if (populateOnStart && prefab != null && amount > 0) 
    {
        for (int i = 0; i < amount; i++) 
        {
            var instance = Instantiate(Prefab);
            instance.SetActive(false);
            pool.Add(instance);
        }
    }
}

प्रारंभ फ़ंक्शन में हम जांचते हैं कि क्या हमें सूची को प्रारंभ पर पॉप्युलेट करना चाहिए और ऐसा करना चाहिए यदि prefab सेट किया गया है और राशि 0 से बड़ी है (अन्यथा हम अनिश्चित काल के लिए बनाएंगे)।

यह लूप को नई वस्तुओं को तत्काल लाने और पूल में डालने के लिए एक सरल है। एक बात ध्यान देने की है कि हम सभी उदाहरणों को निष्क्रिय कर देते हैं। इस तरह से वे अभी तक खेल में दिखाई नहीं दे रहे हैं।

इसके बाद, Instantiate फ़ंक्शन है, जो कि अधिकांश जादू होता है

public GameObject Instantiate (Vector3 position, Quaternion rotation) 
{
    foreach (var item in pool) 
    {
        if (!item.activeInHierarchy) 
        {
            item.transform.position = position;
            item.transform.rotation = rotation;
            item.SetActive(true);
            return item;
        }
    }

    if (growOverAmount) 
    {
        var instance = (GameObject)Instantiate(prefab, position, rotation);
        pool.Add(instance);
        return instance;
    }

    return null;
}

Instantiate फंक्शन, एकता के अपने Instantiate फंक्शन की तरह ही दिखता है, सिवाय इसके कि प्रीफैब को पहले ही एक क्लास मेंबर के रूप में ऊपर दिया जा चुका है।

Instantiate फ़ंक्शन का पहला चरण यह देखने के लिए जाँच कर रहा है कि क्या पूल में अभी कोई निष्क्रिय वस्तु है। इसका मतलब यह है कि हम उस वस्तु का पुन: उपयोग कर सकते हैं और इसे आवश्यक रूप से वापस दे सकते हैं। यदि पूल में एक निष्क्रिय वस्तु है, तो हम स्थिति और रोटेशन सेट करते हैं, इसे सक्रिय करने के लिए सेट करें (अन्यथा इसे गलती से पुन: उपयोग किया जा सकता है यदि आप इसे सक्रिय करना भूल जाते हैं) और इसे आवश्यककर्ता को लौटा दें।

दूसरा चरण केवल तब होता है जब पूल में कोई निष्क्रिय वस्तु नहीं होती है और पूल को प्रारंभिक राशि से अधिक बढ़ने की अनुमति होती है। क्या होता है सरल: प्रीफैब का एक और उदाहरण बनाया और पूल में जोड़ा जाता है। पूल के विकास की अनुमति से आपको पूल में वस्तुओं की सही मात्रा होने में मदद मिलती है।

तीसरा "चरण" केवल तब होता है जब पूल में कोई निष्क्रिय आइटम नहीं होते हैं और पूल को बढ़ने की अनुमति नहीं होती है। जब ऐसा होता है, तो अनुरोधकर्ता को एक शून्य GameObject प्राप्त होगा जिसका अर्थ है कि कुछ भी उपलब्ध नहीं था और NullReferenceExceptions को रोकने के लिए ठीक से नियंत्रित किया जाना चाहिए।

महत्वपूर्ण!

यह सुनिश्चित करने के लिए कि आपके आइटम पूल में वापस आ जाएं, आपको गेम ऑब्जेक्ट को नष्ट नहीं करना चाहिए। केवल एक चीज जो आपको करने की आवश्यकता है, उन्हें निष्क्रिय करने के लिए सेट करें और वह उन्हें पूल के माध्यम से पुन: उपयोग के लिए उपलब्ध कराएगा।

साधारण वस्तु पूल

नीचे एक ऑब्जेक्ट पूल का एक उदाहरण है जो किसी दिए गए ऑब्जेक्ट प्रकार को किराए पर देने और वापस करने की अनुमति देता है। ऑब्जेक्ट पूल बनाने के लिए फ़ंक्शन के लिए एक फंक और उपयोगकर्ता को लचीलापन देने के लिए ऑब्जेक्ट को नष्ट करने के लिए एक एक्शन की आवश्यकता होती है। जब पूल खाली होता है तो एक वस्तु का अनुरोध करने पर एक नई वस्तु बनाई जाएगी और यह अनुरोध करने पर कि पूल में ऑब्जेक्ट हैं तो ऑब्जेक्ट्स को पूल से हटा दिया जाता है और वापस कर दिया जाता है।

ऑब्जेक्ट पूल

public class ResourcePool<T> where T : class
{
    private readonly List<T> objectPool = new List<T>();
    private readonly Action<T> cleanUpAction;
    private readonly Func<T> createAction;

    public ResourcePool(Action<T> cleanUpAction, Func<T> createAction)
    {
        this.cleanUpAction = cleanUpAction;
        this.createAction = createAction;
    }

    public void Return(T resource)
    {
        this.objectPool.Add(resource);
    }

    private void PurgeSingleResource()
    {
        var resource = this.Rent();
        this.cleanUpAction(resource);
    }

    public void TrimResourcesBy(int count)
    {
        count = Math.Min(count, this.objectPool.Count);
        for (int i = 0; i < count; i++)
        {
            this.PurgeSingleResource();
        }
    }

    public T Rent()
    {
        int count = this.objectPool.Count;
        if (count == 0)
        {
            Debug.Log("Creating new object.");
            return this.createAction();
        }
        else
        {
            Debug.Log("Retrieving existing object.");
            T resource = this.objectPool[count-1];
            this.objectPool.RemoveAt(count-1);
            return resource;
        }
    }
}

नमूना उपयोग

public class Test : MonoBehaviour
{
    private ResourcePool<GameObject> objectPool;

    [SerializeField]
    private GameObject enemyPrefab;

    void Start()
    {
        this.objectPool = new ResourcePool<GameObject>(Destroy,() => Instantiate(this.enemyPrefab) );
    }

    void Update()
    {
        // To get existing object or create new from pool
        var newEnemy = this.objectPool.Rent();
        // To return object to pool
        this.objectPool.Return(newEnemy);
        // In this example the message 'Creating new object' should only be seen on the frame call
        // after that the same object in the pool will be returned.
    }
}

एक और साधारण वस्तु पूल

एक और उदाहरण: एक हथियार जो गोलियों से बाहर निकलता है।

हथियार इसे बनाने वाली गोलियों के लिए एक वस्तु पूल के रूप में कार्य करता है।

public class Weapon : MonoBehaviour {
    
    // The Bullet prefab that the Weapon will create
    public Bullet bulletPrefab;

    // This List is our object pool, which starts out empty
    private List<Bullet> availableBullets = new List<Bullet>();

    // The Transform that will act as the Bullet starting position
    public Transform bulletInstantiationPoint;

    // To spawn a new Bullet, this method either grabs an available Bullet from the pool,
    // otherwise Instantiates a new Bullet
    public Bullet CreateBullet () {
        Bullet newBullet = null;

        // If a Bullet is available in the pool, take the first one and make it active
        if (availableBullets.Count > 0) {
            newBullet = availableBullets[availableBullets.Count - 1];

            // Remove the Bullet from the pool
            availableBullets.RemoveAt(availableBullets.Count - 1);

            // Set the Bullet's position and make its GameObject active
            newBullet.transform.position = bulletInstantiationPoint.position;
            newBullet.gameObject.SetActive(true);
        }
        // If no Bullets are available in the pool, Instantiate a new Bullet
        else {
            newBullet newObject = Instantiate(bulletPrefab, bulletInstantiationPoint.position, Quaternion.identity);

            // Set the Bullet's Weapon so we know which pool to return to later on
            newBullet.weapon = this;
        }
        
        return newBullet;
    }

}

public class Bullet : MonoBehaviour {
    
    public Weapon weapon;

    // When Bullet collides with something, rather than Destroying it, we return it to the pool
    public void ReturnToPool () {
        // Add Bullet to the pool
        weapon.availableBullets.Add(this);

        // Disable the Bullet's GameObject so it's hidden from view
        gameObject.SetActive(false);
    }

}


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow