What I want:
I want the player to be able to click on instantiated objects and get points, then have those points show in the score-keeping text.
What I’ve done:
I’m currently using the following “FindGameObjectsWithTag” code to retrieve the buttons that are components of the instantiated prefab objects:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class CPointScore : MonoBehaviour
{
public TextMeshProUGUI CPointsText;
private float ScoreNum;
private GameObject[] CButtonGmeObjsHolder;
private void CTagFinder()
{
CButtonGmeObjsHolder = GameObject.FindGameObjectsWithTag("Ctag");
foreach (GameObject CButtonGmeObj in CButtonGmeObjsHolder)
{
Debug.Log("GmeObj Found");
Button CButton = CButtonGmeObj.GetComponent<Button>();
CButton.onClick.AddListener(AddScore);
}
}
public void AddScore()
{
ScoreNum += 1;
Debug.Log("Point Added # " + ScoreNum);
}
void Start()
{
InvokeRepeating("CTagFinder", 1f, 15.1f);
}
void Update()
{
CPointsText.text = ScoreNum.ToString();
}
}
Because FindGameObjectsWithTag only calls once I have the InvokeRepeating code in start. I have game objects spawning throughout the duration of the game so it needs to be constantly checking for tags.
Issue:
So the code finds the tags, the buttons are able to be clicked, and the score-keeping text updates which is great. The problem is that if I click one tagged button it will register a point for itself and every tagged button currently in the scene that spawned after it. For example, lets say I have 4 spawned objects currently on scene, when the first object spawned is clicked it will add 4 points instead of 1. If the second object spawned is clicked it will add 3 points instead of 1. I would like to have only the tagged button that is clicked register a point.
Question:
What can I change in my code so that only the tagged button that is clicked registers a point?
Method 1
Just have a score manager script in your scene. This can function as a manager class, which means it has a static reference to itself, but isn’t a singleton as each time you load your game scene it replaces itself.
Then when your instantiated objects are clicked on, you call a method on your ScoreManager and pass it points if you want a different point amount. Have that method add that point amount to totalScore and then update your text. You can handle the clicks in many ways, but since you’re doing buttons, that’s fine. Have a script on those objects that hook into the button on that object. No need to assign it through code!
Method 2
Have your code that instantiates the Gameobject assign your reference to the CPointScore to a script on the object. So your instantiated objects will have their own script with a method on them that when clicked will use the CPointScore variable in their script to call the AddScore method. Just instantiate as normal, then right after, use the reference that instantiate gave to assign your variable value.
Method 3
Use object pooling, which would allow you to have a pool of clickable objects in the scene and you can move them around, turn them on, and have their OnClicks already point to AddScore through the inspector, and turn them off when they are clicked on.
It’s perfectly fine to have a script on your instantiated objects that allows it to connect to your ScoreSystem.
I have decided to use the solution below instead of using findgameobjectswithtag as it works perfectly for what I am trying to do and is more efficient than finding tags:
Have a dedicated component YourComponent attached to the same GameObject as the buttons and have a global
public static event Action<YourComponent> OnCTSButtonSpawned;