0
\$\begingroup\$

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;
    }

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

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

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!

\$\endgroup\$
4
  • \$\begingroup\$ Maybe ray detection is not needed in this particular game type. Given that the obstacles are regularly arranged at a certain distance, you can calculate the score simply by the horizontal distance the player has walked, and the player's horizontal speed is constant, you can directly use the time elapsed in the game to increase the score. Just a suggestion : ) \$\endgroup\$
    – Mangata
    Nov 1, 2022 at 17:16
  • \$\begingroup\$ I could keep that suggestion. What I'm mostly worried about is scrapping the ray detection part, which I don't really want to do it 😅 \$\endgroup\$ Nov 1, 2022 at 17:36
  • \$\begingroup\$ where your despawn function() , you can reset object flag in despawn function , and add back to pool , when you spwan next time , it will like a new Object. \$\endgroup\$
    – TimChang
    Nov 2, 2022 at 7:46
  • \$\begingroup\$ Thank you for the comment TimChang. I'm actually making an updated version because I found the solution to my problem :D. Stay tuned! \$\endgroup\$ Nov 2, 2022 at 10:10

1 Answer 1

0
\$\begingroup\$

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.

\$\endgroup\$
4
  • \$\begingroup\$ When you solve your own problem and post an answer, remember to come back after the waiting period to click the checkmark icon in the top-left that marks the answer as Accepted. \$\endgroup\$
    – DMGregory
    Nov 2, 2022 at 12:22
  • \$\begingroup\$ It says I can't accept my own answers 😛 \$\endgroup\$ Nov 2, 2022 at 12:38
  • \$\begingroup\$ No, it says you have to wait a certain period of time before you can accept your own answer.. That's why I said remember to come back after that waiting period. You can't vote on your own answers, but the acceptance checkmark is below the voting buttons. \$\endgroup\$
    – DMGregory
    Nov 2, 2022 at 12:39
  • \$\begingroup\$ Ohhh got it got it. Thank you again DMGregory <3 \$\endgroup\$ Nov 2, 2022 at 13:25

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .