private void ShowInstruction() { Debug.Log($"Calibrating pose {currentPose} for {lrName} hand"); handAnimator.SetInteger("handState", (int)currentPose); currentStep = Step.Wait; switch (currentPose) { case Pose.HandOpen: TutorialSteps.Instance.audioManager.ScheduleAudioClip(audioClips.handOpen, queue: false); TutorialSteps.PublishNotification($"Open your {lrName} hand", Mathf.Max(audioClips.handOpen.length, calibrationParams.waitTime + calibrationParams.dwellTime)); break; case Pose.HandClosed: TutorialSteps.Instance.audioManager.ScheduleAudioClip(audioClips.handClosed, queue: false); TutorialSteps.PublishNotification($"Make a fist with your {lrName} hand", Mathf.Max(audioClips.handClosed.length, calibrationParams.waitTime + calibrationParams.dwellTime)); break; //case Pose.FingersExt: // TutorialSteps.Instance.audioManager.ScheduleAudioClip(fingersExt, queue: false); // SendToast($"Extend your {right} fingers", waitTime + dwellTime); // break; //case Pose.FingersFlexed: // TutorialSteps.Instance.audioManager.ScheduleAudioClip(fingersFlexed, queue: false); // SendToast($"Flex your {right} fingers", waitTime + dwellTime); // break; case Pose.ThumbUp: TutorialSteps.Instance.audioManager.ScheduleAudioClip(audioClips.thumbUp, queue: false); TutorialSteps.PublishNotification("Give me a thumbs up", Mathf.Max(audioClips.thumbUp.length, calibrationParams.waitTime + calibrationParams.dwellTime)); break; case Pose.ThumbFlex: TutorialSteps.Instance.audioManager.ScheduleAudioClip(audioClips.thumbFlex, queue: false); TutorialSteps.PublishNotification($"Flex your {lrName} thumb", Mathf.Max(audioClips.thumbFlex.length, calibrationParams.waitTime + calibrationParams.dwellTime)); break; case Pose.AbdOut: TutorialSteps.Instance.audioManager.ScheduleAudioClip(audioClips.abdOut, queue: false); TutorialSteps.PublishNotification($"Move your {lrName} thumb out", Mathf.Max(audioClips.abdOut.length, calibrationParams.waitTime + calibrationParams.dwellTime)); break; //case Pose.NoThumbAbd: // TutorialSteps.Instance.audioManager.ScheduleAudioClip(noThumbAbd, queue: false); // SendToast($"Move your {right} thumb up", waitTime + dwellTime); // break; default: Debug.LogError($"Pose unknown: {currentPose}"); break; } }
// Update is called once per frame void Update() { // load old calibration if existing if (interpolator == null && hand.IsLinked && hand.GetInterpolationProfile(out interpolator)) { poses = LoadProfiles(); Debug.Log($"Connected {(isRight ? "right" : "left")} Hand"); } // only start the calibration, if the SenseGlove could be found and the calibrating flag was set if (calibrating && hand.IsLinked) { switch (currentStep) { // 1. show instruction case Step.ShowInstruction: { virtualHand.SetActive(true); ShowInstruction(); break; } // 2. wait case Step.Wait: { calibrationParams.waitTimer.LetTimePass(Time.deltaTime); poseStore.Clear(); break; } // 3. dwell case Step.Dwell: { if (TutorialSteps.Instance.audioManager.IsAudioPlaying()) { break; } calibrationParams.dwellTimer.LetTimePass(Time.deltaTime); completionWidget.active = true; completionWidget.text = "calibrating"; completionWidget.progress = calibrationParams.dwellTimer.GetFraction(); poseStore.AddPose(GetCurrentPoseValues()); float error = poseStore.ComputeError(); if (error > calibrationParams.maxError) { const string warningText = "Finger position changed too much, retry"; Debug.LogWarning(warningText); poseStore.Clear(); calibrationParams.dwellTimer.ResetTimer(); } break; } // 3.5 finish calibration & init testing phase case Step.TestInit: { hand.SaveHandCalibration(); virtualHand.SetActive(false); Debug.Log($"Saved Calibration Profiles for {lrName} hand"); TutorialSteps.Instance.audioManager.ScheduleAudioClip(audioClips.test, queue: false); TutorialSteps.PublishNotification($"{lrName} thumbs up to continue", audioClips.test.length + testParams.dwellTime); handAnimator.SetInteger("handState", (int)Pose.ThumbUp); currentStep = Step.Test; goto case Step.Test; } // 4. test calibration case Step.Test: { if (TutorialSteps.Instance.audioManager.IsAudioPlaying()) { break; } PoseBuffer buffer = new PoseBuffer(bufferSize: 2); buffer.AddPose(poseValues[(int)Pose.ThumbUp]); buffer.AddPose(GetCurrentPoseValues()); float error = buffer.ComputeError(); testParams.dwellTimer.LetTimePass(Time.deltaTime); if (error > testParams.maxError) { testParams.dwellTimer.ResetTimer(); } completionWidget.text = "hold"; completionWidget.active = true; completionWidget.progress = testParams.dwellTimer.GetFraction(); break; } // 5. calibration done case Step.Done: { calibrating = false; completionWidget.active = false; doneCallbacks.Call(currentStep); break; } default: break; } } }
void Start() { currentState = State.START; StateManager.Instance.onStateChangeTo[StateManager.States.HUD].Add((s) => StopCalibration(), once: true); // get objectives foreach (var comp in PlayerRig.Instance.gameObject.GetComponentsInChildren <XROffset>()) { if (comp.isRight) { rightHandObjective = comp.transform; rightHand = comp.orientation.target; } else { leftHandObjective = comp.transform; leftHand = comp.orientation.target; } } // get arms leftShoulder = GameObject.Find("shoulder_left_axis0").transform; rightShoulder = GameObject.Find("shoulder_right_axis0").transform; #region StateDefinitions stateMachine.onEnter[State.START] = (state) => { TutorialSteps.Instance.audioManager.ScheduleAudioClip(armLengthAudioClips.start, onEnd: () => Next()); }; stateMachine.onEnter[State.RIGHT_SHOULDER_TOUCH] = (state) => { TutorialSteps.Instance.audioManager.ScheduleAudioClip(armLengthAudioClips.touch_right, onStart: () => { TutorialSteps.PublishNotification("Touch your right shoulder with your left arm"); rightShoulderVolume.StartWaiting(); }, queue: true); rightShoulderVolume.OnDone((b) => { leftArmTouchpoint.position = new Vector3( leftHandObjective.position.x, leftShoulder.position.y, leftShoulder.position.z ); Next(); }, once: true); }; stateMachine.onExit[State.RIGHT_SHOULDER_TOUCH] = (state) => { rightShoulderVolume.StopWaiting(); }; stateMachine.onEnter[State.LEFT_SHOULDER_TOUCH] = (state) => { TutorialSteps.Instance.audioManager.ScheduleAudioClip(armLengthAudioClips.touch_left, onStart: () => { TutorialSteps.PublishNotification("Touch your left shoulder with your right arm"); leftShoulderVolume.StartWaiting(); }); leftShoulderVolume.OnDone((b) => { rightArmTouchpoint.position = new Vector3( rightHandObjective.position.x, rightShoulder.position.y, rightShoulder.position.z ); Next(); }, once: true); }; stateMachine.onExit[State.LEFT_SHOULDER_TOUCH] = (state) => { leftShoulderVolume.StopWaiting(); }; stateMachine.onEnter[State.LEFT_SCALE] = (state) => { TutorialSteps.Instance.audioManager.ScheduleAudioClip(armLengthAudioClips.scale_left); TutorialSteps.PublishNotification("Strech your left arm fully"); TutorialSteps.PublishNotification("Left thumbs up to calibrate"); UserInteractionManager.Instance.Confirm((b) => { FitLeft(); Next(); }, left: true, once: true); }; stateMachine.onEnter[State.RIGHT_SCALE] = (state) => { TutorialSteps.Instance.audioManager.ScheduleAudioClip(armLengthAudioClips.scale_right); TutorialSteps.PublishNotification("Strech your right arm fully"); TutorialSteps.PublishNotification("Right thumbs up to calibrate"); UserInteractionManager.Instance.Confirm((b) => { FitRight(); Next(); }, left: false, once: true); }; stateMachine.onEnter[State.DONE] = (state) => { Save(); onDoneCallbacks.Call(State.DONE); }; #endregion Load(); }