유니티 모바일 인풋 시스템

정선호·2023년 7월 25일
0

Unity Features

목록 보기
15/28

재생목록

개요 및 목차

유니티 인풋 시스템을 사용해 모바일에서 주로 사용하는 입력들에 대해 정리한 내용

목차

  • 터치(Touch)
  • 스와이프(Swipe)
  • 핀치 줌(Pinch Zoom)

Touch

강의 영상

인풋 액션 설정

  • 액션 타입을 Value로, 컨트롤 타입을 Vector2로 하여 터치의 위치값을 가져오는 액션
  • 액션 타입을 Button으로 하는 터치 여부를 가져오는 액션

이벤트 처리 스크립트

터치 시 TouchPressed함수가 호출된다.
TouchPressed함수에서는 터치의 위치값을 가져오는 액션에서 값을 따내 터치한 스크린의 스크린 포지션을 가져온다.

EventSystem.current.IsPointerOverGameObject()를 이용해 해당 터치가 UI 위의 터치인지 확인할 수 있다.


Swipe

강의 영상

인풋 액션 설정

  • 액션 타입을 Value로, 컨트롤 타입을 Vector2로 하여 터치의 위치값을 가져오는 액션
  • 액션 타입을 Pass Through 혹은 Value, 컨트롤 타입을 Button으로 하는 터치 여부를 가져오는 액션

이벤트 처리 스크립트

인풋 시스템 스크립트를 생성해 범용적으로 입력을 할당 및 해제해주는 PlayerInput클래스와
Swipe 모션만을 집중적으로 파악하는 SwipeDetection클래스로 나뉘어져 있다.

PlayerInput

namespace Player.Input
{
    public class PlayerInput : MonoBehaviour
    {
        internal PlayerInputSystem input;
        internal Camera mainCam;

        internal PlayerState state;
        
        private StateMachine<PlayerInput> _stateMachine;
    
        #region Events
    
        public delegate void StartTouch(Vector2 pos);
        public event StartTouch OnStartTouch;
    
        public delegate void EndTouch(Vector2 pos);
        public event StartTouch OnEndTouch;
        
        #endregion
        
    
        #region Unity Methods
    
        private void Awake()
        {
            input = new PlayerInputSystem();
            mainCam = Camera.main;

            state = new PlayerState();
            // _stateMachine = new StateMachine<PlayerInput>(this, new OnIdle());
            // _stateMachine.AddState(new OnSwipeAndTouch());
            // _stateMachine.AddState(new OnPinch());
        }
    
        private void OnEnable()
        {
            input.Enable();

            input.Touch.PrimaryTouch.started += StartTouchPrimary;
            input.Touch.PrimaryTouch.canceled += EndTouchPrimary;
        }
    
        private void OnDisable()
        {
            input.Touch.PrimaryTouch.started -= StartTouchPrimary;
            input.Touch.PrimaryTouch.canceled -= EndTouchPrimary;
            
            input.Disable();
        }
        
        private void OnDestroy()
        {
            // _stateMachine.RemoveAllState();
            // _stateMachine = null;
            state = null;

            mainCam = null;
            input = null;
        }

        private void Update()
        {
            //_stateMachine.CurrentState.Update();
        }

        #endregion
    
    
    
        #region Input Methods
    
        private void StartTouchPrimary(InputAction.CallbackContext ctx)
        {
            if (OnStartTouch != null)
            {
                OnStartTouch(ScreenToWorld(input.Touch.PrimaryPosition.ReadValue<Vector2>()));
            }
        }
        
        private void EndTouchPrimary(InputAction.CallbackContext ctx)
        {
            if (OnEndTouch != null)
            {
                OnEndTouch(ScreenToWorld(input.Touch.PrimaryPosition.ReadValue<Vector2>()));
            }
        }
    
        #endregion
    
    
    
        #region Utilities

        private Vector3 ScreenToWorld(Vector3 screenPos)
        {
            // TODO : change screenPos.z to flexible value
            screenPos.z = 5f;
            return mainCam.ScreenToWorldPoint(screenPos);
        }
    
        public Vector2 PrimaryPosition()
        {
            return ScreenToWorld(input.Touch.PrimaryPosition.ReadValue<Vector2>());
        }
    
        #endregion
    }
}

SwipeDetection

namespace Player.Input
{
    public class SwipeDetection : MonoBehaviour
    {
        private PlayerInput _pInput;

        [SerializeField] private float minDistance = 0.2f;
        [SerializeField, Range(0f, 1f)] private float dirThreshold = 0.9f;

        [SerializeField] private GameObject trail;
        
        private Vector2 _startPos;
        private float _startTime;

        private Vector2 _endPos;
        private float _endTime;

        private Coroutine _swipeCor;
        


        #region Input Methods

        private void SwipeStart(Vector2 pos)
        {
            Debug.Log("Start");
            _startPos = pos;

            trail.SetActive(true);
            trail.transform.position = pos;
            
            _swipeCor = StartCoroutine(SwipeCor());
            
            // TODO : Check Second Touch
        }
        
        private void SwipeEnd(Vector2 pos)
        {
            StopCoroutine(_swipeCor);
            _swipeCor = null;
            
            trail.SetActive(false);
            
            _endPos = pos;
            DetectSwipe();
        }

        private IEnumerator SwipeCor()
        {
            while (true)
            {
                // TODO : Change Camera Position
                trail.transform.position = _pInput.PrimaryPosition();
                yield return new WaitForEndOfFrame();
            }
        }

        private void DetectSwipe()
        {
            if (Vector3.Distance(_startPos, _endPos) >= minDistance)
            {
                // TODO : Swipe Event
                Debug.DrawLine(_startPos, _endPos, Color.red, 5f);
                Vector2 dir = (_endPos - _startPos).normalized;
                SwipeDirection(dir);
            }
            else
            {
                // TODO : Touch Event
                Debug.Log("Touched");
            }
        }

        private void SwipeDirection(Vector2 direction)
        {
            if (Vector2.Dot(Vector2.up, direction) > dirThreshold)
            {
                Debug.Log("Swipe Up");
            }
            if (Vector2.Dot(Vector2.down, direction) > dirThreshold)
            {
                Debug.Log("Swipe Down");
            }
            if (Vector2.Dot(Vector2.left, direction) > dirThreshold)
            {
                Debug.Log("Swipe Left");
            }
            if (Vector2.Dot(Vector2.right, direction) > dirThreshold)
            {
                Debug.Log("Swipe Right");
            }
        }

        #endregion
        
        
        
        #region Unity Methods

        private void Awake()
        {
            _pInput = GetComponent<PlayerInput>();
        }

        private void OnEnable()
        {
            _pInput.OnStartTouch += SwipeStart;
            _pInput.OnEndTouch += SwipeEnd;
        }

        private void OnDisable()
        {
            _pInput.OnStartTouch -= SwipeStart;
            _pInput.OnEndTouch -= SwipeEnd;
        }

        private void OnDestroy()
        {
            _pInput = null;
        }

        #endregion
    }
}

Pinch

강의 영상

인풋 액션 설정

  • Value Vector2에 첫 번째 손가락 터치(Touch #0)의 Position 바인드
  • Value Vector2에 두 번째 손가락 터치(Touch #1)의 Position 바인드
  • Button으로 두 번째 손가락 터치(Touch #1) 확인, 인터랙션에 Press(Press only) 추가

명령 처리 스크립트

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

namespace Player.Input
{
    public class PinchDetection : MonoBehaviour
    {
        [SerializeField] private float camSpeed = 4f;
        
        private PlayerInputSystem _input;

        private Coroutine _pinchCor;

        private Transform _camTransform;


        #region Unity Methods

        private void Awake()
        {
            _input = new PlayerInputSystem();
            _camTransform = Camera.main.transform;
        }

        private void OnEnable()
        {
            _input.Enable();
        }

        private void OnDisable()
        {
            _input.Disable();
        }

        private void OnDestroy()
        {
            _input = null;
        }

        private void Start()
        {
            _input.Touch.SecondaryTouch.started += _ => PinchStart();
            _input.Touch.SecondaryTouch.canceled += _ => PinchEnd();
        }

        #endregion



        #region Input Methods

        private void PinchStart()
        {
            _pinchCor = StartCoroutine(DetectPinch());
        }

        private void PinchEnd()
        {
            StopCoroutine(_pinchCor);
        }

        private IEnumerator DetectPinch()
        {
            float prevDist = 0f, curDist = 0f;

            while (true)
            {
                curDist = Vector2.Distance(_input.Touch.PrimaryPosition.ReadValue<Vector2>(),
                    _input.Touch.SecondaryPosition.ReadValue<Vector2>());
                
                // camera zoom in

                Vector3 curCamPos = _camTransform.position;
                Vector3 targetPos = curCamPos;
                if (curDist > prevDist)
                {
                    targetPos.z -= 1;
                    _camTransform.position = Vector3.Slerp(curCamPos, targetPos, Time.deltaTime * camSpeed);
                }
                // camera zoom out
                else if (curDist < prevDist)
                {
                    targetPos.z += 1;
                    _camTransform.position = Vector3.Slerp(curCamPos, targetPos, Time.deltaTime * camSpeed);
                }

                prevDist = curDist;
                yield return null;
            }
        }
        
        #endregion
    }
}
profile
학습한 내용을 빠르게 다시 찾기 위한 저장소

1개의 댓글

comment-user-thumbnail
2023년 7월 25일

잘 읽었습니다. 좋은 정보 감사드립니다.

답글 달기