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交互方案,能主动监听标签的发现、离开事件,比轮询+超时的方式更高效准确。
实现步骤
- 初始化前台调度:在Unity启动时注册NFC监听
- 处理NFC事件:通过
onNewIntent接收实时的标签数据(需通过Android原生层传递到Unity) - 生命周期管理:在应用暂停/恢复时注册/注销调度
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
相关产品推荐
相关产品推荐

