Unity

Unity 3D Object Drag & Drop

CommitGuy 2024. 6. 4. 09:00

Unity에서 3D 게임을 만들다 보면 3D GameObject를 컴퓨터의 경우 마우스, 모바일의 경우 손가락으로 자유롭게 Drag & Drop 하여 원하는 위치에 놓는 기능이 필요할때가 있다

 

위에서 서술한 기능을 나는 아래와 같이 구현하였다

 

https://www.youtube.com/shorts/H-rxX2ozjUE

3D Object Drag & Drop

 

 

우선 클릭 또는 터치 했을때 내가 Drag & Drop 하고자 하는 GameObject인지 구분부터 해야한다.

private GameObject OnClickObjUsingTag(string tag)
{
    Vector3 touchPos = new Vector3(0, 0, 0);

#if UNITY_EDITOR
    touchPos = Input.mousePosition;
#else
    touchPos = Input.GetTouch(0).position;
#endif

    Ray ray = Camera.main.ScreenPointToRay(touchPos);
    RaycastHit hitInfo;
    Physics.Raycast(ray, out hitInfo);

    if (hitInfo.collider != null)
    {
        GameObject hitObject = hitInfo.collider.gameObject;
        if (hitObject != null)
        {
            if (hitObject.gameObject.tag.Equals(tag))
            {
                return hitObject;
            }
        }
    }

    return null;
}

 

내가 터치한 위치로부터 Camera.main.ScreenPointToRay 함수를 사용해 Ray를 발사하여 Ray에 맞은 GameObject가 내가 이동시키고자 하는 GameObject인지 확인한다.

private void TouchMovedEvent()
{
    if (_movableObj != null)
    {
        Vector3 touchPos = Vector3.zero;
#if UNITY_EDITOR
        touchPos = Input.mousePosition;
#else
        touchPos = Input.GetTouch(0).position;
#endif
        Vector3 worldPos = Camera.main.ScreenToWorldPoint(new Vector3(touchPos.x, touchPos.y, 10));

        //_movableObj.transform.position = worldPos;
        _movableObj.transform.position = Vector3.MoveTowards(_movableObj.transform.position, worldPos, Time.deltaTime * 20f);
    }
}

내가 이동시키려고 한 GameObject가 맞다면 내가 드래그 하는 동안 해당 GameObject를 내가 터치한 위치를 World Position으로 변환시킨(Camera.main.ScreenToWorldPoint 함수) 좌표값으로 이동시킨다

 

private void TouchEndedEvent()
{
    if (_movableObj != null)
    {
        Ray ray = new Ray(_movableObj.transform.position, Camera.main.transform.forward);

        RaycastHit hitInfo;
        Physics.Raycast(ray, out hitInfo);

        if (hitInfo.collider != null)
        {
            Debug.Log("hit info : " + hitInfo.collider.gameObject.name);
            _movableObj.transform.position = hitInfo.collider.gameObject.transform.position;
            _movableObj.transform.position += _offset;
        }
        else
        {
            _movableObj.transform.position = _beforePosition;
        }
    }
}

손가락을 땠을때는 Drag 되고 있든 GameObject로부터 Camera z 방향(forward)으로 Ray를 발사하여 그 Ray에 닿은 위치로 해당 게임오브젝트를 이동시킨다

 

 

전체코드

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

namespace UnityStudy.DragDrop
{
    public class TouchManager : MonoBehaviour
    {
        private Vector2 _lastTouchPos = Vector2.zero;
        private Vector2 _currentTouchPos = Vector2.zero;
        private Vector3 _prePos = Vector3.zero;

        private Vector3 _beforePosition;
        private Vector3 _offset = new Vector3(0.0f, 1.0f, 0.0f);

        [SerializeField] private GameObject _movableObj;

        private const string _moveableTAG = "Moveable";


        private void Update()
        {
#if UNITY_EDITOR
            _lastTouchPos = _currentTouchPos;
            _currentTouchPos = Input.mousePosition;

            if (Input.GetMouseButtonDown(0))
            {
                TouchBeganEvent();
                _prePos = Input.mousePosition;
            }

            if (Input.GetMouseButton(0))
            {
                if (Input.mousePosition != _prePos) { TouchMovedEvent(); }
                else { TouchStayEvent(); }
            }

            if (Input.GetMouseButtonUp(0))
            {
                TouchEndedEvent();
            }
#else
            // Multi-touch 방지 
            if (Input.touchCount <= 0) { return; }

            Touch touchInfo = Input.GetTouch(0);
            _lastTouchPos = _currentTouchPos;
            _currentTouchPos = touchInfo.position;

            switch (touchInfo.phase)
            {
                case TouchPhase.Began:
                    TouchBeganEvent();
                    break;

                case TouchPhase.Moved:
                    TouchMovedEvent();
                    break;

                case TouchPhase.Stationary:
                    TouchStayEvent();
                    break;

                case TouchPhase.Ended:
                    TouchEndedEvent();
                    break;
            }
#endif

#if UNITY_EDITOR
            // 캐릭터 Ray 확인용 
            if (_movableObj != null)
            {
                Ray ray = new Ray(_movableObj.transform.position, Camera.main.transform.forward);
                Debug.DrawRay(ray.origin, ray.direction * 1000, Color.red);
            }


            // TEST Code
            Vector3 touchPos = new Vector3(0, 0, 0);

#if UNITY_EDITOR
            touchPos = Input.mousePosition;
#else
            touchPos = Input.GetTouch(0).position;
#endif

            Ray test_ray = Camera.main.ScreenPointToRay(touchPos);

            Debug.DrawRay(test_ray.origin, test_ray.direction * 1000, Color.blue);

#endif
        }

        private void TouchBeganEvent()
        {
            _movableObj = OnClickObjUsingTag(_moveableTAG);

            if (_movableObj != null)
            {
                _beforePosition = _movableObj.transform.position;
            }
        }

        private void TouchMovedEvent()
        {
            if (_movableObj != null)
            {
                Vector3 touchPos = Vector3.zero;
#if UNITY_EDITOR
                touchPos = Input.mousePosition;
#else
                touchPos = Input.GetTouch(0).position;
#endif
                Vector3 worldPos = Camera.main.ScreenToWorldPoint(new Vector3(touchPos.x, touchPos.y, 10));

                //_movableObj.transform.position = worldPos;
                _movableObj.transform.position = Vector3.MoveTowards(_movableObj.transform.position, worldPos, Time.deltaTime * 20f);
            }
        }

        private void TouchStayEvent()
        {

        }

        private void TouchEndedEvent()
        {
            if (_movableObj != null)
            {
                Ray ray = new Ray(_movableObj.transform.position, Camera.main.transform.forward);

                RaycastHit hitInfo;
                Physics.Raycast(ray, out hitInfo);

                if (hitInfo.collider != null)
                {
                    Debug.Log("hit info : " + hitInfo.collider.gameObject.name);
                    _movableObj.transform.position = hitInfo.collider.gameObject.transform.position;
                    _movableObj.transform.position += _offset;
                }
                else
                {
                    _movableObj.transform.position = _beforePosition;
                }
            }

            //_movableObj = null;
        }

        private GameObject OnClickObjUsingTag(string tag)
        {
            Vector3 touchPos = new Vector3(0, 0, 0);

#if UNITY_EDITOR
            touchPos = Input.mousePosition;
#else
            touchPos = Input.GetTouch(0).position;
#endif

            Ray ray = Camera.main.ScreenPointToRay(touchPos);
            RaycastHit hitInfo;
            Physics.Raycast(ray, out hitInfo);

            if (hitInfo.collider != null)
            {
                GameObject hitObject = hitInfo.collider.gameObject;
                if (hitObject != null)
                {
                    if (hitObject.gameObject.tag.Equals(tag))
                    {
                        return hitObject;
                    }
                }
            }

            return null;
        }
    }
}

'Unity' 카테고리의 다른 글

[Unity] PostProcessing  (0) 2024.07.11
[Unity] Animator Component의 Root Motion 이란?  (0) 2024.07.10
Unity Design Pattern - Object Pool  (2) 2024.06.03
유니티 3D Model 파괴 효과 만들기  (0) 2024.02.26
Unity Android Plugin 만들기  (2) 2024.01.28