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

Unity AR万用表引导异常:仅显示各模式第一步问题排查

Unity AR万用表电流测量引导模式功能修复

需求说明

  • 电流测量引导模式(TipMode.CurrentMeasurement)需实现完整流程:
    1. 初始状态:显示TipCurrentMeasurementRedAdapter文本,高亮红适配器与蓝色端口
    2. 红适配器连接蓝色端口后:显示TipCurrentMeasurementBlackAdapter文本,高亮黑适配器与黑色端口
    3. 黑适配器连接黑色端口后:显示TipCurrentMeasurementSelectionKnob文本,高亮选择旋钮
  • 错误处理:若红适配器错误连接到红色端口,需红色高亮该连接,并显示本地化表中ErrorRedAdapterConnection文本

问题分析

原代码存在两个核心问题导致引导流程无法正常推进:

  1. 电流测量引导的第一步仅判断红适配器是否连接,未验证是否连接到蓝色端口,错误连接到红色端口时也会进入下一步
  2. 错误处理方法HandleErrors()未在Update()中调用,导致错误状态无法实时更新

修复后完整代码

using System;
using System.Collections.Generic;
using UnityEngine;
using MixedReality.Toolkit.UX;
using UnityEngine.Localization;
using UnityEngine.Localization.Components;

namespace Assets.Multimeter.Scripts
{
public class MultimeterOrder : MonoBehaviour
{
    public Transform RedAdapterTransform;
    public Transform BlackAdapterTransform;
    public GameObject RedPort;
    public GameObject BlackPort;
    public GameObject BluePort;
    public GameObject SelectionKnob;
    public GameObject TextInstructionGameObject;
    public LocalizeStringEvent TextInstructionLocalizeStringEvent;

    private readonly List<Tuple<Renderer, Color>> rend = new();
    private enum TipMode { None, ResistanceMeasurement, CurrentMeasurement, VoltageMeasurement }
    private TipMode tipModeType = TipMode.None;
    private bool greenBlinkingStartable = true;
    private float greenBlinkingStartTimer;
    private const float greenBlinkingDuration = 1.5f;
    private const float greenBlinkingSpeed = 3f, redBlinkingSpeed = 1f, normalBlinkingSpeed = 2f;
    private DialogPool dialogPool;
    private IDialog dialog;
    private enum Error { NoError, ErrorRedAdapterConnection }
    private Error errorType = Error.NoError;
    private Shader shaderForBlinking, defaultShader;
    private bool colorIsRed;
    private bool colorIsGreen;
    private ProbeConnection probeConnection;

    private enum ResistanceMeasurementSteps { None, RedAdapter, BlackAdapter, SelectionKnob }
    private ResistanceMeasurementSteps resistanceMeasurementStep = ResistanceMeasurementSteps.None;

    private enum VoltageMeasurementSteps { None, RedAdapter, BlackAdapter, SelectionKnob }
    private VoltageMeasurementSteps voltageMeasurementStep = VoltageMeasurementSteps.None;

    private enum CurrentMeasurementSteps { None, RedAdapter, BlackAdapter, SelectionKnob }
    private CurrentMeasurementSteps currentMeasurementStep = CurrentMeasurementSteps.None;

    private void Start()
    {
        foreach (Renderer rendChild in this.GetComponentsInChildren<Renderer>())
        {
            this.rend.Add(new Tuple<Renderer, Color>(rendChild, rendChild.material.color));
        }

        this.dialogPool = this.GetComponent<DialogPool>();
        this.shaderForBlinking = Shader.Find("Standard");
        this.defaultShader = Shader.Find("glTF/PbrMetallicRoughness");

        probeConnection = FindObjectOfType<ProbeConnection>();
    }

    private void Update()
    {
        ResetDialogs();
        HandleErrors();

        switch (tipModeType)
        {
            case TipMode.ResistanceMeasurement:
                HandleResistanceMeasurement();
                break;
            case TipMode.VoltageMeasurement:
                HandleVoltageMeasurement();
                break;
            case TipMode.CurrentMeasurement:
                HandleCurrentMeasurement();
                break;
            default:
                HandleDefault();
                break;
        }
    }

    private void HandleResistanceMeasurement()
    {
        switch (resistanceMeasurementStep)
        {
            case ResistanceMeasurementSteps.RedAdapter:
                HighlightPortAndAdapter(RedPort, RedAdapterTransform);
                if (IsAdapterConnectedToPort(RedAdapterTransform, RedPort))
                {
                    ActivateTextInstruction("TipResistanceMeasurementBlackAdapter");
                    resistanceMeasurementStep = ResistanceMeasurementSteps.BlackAdapter;
                    errorType = Error.NoError;
                }
                else if (IsAdapterConnectedToPort(RedAdapterTransform, BluePort))
                {
                    HighlightError(BluePort);
                }
                break;

            case ResistanceMeasurementSteps.BlackAdapter:
                HighlightPortAndCheckConnection(BlackPort, BlackAdapterTransform, () =>
                {
                    ActivateTextInstruction("TipResistanceMeasurementSelectionKnob");
                    resistanceMeasurementStep = ResistanceMeasurementSteps.SelectionKnob;
                });
                break;

            case ResistanceMeasurementSteps.SelectionKnob:
                HighlightSelectionKnob();
                break;
        }
    }

    private void HandleVoltageMeasurement()
    {
        switch (voltageMeasurementStep)
        {
            case VoltageMeasurementSteps.RedAdapter:
                HighlightPortAndAdapter(RedPort, RedAdapterTransform);
                if (IsAdapterConnectedToPort(RedAdapterTransform, RedPort))
                {
                    ActivateTextInstruction("TipVoltageMeasurementBlackAdapter");
                    voltageMeasurementStep = VoltageMeasurementSteps.BlackAdapter;
                    errorType = Error.NoError;
                }
                else if (IsAdapterConnectedToPort(RedAdapterTransform, BluePort))
                {
                    HighlightError(BluePort);
                }
                break;

            case VoltageMeasurementSteps.BlackAdapter:
                HighlightPortAndCheckConnection(BlackPort, BlackAdapterTransform, () =>
                {
                    ActivateTextInstruction("TipVoltageMeasurementSelectionKnob");
                    voltageMeasurementStep = VoltageMeasurementSteps.SelectionKnob;
                });
                break;

            case VoltageMeasurementSteps.SelectionKnob:
                HighlightSelectionKnob();
                break;
        }
    }

    private void HandleCurrentMeasurement()
    {
        switch (currentMeasurementStep)
        {
            case CurrentMeasurementSteps.RedAdapter:
                HighlightPortAndAdapter(BluePort, RedAdapterTransform);
                if (IsAdapterConnectedToPort(RedAdapterTransform, BluePort))
                {
                    ActivateTextInstruction("TipCurrentMeasurementBlackAdapter");
                    currentMeasurementStep = CurrentMeasurementSteps.BlackAdapter;
                    errorType = Error.NoError;
                }
                else if (IsAdapterConnectedToPort(RedAdapterTransform, RedPort))
                {
                    HighlightError(RedPort);
                }
                break;

            case CurrentMeasurementSteps.BlackAdapter:
                HighlightPortAndCheckConnection(BlackPort, BlackAdapterTransform, () =>
                {
                    ActivateTextInstruction("TipCurrentMeasurementSelectionKnob");
                    currentMeasurementStep = CurrentMeasurementSteps.SelectionKnob;
                });
                break;

            case CurrentMeasurementSteps.SelectionKnob:
                HighlightSelectionKnob();
                break;
        }
    }


    private void HighlightPortAndCheckConnection(GameObject port, Transform adapterTransform, Action onConnected)
    {
        HighlightPortAndAdapter(port, adapterTransform);

        if (IsAdapterConnectedToPort(adapterTransform, port))
        {
            onConnected?.Invoke();
            errorType = Error.NoError;
        }
        else if (IsAdapterConnected(adapterTransform))
        {
            if (tipModeType == TipMode.CurrentMeasurement && adapterTransform == RedAdapterTransform && port == BluePort)
            {
                HighlightError(RedPort);
            }
            else
            {
                HighlightError(port);
            }
        }
    }

    private void HighlightPortAndAdapter(GameObject port, Transform adapterTransform)
    {
        if (tipModeType == TipMode.CurrentMeasurement && port == BluePort)
        {
            ColorBlinkBluePort(port.GetComponent<Renderer>(), adapterTransform.GetComponent<Renderer>());
        }
        else
        {
            ColorBlink(port.GetComponent<Renderer>());
            ColorBlink(adapterTransform.GetComponent<Renderer>());
        }
    }

    private void ColorBlinkBluePort(Renderer portRenderer, Renderer adapterRenderer)
    {
        if (portRenderer != null && adapterRenderer != null)
        {
            portRenderer.material.color = Color.Lerp(Color.blue, Color.white, Mathf.PingPong(Time.time * normalBlinkingSpeed, 1));
            adapterRenderer.material.color = Color.Lerp(Color.red, Color.white, Mathf.PingPong(Time.time * normalBlinkingSpeed, 1));
        }
    }

    private bool IsAdapterConnectedToPort(Transform adapterTransform, GameObject targetPort)
    {
        return probeConnection.IsAdapterConnectedToPort(adapterTransform, targetPort);
    }

    private bool IsAdapterConnected(Transform adapterTransform)
    {
        return probeConnection.IsAdapterConnected(adapterTransform);
    }

    private void HighlightSelectionKnob()
    {
        if (SelectionKnob.activeInHierarchy)
        {
            GreenColorBlink(tipModeType);
        }
    }

    private void ColorReset()
    {
        if (colorIsRed || colorIsGreen)
        {
            foreach (var rendComponent in rend)
            {
                rendComponent.Item1.material.color = rendComponent.Item2;
            }

            colorIsRed = false;
            colorIsGreen = false;
            errorType = Error.NoError;
            CloseDialog();
        }
    }

    private void ColorBlink(Renderer renderer)
    {
        if (renderer != null)
        {
            renderer.material.color = Color.Lerp(Color.red, Color.white, Mathf.PingPong(Time.time * normalBlinkingSpeed, 1));
        }
    }

    private void GreenColorBlink(TipMode mode)
    {
        TextInstructionGameObject.SetActive(false);

        if (greenBlinkingStartable)
        {
            greenBlinkingStartTimer = Time.time;
            greenBlinkingStartable = false;
        }

        if (Time.time - greenBlinkingStartTimer < greenBlinkingDuration)
        {
            foreach (var rendComponent in rend)
            {
                if (rendComponent.Item1.material.shader != defaultShader)
                {
                    rendComponent.Item1.material.shader = defaultShader;
                }

                rendComponent.Item1.material.color = Color.Lerp(rendComponent.Item2, Color.green, Mathf.PingPong(Time.time * greenBlinkingSpeed, 1));
                colorIsGreen = true;
            }
        }
        else if (colorIsGreen)
        {
            foreach (var rendComponent in rend)
            {
                rendComponent.Item1.material.color = rendComponent.Item2;
            }
            colorIsGreen = false;
            greenBlinkingStartable = true;
        }
    }

    private void HighlightError(GameObject port)
    {
        ActivateTextInstruction("ErrorRedAdapterConnection");
        ColorBlink(port.GetComponent<Renderer>());
        errorType = Error.ErrorRedAdapterConnection;
    }

    private void RedColorBlink()
    {
        TextInstructionGameObject.SetActive(false);

        foreach (var rendComponent in rend)
        {
            if (rendComponent.Item1.material.shader != defaultShader)
            {
                rendComponent.Item1.material.shader = defaultShader;
            }

            colorIsRed = true;
            rendComponent.Item1.material.color = Color.Lerp(rendComponent.Item2, Color.red, Mathf.PingPong(Time.time * redBlinkingSpeed, 1));
        }
    }

    private void OnDestroy()
    {
        CloseDialog();
    }

    private void ResetDialogs()
    {
        if (dialog != null)
        {
            dialog.Dismiss();
            dialog = null;
        }
    }

    private void ShowDialog(string dialogKey)
    {
        CloseDialog();
    }

    private void CloseDialog()
    {
        if (dialog != null)
        {
            dialog.Dismiss();
            dialog = null;
        }
    }

    public void SetResistanceMeasurementTips(bool pressed)
    {
        if (pressed)
        {
            Debug.Log("Resistance measurement button pressed.");
            tipModeType = TipMode.ResistanceMeasurement;
            ActivateTextInstruction("TipResistanceMeasurementRedAdapter");
            resistanceMeasurementStep = ResistanceMeasurementSteps.RedAdapter;
            errorType = Error.NoError;
        }
        else
        {
            Debug.Log("Resistance measurement button released.");
            tipModeType = TipMode.None;
            TextInstructionGameObject.SetActive(false);
            resistanceMeasurementStep = ResistanceMeasurementSteps.None;
            ColorReset();
        }
    }

    public void SetCurrentMeasurementTips(bool pressed)
    {
        if (pressed)
        {
            Debug.Log("Current measurement button pressed.");
            tipModeType = TipMode.CurrentMeasurement;
            ActivateTextInstruction("TipCurrentMeasurementRedAdapter");
            currentMeasurementStep = CurrentMeasurementSteps.RedAdapter;
            errorType = Error.NoError;
        }
        else
        {
            Debug.Log("Current measurement button released.");
            tipModeType = TipMode.None;
            TextInstructionGameObject.SetActive(false);
            currentMeasurementStep = CurrentMeasurementSteps.None;
            ColorReset();
        }
    }

    public void SetVoltageMeasurementTips(bool pressed)
    {
        if (pressed)
        {
            Debug.Log("Voltage measurement button pressed.");
            tipModeType = TipMode.VoltageMeasurement;
            ActivateTextInstruction("TipVoltageMeasurementRedAdapter");
            voltageMeasurementStep = VoltageMeasurementSteps.RedAdapter;
            errorType = Error.NoError;
        }
        else
        {
            Debug.Log("Voltage measurement button released.");
            tipModeType = TipMode.None;
            TextInstructionGameObject.SetActive(false);
            voltageMeasurementStep = VoltageMeasurementSteps.None;
            ColorReset();
        }
    }

    private void ActivateTextInstruction(string instructionKey)
    {
        TextInstructionGameObject.SetActive(true);
        TextInstructionLocalizeStringEvent.StringReference = new LocalizedString("MultimeterTable", instructionKey);
        TextInstructionLocalizeStringEvent.RefreshString();
    }

    private void HandleErrors()
    {
        switch (errorType)
        {
            case Error.ErrorRedAdapterConnection:
                RedColorBlink();
                break;
            case Error.NoError:
                if (colorIsRed)
                {
                    ColorReset();
                }
                break;
        }
    }

    private void HandleDefault()
    {
        ColorReset();
    }
   }
}

关键修改点

  1. 新增端口验证方法:IsAdapterConnectedToPort,用于确认适配器是否连接到指定端口(需确保ProbeConnection类实现对应逻辑)
  2. 修正电流引导逻辑:仅当红适配器连接到蓝色端口时才推进流程,错误连接到红色端口时触发错误提示
  3. 实时错误处理:在Update中调用HandleErrors,确保错误状态实时更新
  4. 状态重置优化:切换引导模式或完成步骤时自动重置错误状态,避免残留提示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 12:27:02