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

Unity安卓游戏NFC标签离开后检测状态未消失问题求助

问题分析与解决方案

你的核心问题在于:currentActivity.getIntent()获取的是Activity启动或首次被NFC触发时的初始Intent,标签移开后系统不会自动更新这个Intent,导致你一直读到缓存的旧数据。以下是针对性的解决思路和代码修改方案:

一、快速修复:清空已处理的Intent+超时判断

这种方式无需修改Android原生插件,仅通过Unity代码调整即可实现基础的标签离开检测:

修改后的Unity代码

private string _lastDetectedTag;
private float _lastTagDetectTime;
private const float TAG_LOST_TIMEOUT = 1.2f; // 1.2秒未检测到则判定标签离开

void Update()
{
    string currentTag = DetectNFCTag();
    
    if (currentTag != null)
    {
        // 检测到有效标签,更新状态并显示UI
        _lastDetectedTag = currentTag;
        _lastTagDetectTime = Time.time;
        ShowUIContent(currentTag); // 你的显示UI逻辑
    }
    else
    {
        // 超过超时时间且之前有检测到标签,判定标签已离开
        if (_lastDetectedTag != null && Time.time - _lastTagDetectTime > TAG_LOST_TIMEOUT)
        {
            _lastDetectedTag = null;
            HideUIContent(); // 你的隐藏UI逻辑
        }
    }
}

string DetectNFCTag()
{
    try
    {
        AndroidJavaObject currentActivity = new AndroidJavaClass("com.unity3d.player.UnityPlayer")
            .GetStatic<AndroidJavaObject>("currentActivity");
        AndroidJavaObject intent = currentActivity.Call<AndroidJavaObject>("getIntent");
        string action = intent.Call<string>("getAction");

        if (action == "android.nfc.action.NDEF_DISCOVERED")
        {
            AndroidJavaObject[] rawMsg = intent.Call<AndroidJavaObject[]>(
                "getParcelableArrayExtra", "android.nfc.extra.NDEF_MESSAGES");
            
            if (rawMsg != null && rawMsg.Length > 0)
            {
                AndroidJavaObject[] records = rawMsg[0].Call<AndroidJavaObject[]>("getRecords");
                byte[] payload = records[0].Call<byte[]>("getPayload");
                string result = System.Text.Encoding.Default.GetString(payload);
                result = result.Remove(0, 3); // 移除语言标识前缀
                
                // 关键:处理完Intent后清空Action,避免重复读取旧数据
                intent.Call<AndroidJavaObject>("setAction", "");
                return result;
            }
        }
    }
    catch (Exception ex)
    {
        Debug.LogError("NFC检测异常: " + ex.Message);
    }
    return null;
}

// 示例UI控制方法
void ShowUIContent(string tagData)
{
    // 显示按钮、文本等逻辑
}

void HideUIContent()
{
    // 隐藏所有NFC相关UI逻辑
}

二、最优方案:使用Android NFC前台调度系统

这是Android官方推荐的实时NFC交互方案,能主动监听标签的发现、离开事件,比轮询+超时的方式更高效准确。

实现步骤

  1. 初始化前台调度:在Unity启动时注册NFC监听
  2. 处理NFC事件:通过onNewIntent接收实时的标签数据(需通过Android原生层传递到Unity)
  3. 生命周期管理:在应用暂停/恢复时注册/注销调度

Unity端核心代码

private AndroidJavaObject _nfcAdapter;
private AndroidJavaObject _pendingIntent;
private AndroidJavaObject[] _intentFilters;
private AndroidJavaObject[][] _techLists;
private string _currentTag;

void Start()
{
    InitNfcForegroundDispatch();
}

void InitNfcForegroundDispatch()
{
    try
    {
        AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
        
        // 获取系统NFC适配器
        AndroidJavaClass nfcClass = new AndroidJavaClass("android.nfc.NfcAdapter");
        _nfcAdapter = nfcClass.CallStatic<AndroidJavaObject>("getDefaultAdapter", currentActivity);
        
        if (_nfcAdapter == null)
        {
            Debug.LogError("设备不支持NFC");
            return;
        }
        
        // 创建PendingIntent用于接收NFC事件
        AndroidJavaObject intent = new AndroidJavaObject("android.content.Intent", 
            currentActivity, currentActivity.GetType());
        intent.Call<AndroidJavaObject>("addFlags", 
            new AndroidJavaClass("android.content.Intent").GetStatic<int>("FLAG_ACTIVITY_SINGLE_TOP"));
        _pendingIntent = new AndroidJavaClass("android.app.PendingIntent")
            .CallStatic<AndroidJavaObject>("getActivity", currentActivity, 0, intent, 0);
        
        // 设置Intent过滤器,监听NDEF类型标签
        AndroidJavaObject ndefFilter = new AndroidJavaObject("android.content.IntentFilter", 
            "android.nfc.action.NDEF_DISCOVERED");
        ndefFilter.Call<AndroidJavaObject>("addDataType", "text/plain");
        _intentFilters = new AndroidJavaObject[] { ndefFilter };
        
        // 设置支持的NFC技术列表
        _techLists = new AndroidJavaObject[][] { 
            new AndroidJavaObject[] { new AndroidJavaObject("java.lang.String", "android.nfc.tech.Ndef") } 
        };
        
    }
    catch (AndroidJavaException e)
    {
        Debug.LogError("NFC前台调度初始化失败: " + e.Message);
    }
}

// 应用暂停/恢复时注册/注销调度
void OnApplicationPause(bool pauseStatus)
{
    if (_nfcAdapter == null) return;
    
    AndroidJavaObject currentActivity = new AndroidJavaClass("com.unity3d.player.UnityPlayer")
        .GetStatic<AndroidJavaObject>("currentActivity");
    
    if (!pauseStatus)
    {
        _nfcAdapter.Call("enableForegroundDispatch", currentActivity, _pendingIntent, _intentFilters, _techLists);
    }
    else
    {
        _nfcAdapter.Call("disableForegroundDispatch", currentActivity);
    }
}

// 接收Android原生层传递的NFC事件(需修改UnityPlayerActivity实现onNewIntent)
public void OnNfcTagDetected(string tagData)
{
    _currentTag = tagData;
    ShowUIContent(tagData);
}

public void OnNfcTagLost()
{
    _currentTag = null;
    HideUIContent();
}

Android原生层修改(可选,若需精准监听标签离开)

需自定义继承UnityPlayerActivity的类,重写onNewIntent和onPause方法,当标签离开时通过UnityPlayer.UnitySendMessage通知Unity:

public class CustomUnityActivity extends UnityPlayerActivity {
    private Tag mCurrentTag;
    
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        String action = intent.getAction();
        
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            mCurrentTag = tag;
            
            // 解析NDEF数据并传递给Unity
            String tagData = parseNdefData(intent);
            UnityPlayer.UnitySendMessage("NfcManager", "OnNfcTagDetected", tagData);
        }
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        // 应用暂停时判定标签离开
        if (mCurrentTag != null) {
            UnityPlayer.UnitySendMessage("NfcManager", "OnNfcTagLost", "");
            mCurrentTag = null;
        }
    }
    
    private String parseNdefData(Intent intent) {
        // 这里实现和Unity端一致的NDEF数据解析逻辑
        return "";
    }
}

三、Android清单注意事项

确保你的Activity配置保持singleTask启动模式,避免NFC触发时重启Activity:

<activity android:name="com.unity3d.player.UnityPlayerActivity"
          android:label="@string/app_name"
          android:launchMode="singleTask">
    <!-- 原有intent-filter配置不变 -->
</activity>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 17:27:30