'전체 글'에 해당되는 글 1801건

  1. 2024.04.16 tutorial box
  2. 2024.02.26 유니티 쉐이더 스타트업 굴절
  3. 2024.02.24 오큘러스 퀘스트 3 컨트롤러 모델
  4. 2024.01.26 Gaze Interaction
  5. 2024.01.19 Oculus Locomotion Environment Package
  6. 2024.01.18 XR Origin (Action-based) 오큘러스 입력 받기

tutorial box

카테고리 없음 2024. 4. 16. 16:11
반응형

 

반응형
:

유니티 쉐이더 스타트업 굴절

카테고리 없음 2024. 2. 26. 09:36
반응형

https://chulin28ho.tistory.com/364

굴절 셰이더 만들기 / Unity Refraction Shader

강의 중 컴퓨터가 이상해서 제대로 강의를 못한 부분 올립니다. 우선 플렌을 하나 만들께요. 플렌 뒤에는... 이것저것 요것이 숨어 있습니다. 지금 안보여서 글치.. 카메라도 저렇게 설치해야 한

chulin28ho.tistory.com

https://cafe.naver.com/unitygame22/184?tc=shared_link

굴절 (refraction)

대한민국 모임의 시작, 네이버 카페

cafe.naver.com

반응형
:

오큘러스 퀘스트 3 컨트롤러 모델

VR 2024. 2. 24. 14:22
반응형

OculusQuestControllerAndHand.unitypackage
15.56MB

 

반응형

'VR' 카테고리의 다른 글

Use Custom Hand Model  (0) 2023.12.22
blender 2.8 fbx import error  (0) 2023.12.22
HandGun 장전  (0) 2023.12.20
Unity VR Create Snap Interactions  (0) 2023.12.13
:

Gaze Interaction

VR/XR Interaction Toolkit 2024. 1. 26. 23:16
반응형

스탭 1

새 씬을 만들고 메인 카메라를 지운후 XR Origin을 생성 합니다. 

 

Left, Right Controller를 선택하고 XR Controller를 제외한 나머지 컴포넌트들을 지워줍니다.

 

XR Controller의 Model Prefab를 None으로 설정 합니다.

 

XR Origin을 선택하고 Character Controller, Character Controller Driver컴포넌트를 부착 합니다.

 

 

 

Cube를 생성하고 이름을 Ground로 변경 합니다.

Scale을 (50, 0.01, 50)으로 설정 합니다.

 

 

메터리얼을 넣어 줍니다.

 

 

XR Origin을 선택후 자식으로 빈 오브젝트 (Locomotion System)을 생성 하고 Locomotion System을 부착 합니다.

 

Locomotion System오브젝트를 선택후 빈오브젝트 Move를 생성 하고 Continous Move Provider(Action-based) 컴포넌트를 부착 합니다.

 

실행후 왼손 컨트롤러의 Thumb Stick을 사용해 이동해 봅니다.

 

컨트롤러 모델이 없으니 허전하군요, 부착 해줄까요 

 

 

Locomotion System을 선택하고 빈오브젝트 (Turn)을 생성 합니다.

 

이어서 Snap Turn Provider (Action - based) 컴포넌트를 부착 합니다.

 

실행후 왼손 컨트롤러로 이동을 오른손 컨트롤러로 스냅 턴이 되는지 확인 합니다.

 

 

프로젝트 창에서 Gaze Interactor 프리팹을 찾아 XR Origin에 넣어줍니다.

 

Interaction Layer Mask를 다음과 같이 선택 합니다. Gaze 없을 경우 추가 합니다.

 

Hover Time To Select를 3으로 

Time To Auto Deselect를 2로 설정 합니다.

 

 

XR Controller 컴포넌트의 Is Tracked Action의 Use Reference를 체크 해제 합니다.

 

 

XR Interactor Reticle Visual컴포넌트를 추가 합니다.

 

Reticle Prefab에 Gaze Reticle프리팹을 넣어줍니다.

Gaze Reticle.unitypackage
0.04MB

 

 

 

 

그리고 컴포넌트를 비활성화 해줍니다.

 

Draw On NO Hit를 체크 합니다.

 


 

스탭 2

 

빈오브젝트 Gaze를 생성 합니다.

 

Gaze오브젝트를 선택후 Gaze Activation Zone을 생성 합니다.

 

Box Collider컴포넌트를 추가후 Size를 조절하고 Is Trigger를 체크 합니다.

 

Toggle Component Zone컴포넌트를 추가 합니다.

 

Toggle Component Zone스크립트는 다음과 같습니다.

namespace UnityEngine.XR.Content.Interaction
{
    /// <summary>
    /// This component is designed to easily toggle a specific component on or off when an object
    /// enters the specified <see cref="triggerVolume"/>.
    /// </summary>
    [RequireComponent(typeof(Collider))]
    public class ToggleComponentZone : MonoBehaviour
    {
        [SerializeField]
        [Tooltip("Main Trigger Volume to detect the Activation Object within. Must be on same physics layer as the Activation Object.")]
        Collider m_TriggerVolume;

        /// <summary>
        /// Main Trigger Volume to detect the Activation Object within.
        /// Must be on same physics layer as the Activation Object.
        /// </summary>
        public Collider triggerVolume
        {
            get => m_TriggerVolume;
            set => m_TriggerVolume = value;
        }

        [SerializeField]
        [Tooltip("Collider that will trigger the component to turn on or off when entering the Trigger Volume. Must have a Rigidbody component and be on the same physics layer as the Trigger Volume.")]
        Collider m_ActivationObject;

        /// <summary>
        /// Collider that will trigger the component to turn on or off when entering the Trigger Volume.
        /// Must have a Rigidbody component and be on the same physics layer as the Trigger Volume.
        /// </summary>
        public Collider activationObject
        {
            get => m_ActivationObject;
            set => m_ActivationObject = value;
        }

        [SerializeField]
        [Tooltip("Component to set the enabled state for. Will set the value to the Enable On Entry value upon entry and revert to original value on exit.")]
        Behaviour m_ComponentToToggle;

        /// <summary>
        /// Component to set the enabled state for. Will set the value to the
        /// Enable On Entry value upon entry and revert to original value on exit.
        /// </summary>
        public Behaviour componentToToggle
        {
            get => m_ComponentToToggle;
            set => m_ComponentToToggle = value;
        }

        [SerializeField]
        [Tooltip("Sets whether to enable or disable the Component To Toggle upon entry into the Trigger Volume.")]
        bool m_EnableOnEntry = true;

        /// <summary>
        /// Sets whether to enable or disable the Component To Toggle upon entry into the Trigger Volume.
        /// </summary>
        public bool enableOnEntry
        {
            get => m_EnableOnEntry;
            set => m_EnableOnEntry = value;
        }

        bool m_InitialStateOnEntry;

        void Start()
        {
            if (m_TriggerVolume == null && !TryGetComponent(out m_TriggerVolume))
            {
                enabled = false;
                return;
            }

            if (!m_TriggerVolume.isTrigger)
                m_TriggerVolume.isTrigger = true;
        }

        void OnTriggerEnter(Collider other)
        {
            if (other != null && other == m_ActivationObject)
            {
                m_InitialStateOnEntry = m_ComponentToToggle.enabled;
                m_ComponentToToggle.enabled = m_EnableOnEntry;
            }
        }

        void OnTriggerExit(Collider other)
        {
            if (other != null && other == m_ActivationObject)
            {
                m_ComponentToToggle.enabled = m_InitialStateOnEntry;
            }
        }
    }
}

 

 

Gaze오브젝트를 선택하고 약간 앞으로 이동시켜 줍니다.

 

 

 

Trigger Volume에 GazeActivationZone을 넣어주고 

Activation Object에 XR Origin을 넣어줍니다.

 

 

Component To Toggle에는 Gaze Interactor를 넣어주면 되는데 이때 Gaze Interactor에 붙어 있는 XR Interactor Reticle Visual이 들어가야 합니다.

 

 

일반적으로는 들어가지 않기에 Editor스크립트를 만들었습니다.

다음과 같이 ToggleComponentZoneEditor와 ToggleComponentZoneWindow스크립트가 필요 합니다.

using UnityEngine;
using UnityEditor;
using UnityEngine.XR.Content.Interaction;

[CustomEditor(typeof(ToggleComponentZone))]
public class ToggleComponentZoneEditor : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector(); // Draw the default inspector

        ToggleComponentZone script = (ToggleComponentZone)target;

        if (script.componentToToggle != null && GUILayout.Button("Select Behaviour"))
        {
            // Open the selection window
            ToggleComponentZoneWindow.ShowWindow(script);
        }
    }
}

 

using UnityEngine;
using UnityEditor;
using UnityEngine.XR.Content.Interaction;

public class ToggleComponentZoneWindow : EditorWindow
{
    private ToggleComponentZone targetSelector;
    private Behaviour[] behaviours;

    // Method to create the window
    public static void ShowWindow(ToggleComponentZone target)
    {
        ToggleComponentZoneWindow window = (ToggleComponentZoneWindow)GetWindow(typeof(ToggleComponentZoneWindow), true, "Select Behaviour");
        window.targetSelector = target;
        window.behaviours = target.componentToToggle.GetComponents<Behaviour>();
        window.Show();
    }

    void OnGUI()
    {
        if (behaviours != null)
        {
            foreach (Behaviour behaviour in behaviours)
            {
                if (GUILayout.Button(behaviour.GetType().Name))
                {
                    targetSelector.componentToToggle = behaviour; // Assign the selected behaviour
                    Debug.Log(behaviour.GetType().Name + " selected.");
                    Close(); // Close the window after selection
                }
            }
        }
    }
}

 

 


 

Gaze오브젝트 자식으로 Cube를 하나 만들고 

 

Scale과 Position을 변경 합니다.

 

메터리얼을 변경 합니다.

InteractablesMaterial.unitypackage
0.00MB

 

Cube를 선택하고 XR Simple Interactable컴포넌트를 부착 합니다.

Gaze Configuration의 Allow Gaze Interaction을 체크합니다.

Gaze로 Select를 하고 싶다면 Allow Gaze Select도 체크 합니다.

 

Cube를 선택하고 자식으로 빈오브젝트 (Feedback)을 생성 합니다.

 

Interactable Source에 Cube를 넣어주고 

Ignore Hover Priority Events는 체크 해제 합니다. 

Actiavate Click Animation Mode를 Activated로 변경 합니다.

 

 

Feedback 자식으로 빈오브젝트 Audio Effect를 생성하고 

 

Audio Source와 Audio Affordance Receiver컴포넌트를 추가 합니다.

 

AudioClip에 Button_14_Hover를 넣어주고 

Affordance State Provider에 Feedback오브젝트를 넣어줍니다.

Affordance Theme Datum 필드도 채워줍니다.

 

 

Feedback오브젝트의 자식으로 빈오브젝트 (Visual Effect)를 생성하고 

 

각 필드를 다음과 같이 채워 줍니다.

 

실행후 결과를 확인 합니다.


 

스탭 3

 

Hands Free Coaching Card프리팹을 위치 시킵니다.

Hand Free Card.unitypackage
1.20MB

 

 

 

Progress Bar Controller를 부착 합니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

namespace UnityEngine.XR.Content.Interaction
{
    /// <summary>
    /// Class to control progress bar on coaching card prefabs.
    /// </summary>
    public class ProgressBarController : MonoBehaviour
    {
        [SerializeField]
        [Tooltip("The projectile that's created")]
        SkinnedMeshRenderer m_Blendshape = null;

        [SerializeField]
        [Tooltip("Lenght of the blendshape progress bar.")]
        float m_BarLength = 28.0f;

        [SerializeField]
        [Tooltip("Duration to dwell and fill the progress bar.")]
        float m_Seconds = 7.5f;

        [SerializeField]
        [Tooltip("The next step GameObject to enable when this step is complete.")]
        GameObject m_NextStep = null;

        [SerializeField] private XRInteractorReticleVisual _reticleVisual;

        float m_SecondsCnt;
        bool m_UpdateTimer;

        void Update()
        {
            if (m_UpdateTimer)
                UpdateTimer();
        }

        /// <summary>
        /// Updates the state of the proress bar.
        /// </summary>
        /// <param name="state">When true, the progress bar will progress. When false, the progresss bar will not progress.</param>
        /// <returns></returns>
        public void UpdateTimerState(bool state)
        {
            m_UpdateTimer = state;
        }

        void UpdateTimer()
        {
            if (!_reticleVisual.enabled) return;
            m_SecondsCnt += Time.deltaTime;
            if (m_SecondsCnt >= m_Seconds)
            {
                m_SecondsCnt = 0f;
                if (m_NextStep != null)
                    m_NextStep.SetActive(true);

                gameObject.SetActive(false);
            }

            m_Blendshape.SetBlendShapeWeight(0, m_SecondsCnt / m_Seconds * m_BarLength);
        }
    }
}

 

 

Blendshape에 ProgressBar 오브젝트를 넣어주고 

 

 

Reticle Visual에 Gaze Interactor를 넣어 줍니다

 

 

Hands Free Coaching Card오브젝트를 선택후  Hover Entered, Exited이벤트를 등록 합니다.

 

 

실행후 결과를 확인 합니다.

 


이 문장은 VR(가상 현실) 또는 AR(증강 현실)과 같은 상호작용 시스템에서의 '시선 상호작용자(Gaze Interactors)'와 '상호작용 가능한 객체(Interactables)' 사이의 상호작용을 설정하는 옵션에 대해 설명하고 있습니다. 간단히 요약하면 다음과 같습니다:

  • "Allows gaze interactors to interact with this interactable."
    • 이 옵션을 활성화하면, 사용자가 시선으로 대상 객체를 바라보았을 때, 해당 객체와 상호작용할 수 있습니다. 즉, 시선 상호작용자는 객체를 '보는 것만으로도' 특정 행동(예: 객체 선택, 버튼 클릭 등)을 유발할 수 있습니다.
  • "If false, interactor will receive no interactable events from gaze interactors."
    • 이 옵션을 비활성화하면, 시선 상호작용자는 해당 객체와의 상호작용에서 배제됩니다. 즉, 사용자가 객체를 바라보더라도 아무런 상호작용이 발생하지 않습니다. 이는 특정 객체가 시선에 의한 상호작용을 받지 않아야 할 때 유용할 수 있습니다.

이러한 설정은 VR/AR 상호작용 설계에서 중요한 부분입니다. 예를 들어, 사용자가 의도치 않게 너무 많은 객체와 상호작용을 유발하는 것을 방지하거나, 특정 상황에서만 특정 객체와의 상호작용을 허용하고자 할 때 이러한 옵션을 조정할 수 있습니다. 이는 사용자 경험(UX)을 향상시키고, 상호작용의 정확성을 높이는 데 기여할 수 있습니다.

 

 


이 문장은 시선 기반 상호작용 시스템에서 "상호작용 가능한 객체(interactable)"를 "시선 상호작용자(gaze interactors)"가 선택할 수 있는지 여부를 설정하는 옵션에 대해 설명하고 있습니다. 간단히 요약하면 다음과 같습니다:

  • "Allows gaze interactors to select this interactable."
    • 이 옵션을 활성화하면, 사용자가 시선으로 해당 객체를 바라봄으로써 객체를 '선택'할 수 있습니다. 즉, 사용자가 그 객체를 정확히 바라보고 있을 때, 특정 입력(예: 버튼 클릭, 시선 유지 등)을 통해 객체와의 상호작용을 시작할 수 있습니다.

이 설정은 VR(가상 현실)이나 AR(증강 현실)과 같은 상호작용 시스템에서 사용자가 시선을 통해 인터페이스 요소나 가상 객체와 효과적으로 상호작용할 수 있게 하는 중요한 기능입니다. 사용자는 물리적인 컨트롤러를 사용하지 않고도 시선을 통해 버튼을 클릭하거나 메뉴를 탐색하는 등의 작업을 수행할 수 있습니다. 이 기능은 특히 손을 자유롭게 사용할 수 없는 상황이나, 사용자가 물리적인 컨트롤러를 사용하는 것이 불편한 환경에서 유용하게 사용될 수 있습니다.

 

 


이 문장은 시선 기반 상호작용 시스템에서 '시선 보조(Gaze Assistance)' 기능에 대해 설명하고 있습니다. 이 기능은 시선 상호작용자(gaze interactor)가 상호작용 가능한 객체(interactable)에 '스냅 볼륨(snap volume)'을 배치하여, 광선 상호작용자(ray interactor)가 해당 볼륨에 '스냅(snap)'하도록 허용합니다. 간단히 요약하면 다음과 같습니다:

  • "Enables gaze assistance"
    • 이 옵션을 활성화하면, 시선 보조 기능이 활성화됩니다. 이는 상호작용 시스템이 시선 기반 입력을 더 잘 인식하고 처리할 수 있도록 도와줍니다.
  • "Allows a gaze interactor to place a snap volume at this interactable"
    • 시선 상호작용자가 상호작용 가능한 객체에 스냅 볼륨을 배치할 수 있습니다. 스냅 볼륨은 상호작용자가 객체에 더 쉽게 '접근'하고 '상호작용'할 수 있도록 하는 가상의 공간입니다.
  • "For ray interactors to snap to"
    • 광선 상호작용자(ray interactor)는 이 스냅 볼륨에 스냅할 수 있습니다. 즉, 사용자가 광선(예: VR 컨트롤러에서 발사되는 가상의 광선)을 사용하여 객체와 상호작용할 때, 광선이 스냅 볼륨 내에 들어오면 자동으로 그 객체에 '부착'되어 상호작용이 더 쉬워집니다.

이 기능은 사용자가 시선 또는 광선을 사용하여 가상 환경 내의 객체와 상호작용할 때 정확도와 편의성을 높이기 위한 것입니다. 특히 복잡하거나 정밀한 상호작용이 필요한 경우, 스냅 볼륨은 사용자가 원하는 객체를 빠르고 정확하게 선택할 수 있도록 도와줍니다. 이는 사용자 경험을 개선하고 상호작용 시스템의 직관성을 높이는 데 기여할 수 있습니다.

 

 

반응형
:

Oculus Locomotion Environment Package

VR/XR Interaction Toolkit 2024. 1. 19. 16:21
반응형

https://drive.google.com/file/d/1n0Wwna94po_Tq1cerceVOrzGsAb2A--b/view?usp=drive_link

 

LocomotionEnvironment.unitypackage

 

drive.google.com

 

반응형
:

XR Origin (Action-based) 오큘러스 입력 받기

VR/XR Interaction Toolkit 2024. 1. 18. 16:30
반응형

 

https://docs.unity.cn/kr/current/Manual/xr_input.html

 

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;

public class InputManager : MonoBehaviour
{
    public InputDevice LeftControllerInputDevice { get; private set; }
    public InputDevice RightControllerInputDevice { get; private set; }

    public static InputManager Instance;

    private void Awake()
    {
        if (Instance == null)
            Instance = this;

        DeviceConnected(new InputDevice());
    }

    private void OnEnable() => InputDevices.deviceConnected += DeviceConnected;

    private void OnDisable() => InputDevices.deviceConnected -= DeviceConnected;

    private void DeviceConnected(InputDevice device)
    {
        var devicesL = new List<InputDevice>();
        InputDevices.GetDevicesAtXRNode(XRNode.LeftHand, devicesL);
        if (devicesL.Count > 0 && devicesL[0] != null && devicesL[0].isValid)
            LeftControllerInputDevice = devicesL[0];

        var devicesR = new List<InputDevice>();
        InputDevices.GetDevicesAtXRNode(XRNode.RightHand, devicesR);
        if (devicesR.Count > 0 && devicesR[0] != null && devicesR[0].isValid)
            RightControllerInputDevice = devicesR[0];
    }

    private void OnDestroy()
    {
        if (Instance == this)
            Instance = null;
    }

    public bool GetButtonPressed(InputFeatureUsage<bool> usage, InputDevice device)
    {
        if (device.TryGetFeatureValue(usage, out bool pressed))
            return pressed;

        return false;
    }

    public Vector2 GetAxis(InputFeatureUsage<Vector2> usage, InputDevice device)
    {
        if (device.TryGetFeatureValue(usage, out Vector2 axisValue))
            return axisValue;

        return Vector2.zero;
    }

    public static void TryHaptics(uint channel, float amplitude, float duration, InputDevice device)
    {
        if (device.TryGetHapticCapabilities(out HapticCapabilities capabilities))
        {
            if (!capabilities.supportsImpulse)
                return;

            device.SendHapticImpulse(channel, amplitude, duration);
        }
    }

    public void SmallHapticsPulse(bool left)
    {
        if (!left)
            TryHaptics(0, 0.5f, 0.25f, RightControllerInputDevice);

        if (left)
            TryHaptics(0, 0.5f, 0.25f, LeftControllerInputDevice);
    }

    public void MediumHapticsPulse(bool left)
    {
        if (!left)
            TryHaptics(0, 0.75f, 0.5f, RightControllerInputDevice);

        if (left)
            TryHaptics(0, 0.75f, 0.5f, LeftControllerInputDevice);
    }

    public void LargeHapticsPulse(bool left)
    {
        if (!left)
            TryHaptics(0, 1f, 0.75f, RightControllerInputDevice);

        if (left)
            TryHaptics(0, 1f, 0.75f, LeftControllerInputDevice);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;

public class Main : MonoBehaviour
{
    [SerializeField] private InputManager inputManager;

    private enum ButtonState
    {
        Pressed,
        Released
    }

    // Button States
    private ButtonState rightPrimaryButtonState = ButtonState.Released;
    private ButtonState leftPrimaryButtonState = ButtonState.Released;
    private ButtonState rightSecondaryButtonState = ButtonState.Released;
    private ButtonState leftSecondaryButtonState = ButtonState.Released;
    private ButtonState rightGripButtonState = ButtonState.Released;
    private ButtonState leftGripButtonState = ButtonState.Released;
    private ButtonState rightTriggerButtonState = ButtonState.Released;
    private ButtonState leftTriggerButtonState = ButtonState.Released;

    // 2D Axis States
    private Vector2 lastRightPrimary2DAxis = Vector2.zero;
    private Vector2 lastLeftPrimary2DAxis = Vector2.zero;
    private Vector2 lastRightSecondary2DAxis = Vector2.zero;
    private Vector2 lastLeftSecondary2DAxis = Vector2.zero;

    // Threshold for detecting significant change in 2D axis
    private const float axisChangeThreshold = 0.1f;

    private void Update()
    {
        // Define all the buttons and axes
        InputFeatureUsage<bool> primaryButton = CommonUsages.primaryButton;
        InputFeatureUsage<bool> secondaryButton = CommonUsages.secondaryButton;
        InputFeatureUsage<bool> gripButton = CommonUsages.gripButton;
        InputFeatureUsage<bool> triggerButton = CommonUsages.triggerButton;
        InputFeatureUsage<Vector2> primary2DAxis = CommonUsages.primary2DAxis;
        InputFeatureUsage<Vector2> secondary2DAxis = CommonUsages.secondary2DAxis;

        // Process each button and axis...
        ProcessButton(primaryButton, ref rightPrimaryButtonState, ref leftPrimaryButtonState, "primary button");
        ProcessButton(secondaryButton, ref rightSecondaryButtonState, ref leftSecondaryButtonState, "secondary button");
        ProcessButton(gripButton, ref rightGripButtonState, ref leftGripButtonState, "grip button");
        ProcessButton(triggerButton, ref rightTriggerButtonState, ref leftTriggerButtonState, "trigger button");

        Process2DAxis(primary2DAxis, ref lastRightPrimary2DAxis, ref lastLeftPrimary2DAxis, "primary 2D axis");
        Process2DAxis(secondary2DAxis, ref lastRightSecondary2DAxis, ref lastLeftSecondary2DAxis, "secondary 2D axis");
    }

    private void ProcessButton(InputFeatureUsage<bool> buttonFeature, ref ButtonState rightState, ref ButtonState leftState, string buttonName)
    {
        bool isRightButtonPressed = inputManager.GetButtonPressed(buttonFeature, inputManager.RightControllerInputDevice);
        bool isLeftButtonPressed = inputManager.GetButtonPressed(buttonFeature, inputManager.LeftControllerInputDevice);

        // Right Controller
        if (!isRightButtonPressed && rightState == ButtonState.Pressed)
        {
            rightState = ButtonState.Released;
        }
        else if (isRightButtonPressed && rightState == ButtonState.Released)
        {
            Debug.Log($"Right {buttonName} was pressed.");
            rightState = ButtonState.Pressed;
        }

        // Left Controller
        if (!isLeftButtonPressed && leftState == ButtonState.Pressed)
        {
            leftState = ButtonState.Released;
        }
        else if (isLeftButtonPressed && leftState == ButtonState.Released)
        {
            Debug.Log($"Left {buttonName} was pressed.");
            leftState = ButtonState.Pressed;
        }
    }

    private void Process2DAxis(InputFeatureUsage<Vector2> axisFeature, ref Vector2 lastRightAxis, ref Vector2 lastLeftAxis, string axisName)
    {
        Vector2 rightAxis = inputManager.GetAxis(axisFeature, inputManager.RightControllerInputDevice);
        Vector2 leftAxis = inputManager.GetAxis(axisFeature, inputManager.LeftControllerInputDevice);

        if (Vector2.Distance(rightAxis, lastRightAxis) > axisChangeThreshold)
        {
            Debug.Log($"Significant change in right {axisName}: {rightAxis}");
            lastRightAxis = rightAxis;
        }

        if (Vector2.Distance(leftAxis, lastLeftAxis) > axisChangeThreshold)
        {
            Debug.Log($"Significant change in left {axisName}: {leftAxis}");
            lastLeftAxis = leftAxis;
        }
    }
}

반응형
: