Search Unity

Question Object Pooling and CoRoutine Enabling With Array Questions

Discussion in 'Scripting' started by swiFtsiLence, Apr 20, 2021.

  1. swiFtsiLence

    swiFtsiLence

    Joined:
    Jan 31, 2021
    Posts:
    1
    I haven't been coding for a long time, only started about three months ago. Please bear with my limited experience and knowledge. I also don't know if what I'm asking is too much.

    If I am asking for a lot, I again apologize. I've spent countless hours trying to figure these out and I feel like my brain is fried. I've even moved on to other parts of the game just so I would feel progress instead of a wall.

    I'm trying to put together a simple game that involves object pooling and then spawning a random object out of any of the pools to reuse. I followed this video for an easy solution to pool 2 objects, but I have some problems that I haven't been able to resolve with my own troubleshooting.

    My scripts are below my questions for reference. (GameManager, PoolManager, HumanMove)

    Here are my three problems/questions.

    1) In my GameManager script, I am able to reference 2 PoolObjectType(CasualMan, MetalWoman). How would I be able to reference more of them? I've tried looking into arrays [] but I can't seem to understand the syntax needed to apply it.

    When I try to add a third PoolObjectType as the code currently sits, just using another ":" symbol, an error shows up saying using the wrong syntax. If I change the ":" to a "," then I get "No overload for method "SpawnEnemy" takes 2 arguments.

    I'd like to be able to spawn up to X amount of random objects. I do know that I'd have to change the Random.Range to one digit higher. (ex. Random.Range(0, 7) if I wanted 6 units. I just don't know how to add those 6 units in the Coroutine.

    2) My knowledge of Coroutines is limited and I'm having trouble understanding how best to implement what I actually want. In the GameManger script, I have a Coroutine called "SpawnEnemy" that I call in Start(). The Coroutine currently spawns 1 unit, at random, every 6 seconds. Once the 6 seconds are up, it sets the object to false, waits what feels like 1 second, then spawns another random object.

    What I'd ultimately like to do is spawn a random unit, it runs for a certain distance, say -50Z, then it disables itself. But at the same time, I don't want to wait until that unit is disabled to spawn another one. I'd like it to be a fluid flow of spawning units.

    Here's an example, unit 1 spawns immediately, after X amount of seconds, unit 2 spawns.

    I do have another script running called "HumanMove" that disables the object once it hits (0, 0, -50f) or collides with the player. So it doesn't make sense to me to have it repeated in the GameManager script. I just don't know how to make the coroutine spawn fluidly (if that's even how to phrase it).

    3) My final question is in regards to a Coroutine in my "HumanMove" script called "Move".

    Here's how it works right now. When the object is enabled, it moves left and right towards the player. When the player collides with the object, I move the object to a transform off-screen. Perform the killing functions and then turn off the object. However, this was a workaround in a bug I couldn't resolve.

    Originally, I would set the transform of the object to be right under the player after colliding with it. But the problem was it wouldn't stop the Coroutine right away. It would finish the Coroutine and then stop. Resulting in 2 problems. 1, it would just move to the side and look awkward as the player ate it and then despawn. Or 2, it would constantly spawn to the left of the player and cause an infinite audio and death effect until the killing methods finished.

    What would be the best way to interrupt the Coroutine? Forcing the enemy to stay under the player making it feel like you actually ate them. Versus using magic and removing them off-screen and still allowing the killing methods to work freely.

    Thank you for your time, experience, and advice in advance!

    - swiFt

    Here is my GameManager script.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using UnityEngine.SceneManagement;
    6.  
    7.  
    8. public class GameManager : Singleton<GameManager>
    9. {
    10.     public static GameManager instance = null;
    11.  
    12.     [SerializeField]
    13.     private GameObject player;
    14.     [SerializeField]
    15.     private GameObject gameOverMenu;
    16.     [SerializeField] [Range(0.1f, 30f)]
    17.     private float spawnTimer = 1f;
    18.     [SerializeField]
    19.     private bool playerActive = false;
    20.     [SerializeField]
    21.     private bool gameOver = false;
    22.     [SerializeField]
    23.     private bool gameStarted =false;
    24.  
    25.     public bool GameOver
    26.     {
    27.         get { return gameOver; }
    28.     }
    29.  
    30.     public bool GameStarted
    31.     {
    32.         get { return gameStarted; }
    33.     }
    34.  
    35.     public GameObject Player
    36.     {
    37.         get { return player; }
    38.     }
    39.  
    40.     public void Start()
    41.     {
    42.         gameOverMenu.SetActive(false);
    43.         gameOver = false;
    44.         int random = Random.Range(0, 3);
    45.         StartCoroutine(SpawnEnemy(random == 0 ? PoolObjectType.CasualMan : PoolObjectType.MetalWoman));
    46.     }
    47.  
    48.     public void PlayerDeath()
    49.     {
    50.         gameOver = true;
    51.     //     mainMenu.SetActive(false);
    52.         gameOverMenu.SetActive(true);
    53.     }
    54.  
    55.     public void PlayerStartedGame()
    56.     {
    57.         playerActive = true;
    58.     }
    59.  
    60.     public void GameStart()
    61.     {
    62.         gameStarted = true;
    63.     }
    64.  
    65.     public void RestartGame()
    66.     {
    67.         SceneManager.LoadScene("Escape");
    68.         gameOverMenu.SetActive(false);
    69.     }
    70.  
    71.     public void Quit()
    72.     {
    73.         Application.Quit();
    74.     }
    75.  
    76.     private IEnumerator SpawnEnemy(PoolObjectType type)
    77.     {
    78.         if (!gameOver && gameStarted)
    79.         {
    80.             GameObject ob = PoolManager.Instance.GetPoolObject(type);
    81.  
    82.             ob.transform.position = new Vector3(Random.Range(-3f, 3f), 0, -150);
    83.             ob.gameObject.SetActive(true);
    84.  
    85.             yield return new WaitForSeconds(5f);
    86.             PoolManager.Instance.CoolObject(ob, type);
    87.         }
    88.        
    89.         int random = Random.Range(0, 3);
    90.         StartCoroutine(SpawnEnemy(random == 0 ? PoolObjectType.CasualMan : PoolObjectType.MetalWoman));
    91.     }
    92. }

    Here is my PoolManager script.


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5.  
    6. public enum PoolObjectType
    7. {
    8.     CasualMan,
    9.     ShirtMan,
    10.     MetalWoman
    11. }
    12.  
    13. [System.Serializable]
    14. public class PoolInfo
    15. {
    16.     public PoolObjectType type;
    17.     public int amount = 0;
    18.     public GameObject prefab;
    19.     public GameObject container;
    20.  
    21.     [HideInInspector]
    22.     public List<GameObject> pool = new List<GameObject>();
    23. }
    24.  
    25. public class PoolManager : Singleton<PoolManager>
    26. {
    27.  
    28.     [SerializeField] private List<PoolInfo> listOfPool;
    29.  
    30.     private void Start()
    31.     {
    32.         for (int i = 0; i < listOfPool.Count; i++)
    33.             FillPool(listOfPool[i]);
    34.     }
    35.  
    36.     private void FillPool(PoolInfo info)
    37.     {
    38.         for (int i = 0; i < info.amount; i++)
    39.         {
    40.             GameObject obInstance = null;
    41.             obInstance = Instantiate(info.prefab, info.container.transform);
    42.             obInstance.gameObject.SetActive(false);
    43.             info.pool.Add(obInstance);
    44.         }
    45.     }
    46.  
    47.     public GameObject GetPoolObject(PoolObjectType type)
    48.     {
    49.         PoolInfo selected = GetPoolByType(type);
    50.         List<GameObject> pool = selected.pool;
    51.  
    52.         GameObject obInstance = null;
    53.         if (pool.Count > 0)
    54.         {
    55.             obInstance = pool[pool.Count - 1];
    56.             pool.Remove(obInstance);
    57.         }
    58.         else
    59.         {
    60.             obInstance = Instantiate(selected.prefab, selected.container.transform);
    61.         }
    62.  
    63.         return obInstance;
    64.     }
    65.  
    66.     private PoolInfo GetPoolByType(PoolObjectType type)
    67.     {
    68.         for(int i = 0; i<listOfPool.Count; i++)
    69.         {
    70.             if (type == listOfPool[i].type)
    71.                 return listOfPool[i];
    72.         }
    73.  
    74.         return null;
    75.     }
    76.  
    77.     public void CoolObject(GameObject ob, PoolObjectType type)
    78.     {
    79.         ob.SetActive(false);
    80.  
    81.         PoolInfo selected = GetPoolByType(type);
    82.         List<GameObject> pool = selected.pool;
    83.  
    84.         if (!pool.Contains(ob))
    85.         {
    86.             pool.Add(ob);
    87.         }
    88.     }
    89. }

    Here's my HumanMove script

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class HumanMove : MonoBehaviour
    6. {
    7.     [SerializeField] private float environmentSpeed = 4f;
    8.     [SerializeField] private float resetPoint = -16.5f;
    9.  
    10.     [SerializeField] private AudioClip screamFX;
    11.     [SerializeField] private GameObject player;
    12.     [SerializeField] Vector3 leftPosition;
    13.     [SerializeField] Vector3 rightPosition;
    14.     [SerializeField] private float speed;
    15.  
    16.     public bool isDead;
    17.     private AudioSource humanAudio;
    18.    
    19.     void OnEnable()
    20.     {
    21.         ReturnToStart();
    22.     }
    23.  
    24.         private void ReturnToStart()
    25.     {
    26.         if (!GameManager.Instance.GameOver && GameManager.Instance.GameStarted)
    27.         {
    28.             transform.position = new Vector3(Random.Range(-3f, 3f),0,-150);
    29.             StartCoroutine (Move (rightPosition));
    30.         }
    31.     }
    32.  
    33.     void Start()
    34.     {
    35.         isDead = false;
    36.         humanAudio = GetComponent<AudioSource>();
    37.     }
    38.  
    39.     protected virtual void Update ()
    40.     {
    41.         if (!isDead && !GameManager.Instance.GameOver && GameManager.Instance.GameStarted)
    42.         {
    43.         transform.Translate(Vector3.back * (environmentSpeed * Time.deltaTime));
    44.         }
    45.         if (transform.localPosition.z <= resetPoint) {
    46.             gameObject.SetActive(false);
    47.         }
    48.     }
    49.  
    50.     IEnumerator Move (Vector3 target)
    51.     {
    52.             if (!isDead && !GameManager.Instance.GameOver && GameManager.Instance.GameStarted)
    53.             {
    54.             while (Mathf.Abs ((target - transform.localPosition).x) > 0.50f)
    55.             {
    56.            
    57.                 Vector3 direction = target.x == leftPosition.x ? Vector3.left : Vector3.right;
    58.                 transform.localPosition += direction * Time.deltaTime * speed;
    59.                                                 yield return null;
    60.             }
    61.            
    62.                         yield return new WaitForSeconds (0f);
    63.  
    64.             if (!isDead)
    65.             {
    66.                 Vector3 newTarget = target.x == leftPosition.x ? rightPosition : leftPosition;
    67.  
    68.             StartCoroutine (Move (newTarget));
    69.             }
    70.         }
    71.     }
    72.  
    73.         private void OnTriggerEnter(Collider other)
    74.     {
    75.         if (!GameManager.Instance.GameOver)
    76.         {
    77.             if (other.gameObject.tag == "Player")
    78.             {
    79.             isDead = true;
    80.             StopCoroutine (Move(rightPosition));
    81.             StopCoroutine (Move(leftPosition));
    82.             KillingPerson();
    83.             }
    84.         }
    85.     }
    86.  
    87.     private void KillingPerson()
    88.     {
    89.         transform.localPosition = new Vector3(0, 0, 10);
    90.         humanAudio.PlayOneShot(screamFX);
    91.         Invoke("KillPerson", 1.2f);
    92.     }
    93.  
    94.     private void KillPerson()
    95.     {
    96.         isDead = false;
    97.         gameObject.SetActive(false);
    98.     }
    99. }