如何点击实例化对象获得积分,然后 link 积分对文本进行评分?

How to click on instantiated objects and get points, then link points to score text?

我想要的: 我希望玩家能够单击实例化对象并获得分数,然后将这些分数显示在记分文本中。

我做了什么: 我目前正在使用以下“FindGameObjectsWithTag”代码来检索作为实例化预制对象组件的按钮:

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

因为 FindGameObjectsWithTag 仅在我启动 InvokeRepeating 代码后才调用。我在整个游戏过程中都有游戏对象生成,因此需要不断检查标签。

问题: 所以代码找到了标签,按钮可以被点击,记分文本更新,这很棒。问题是,如果我单击一个带标签的按钮,它会为自己注册一个点,并为当前在它之后产生的场景中的每个带标签的按钮注册一个点。例如,假设我目前在场景中有 4 个生成的对象,当单击第一个生成的对象时,它将添加 4 个点而不是 1 个。如果单击第二个生成的对象,它将添加 3 个点而不是 1 个。我想只有被点击的标记按钮注册一个点。

问题: 我可以在我的代码中更改什么,以便只有被单击的标记按钮注册一个点?

谢谢

我认为这里有两点:

  • 您重复添加侦听器,因此当最终单击按钮时您将收到多个回调。
  • 重复FindGameObjectsWithTag也很低效

您的主要问题是重复来电。

对于 CTagFinder 的每次重复调用,您将遍历所有现有按钮并执行

CButton.onClick.AddListener(AddScore);

因此这些现有按钮最终会附加 多个 听众!

您要么想确保每个按钮只调用一次,例如跟踪那些你已经为之做过的人:

private readonly HashSet<Button> alreadyRegisteredButtons = new HashSet<Button>();

然后

if(!alreadyRegisteredbuttons.Contains(CButton))
{
     CButton.onClick.AddListener(AddScore);
     alreadyRegisteredButtons.Add(CButton);
}

或者确保在添加之前删除回调,例如

CButton.onClick.RemoveListener(AddScore);
CButton.onClick.AddListener(AddScore);

一般来说,我不会重复使用 FindGameObjectWithTag 轮询对象。而是让你的代码事件驱动。这已经完全避免了这个问题,因为无论如何都不会重复附加监听器。

我会简单地将一个专用组件 YourComponent 附加到与按钮相同的 GameObject 并有一个全局

public static event Action<YourComponent> OnCTSButtonSpawned;

并在此专用组件中执行

private void Start()
{
    OnCTSButtonSpawned?.Invoke(this);
}

并在你的 CPointScore 中收听此活动,例如

private void Awake()
{
    YourComponent.OnCTSButtonSpawned += AttachListener;
}

private void AttachListener(YourComponent component)
{
    if(compoenent.TryGetComponent<Button>(out var button))
    {
        button.onClick.AddListener(AddScore);
    }
}

private void AddScore()
{
    ScoreNum++;
    CPointsText.text = ScoreNum.ToString();
}