You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中访问closeImage按钮时出现空引用问题求助

问题分析与修复方案

核心问题

  1. 错误实例化单例类:Marker控制脚本中用new TappedMarkerManager()创建实例完全错误。TappedMarkerManager继承自MonoBehaviour,不能通过new关键字实例化,这种方式创建的实例不会触发Start方法,导致closeImage未初始化,这就是首次点击报错“Close image is null”的根源,同时会产生多个混乱的实例。
  2. 关闭逻辑有冗余与空引用风险:点击关闭按钮时直接访问closeImage.gameObject但未做空值判断,且遍历所有弹窗图片关闭,逻辑冗余且容易出错。
  3. 弹窗显示逻辑未做互斥处理:每次点击Marker时未先隐藏已显示的弹窗,可能导致多个弹窗同时出现。

修复步骤

1. 修正Marker控制脚本的实例获取方式

删除new TappedMarkerManager()的错误代码,改用单例获取实例:

// 替换原来的实例化代码
TappedMarkerManager tappedMarkerManager;

void Start()
{
    tappedMarkerManager = TappedMarkerManager.Instance;
}

2. 优化TappedMarkerManager的逻辑与空值判断

修改后的完整代码:

using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class TappedMarkerManager : MonoBehaviour
{
    private static TappedMarkerManager instance;
    private List<GameObject> markerImageList = new List<GameObject>();

    string tappedMarkerName;
    Button closeImage;
    GameObject closeImageGO;

    public static TappedMarkerManager Instance
    {
        get
        {
            if (instance == null)
            {
                instance = FindObjectOfType<TappedMarkerManager>();
            }
            return instance;
        }
    }

    void Start()
    {
        closeImageGO = GameObject.FindGameObjectWithTag("close_img");
        if (closeImageGO != null)
        {
            closeImage = closeImageGO.GetComponent<Button>();
            closeImage.onClick.AddListener(TappedMarkerInformation);
            // 初始隐藏关闭按钮
            closeImageGO.SetActive(false);
        }
        else
        {
            Debug.Log("closeImageGO is null");
        }

        var markerImages = GameObject.FindGameObjectsWithTag("marker_img");
        foreach (var markerImage in markerImages)
        {
            markerImageList.Add(markerImage.gameObject);
            markerImage.SetActive(false);
        }
    }

    public void TappedMarkerInformation()
    {
        string clickedBtnName = GetButtonName();
        
        // 优先处理关闭按钮逻辑
        if (clickedBtnName == "ExitBtn")
        {
            // 隐藏所有弹窗图片
            markerImageList.ForEach(img => img.SetActive(false));
            // 安全隐藏关闭按钮
            closeImageGO?.SetActive(false);
            return;
        }

        // 处理Marker点击:先隐藏所有弹窗,再显示目标弹窗
        markerImageList.ForEach(img => img.SetActive(false));
        
        var targetImg = markerImageList.FirstOrDefault(img => img.name == tappedMarkerName);
        if (targetImg != null)
        {
            targetImg.SetActive(true);
            closeImageGO?.SetActive(true);
        }
    }

    public void SetTappedName(string name)
    {
        tappedMarkerName = name;
        TappedMarkerInformation();
    }

    public string GetButtonName()
    {
        return EventSystem.current.currentSelectedGameObject?.name ?? "";
    }
}

3. 额外检查项

  • 确保close_img标签唯一,场景中只有一个关闭按钮使用该标签。
  • 确认关闭按钮的名称是ExitBtn,与代码中的判断一致。
  • 确保TappedMarkerManager脚本仅挂载在场景中一个GameObject上,避免多实例冲突。

修复说明

  • 用单例正确获取实例,彻底解决未初始化导致的空引用问题。
  • 重构逻辑,分离关闭与显示流程,先隐藏所有弹窗再显示目标,避免多弹窗共存。
  • 使用?.空传播运算符处理关闭按钮的状态切换,杜绝空引用报错。
  • 初始时默认隐藏关闭按钮,符合场景初始化需求。

内容的提问来源于stack exchange,提问作者Jeremie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 16:46:13