Search code examples
c#unity-game-enginecollision-detectionspawnobject-pooling

Score counter not increasing after recalling object from a pool with raycast detection hit


Context

Hello, its me again. I have been playing around with "Object Pool" and it is fascinating the amount of CPU performance can save up. However, I have encountered a new "feature" where it scores once for the object being called and going through the pipe.

In my object pool, I have a pipe GameObject where is being Instantiated and set its method Active(false) until it is spawned. Once spawned, my prefabs will be filled accordingly to the Object Pool's size.

Problem

Once it spawns, it does what it should do, both scoring and the same mechanic as "Flappy Bird". However, once the object gets Active again, it doesn't seem to score anymore because it was passed by a player. What I have done is to have a flag that checks if the player (bird) has passed the pipe or not. However, when I pass through it, it will update it as if it was 6 times (one point per frame). You may ask "have you done another flag for the pipe itself?" then the answer is yes. I tried that way also, but it will only keep the score once and not increase further than 5.

I ran out of ideas with the object pool approach. It seems to work fine if it is WITHOUT object pooling, but the flaw here is that it costs me CPU performance.

enter image description here

It either increases it by just 1, or it increases it by 6 times (because for each frame the object is in the ray, it counts another point).

Attempts

I have browsed on the Unity Learning center to find out how to do the object pooling, and it wasn't too bad as I thought. Later, I found this issue and I thought it was a problem with my logic (which it can be). I have spent several hours already (first mistake) to think it is something easy to fix, but apparently it wasn't due the time I have spent to just figure out why it is not working 😅. I have been fiddling around my RayCastDetection, SpawnManager, ObjectPooling, and PlayerControl logic that interacts accordingly to the way I want, but nada.

Code

ObjectPooling.cs

public static ObjectPooling sharedInstance;
public List<GameObject> pooledObjects;
public GameObject objectToPool;
private PlayerControl playerControllerScript;
public int amountToPool;

void Awake()
{
    sharedInstance = this;
}
void Start()
{
    playerControllerScript = GameObject.Find("Player").GetComponent<PlayerControl>();
    pooledObjects = new List<GameObject>();
    GameObject tmp;

    for (int i = 0; i < amountToPool; i++) //Add objects to the pool and turns them invisible (false)
    {
        tmp = Instantiate(objectToPool);
        tmp.SetActive(false);
        playerControllerScript.passedBeam = false;
        pooledObjects.Add(tmp);
    }
}
public GameObject GetPooledObject()
{
    for (int i = 0; i < amountToPool; i++)
    {
        if (!pooledObjects[i].activeInHierarchy)
            return pooledObjects[i];
    }
    return null;
}

RayCastDetection.cs

public class RayCastDetection : MonoBehaviour
{
    public UIManager UIManagerScript;
    public PlayerControl playerControlScript;
    private RaycastHit hit;
    public bool passed;

    void Start()
    {
        UIManagerScript = GameObject.Find("UI_Manager").GetComponent<UIManager>(); //Used for scoring
        playerControlScript = GameObject.Find("Player").GetComponent<PlayerControl>(); //used for player passing through the pipe
        passed = false;
        playerControlScript.passedBeam = false;
    }

    void Update()
{
    Vector3 beamDown = transform.TransformDirection(Vector3.down);
    Ray ray = new Ray(transform.position, beamDown);

    if (!passed)
    {
        if (Physics.Raycast(ray, out hit))
        {
            if (hit.collider.tag == "Player" && !playerControlScript.passedBeam)
            {
                playerControlScript.passedBeam = !playerControlScript.passedBeam;
                UIManagerScript.score++;
            }
            Debug.DrawRay(transform.position, hit.point - transform.position);
        }
    }
    else
        playerControlScript.passedBeam = false;
}
}

SpawnManager.cs

public class SpawnManager : MonoBehaviour
{
    public GameObject[] obstaclesPrefab;
    private PlayerControl playerControllerScript;
    private float startDelay = 1.69f;
    private float repeatRate = 1.1f;

    void Start()
    {
        playerControllerScript = GameObject.Find("Player").GetComponent<PlayerControl>();
        InvokeRepeating("SpawnObstacle", startDelay, repeatRate);
    }

    void Update()
    {
        
    }
    void SpawnObstacle()
    {
        // int obstaclesIndex = Random.Range(0, obstaclesPrefab.Length); //This is used only if I don't want to deal with object pooling, but the whole point is to use it. This is just a reference if I want to go back
        
        if (playerControllerScript.gameOver == false)
        {
            float randomY = Random.Range(-2f, 2f);
            Vector3 randomHeight = new Vector3(35, randomY, -7);
            GameObject pipe = ObjectPooling.sharedInstance.GetPooledObject();
            if (pipe != null)
            {
                pipe.transform.position = randomHeight;
                pipe.SetActive(true);
                //My guess is that I want to instantiate the object pipe's beam to false here 
            }
        }
            // Instantiate(obstaclesPrefab[obstaclesIndex], randomHeight, obstaclesPrefab[obstaclesIndex].transform.rotation); //This is used only if I don't want to deal with object pooling, but the whole point is to use it. This is just a reference if I want to go back
    }
}

Feel free to leave some suggestions in what I have missed out or any questions in regards to fill in. Thank you for your time!


Solution

  • I wanted to share some good news. My problems has been fixed!

    Before I step into the solution, big thanks to rotgers and sharing me the approach he used. I actually was stubborn and wanted to feel unique in some way when it was coming to a solution. I had to step back a little bit and look at the big picture.

    Reflection

    If you ever struggle and spent like 8-12hrs like me for some logic nonsense and you feel it in the tip of the tongue, it is time to step a little bit back when you are not going anywhere after 1 hour.

    Few tips to do (which I didn't do but I wish I do at the moment) was have drawings in how you are picturing it out mentally. Not drawing for the first time was my first mistake. I felt challenged by my own mind and sabotaging my own work to continuing being stubborn.

    Code Solution

    Now, the solution is that I added my UIManager script with my SpawnManager and RayCastDetection in order to make it work. Here is how I did it:

    UIManager.cs

    public class UIManager : MonoBehaviour
    {
        public Text scoreBoardDisplay;
        public Text scoreBoardDisplayShadowed;
        public AudioSource sound_score;
        public string scoreText;
        public int score;
        public int oldScore;
        public bool success;
        public bool incrementScore;
        
        void Start()
        {
            success = false; //NEW
            incrementScore = false; //NEW
        }
    
        void Update()
        {
            if (score != oldScore)
            {
                sound_score.Play();
                scoreText = score.ToString();
                scoreBoardDisplay.text = scoreText;
                scoreBoardDisplayShadowed.text = scoreText;
                oldScore = score;
            }
        }
    }
    

    SpawnManager.cs

    public class SpawnManager : MonoBehaviour
    {
        public GameObject[] obstaclesPrefab;
        private PlayerControl playerControllerScript;
        public UIManager UIManagerScript; //NEW
        private float startDelay = 1.69f;
        private float repeatRate = 1.1f;
        void Start()
        {
            playerControllerScript = GameObject.Find("Player").GetComponent<PlayerControl>();
            UIManagerScript = GameObject.Find("UI_Manager").GetComponent<UIManager>();
            InvokeRepeating("SpawnObstacle", startDelay, repeatRate);
        }
    
        void SpawnObstacle()
        {
            if (playerControllerScript.gameOver == false)
            {
                float randomY = Random.Range(-2f, 2f);
                Vector3 randomHeight = new Vector3(35, randomY, -7);
                GameObject pipe = ObjectPooling.sharedInstance.GetPooledObject();
                if (pipe != null)
                {
                    pipe.transform.position = randomHeight;
                    pipe.SetActive(true);
                    UIManagerScript.incrementScore = false; //NEW, where my problem happens to be solved
                    UIManagerScript.success = false; //NEW, where my problems happens to be solved
                }
            }
        }
    }
    

    RayCastDetection.cs

    public class RayCastDetection : MonoBehaviour
    {
        public UIManager UIManagerScript;
        public PlayerControl playerControlScript;
        private RaycastHit hit;
        public bool passed;
        void Start()
        {
            UIManagerScript = GameObject.Find("UI_Manager").GetComponent<UIManager>();
            playerControlScript = GameObject.Find("Tomato").GetComponent<PlayerControl>();
            passed = false;
        }
    
        void Update()
        {
            Vector3 beamDown = transform.TransformDirection(Vector3.down);
            Ray ray = new Ray(transform.position, beamDown);
    
            if (!passed)
            {
                if (Physics.Raycast(ray, out hit))
                {
                    if (hit.collider.tag == "Player" && !UIManagerScript.success) //Setting a condition to check if the beam hasn't been touched YET
                    {
                        //Once touched, turn this true and increment
                        UIManagerScript.incrementScore = true;
                        UIManagerScript.score++;
                    }
    //If I already incremented my score, then there is no need to re-increment it again from the same ray. Thus, a flag for success is handled
                    if (UIManagerScript.incrementScore)
                        UIManagerScript.success = true;
                    Debug.DrawRay(transform.position, hit.point - transform.position);
                }
            }
        }
    }
    

    Again, thank you very much for guiding me to the right track :). If I think in something to add, I will update it under my Reflection header.

    If you want to play around or use the code, feel free and play with it in my Git