본문 바로가기
개발/Unity

유니티 - 드래그로 블럭 옆으로 한 칸 움직이기 (Drag GameObject Snapped to a Grid)

by 피로물든딸기 2022. 6. 12.
반응형

Unity 전체 링크

 

블럭 한 칸 이동하기 (그리드 기반 이동, 코루틴)

드래그로 오브젝트 Y축 회전하기 (Drag to Rotate Object in Y-axis)
드래그로 오브젝트 위, 아래로 움직이기 (Drag Object in Y-Axis)

드래그로 오브젝트 움직이기 (Move GameObject with Drag)
드래그로 땅 위의 오브젝트 움직이기 (Drag and Move on the Ground)
드래그로 평면 위의 오브젝트 움직이기 (Drag and Move on the Plane)

드래그로 블럭 옆으로 한 칸 움직이기 (Drag GameObject Snapped to a Grid)


아래와 같이 드래그로 블럭을 움직이고, 한 칸 단위(그리드)로만 움직이도록 구현해보자.

 

먼저 드래그로 자연스럽게 블럭을 움직이기 위해서는 블럭의 뒤에 Plane이 숨겨져있어야 한다.

관련 내용은 링크를 참고하자.

 

여기에서는 블럭이 평면에 붙지 않고 현재 위치를 그대로 움직이므로 OnMouseDrag가 아래와 같이 변경된다.

planeDot이 현재 hitPos의 position이면 현재 상태(Plane과 큐브와의 거리)를 유지하게 된다.

그리고 x 축(오른쪽과 왼쪽)으로만 움직이도록 position을 수정한다.

    void OnMouseDrag()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        int layer = 1 << LayerMask.NameToLayer(HIDDEN_PLANE);
        if (Physics.Raycast(ray, out hitLayerMask, Mathf.Infinity, layer))
        {
            Vector3 normal = hitLayerMask.transform.up;
            Vector3 planeDot = hitPos.transform.position;
            Vector3 A = Camera.main.transform.position;
            Vector3 B = hitLayerMask.point;

            Vector3 temp = getContactPoint(normal, planeDot, A, B);
            Vector3 setPos = hitPos.transform.position;

            setPos.x = temp.x;
            hitPos.transform.position = setPos;
        }
    }

 

숨겨진 평면의 Layer는 HiddenPlane이다. 코드에서 const로 정의해둔다.

int layer = 1 << LayerMask.NameToLayer(HIDDEN_PLANE);

 

이제 코루틴으로 한 칸 단위로만 움직이도록 하면 된다.

어디로 움직여야할지 moveBlock에 알려주면 blockMoveTime 동안 그 자리로 이동한다.

    public void moveBlock(Vector3 targetPosition)
    {
        StartCoroutine(moveBlockCoroutine(targetPosition));
    }

    private IEnumerator moveBlockCoroutine(Vector3 targetPosition)
    {
        float elapsedTime = 0.0f;
        Vector3 currentPosition = transform.position;

        while (elapsedTime < blockMoveTime)
        {
            elapsedTime += Time.deltaTime;

            transform.position = Vector3.Lerp(currentPosition, targetPosition, elapsedTime / blockMoveTime);

            yield return null;
        }

        transform.position = targetPosition;
    }

 

마우스를 뗐을 때, 호출해주면 된다.

돌아가야할 위치는 현재 위치에서 반올림한 위치로 돌아가도록 하였다.

    void OnMouseUp()
    {
        this.transform.parent = null;
        Destroy(hitPos);

        Vector3 tempPos = this.transform.position;
        tempPos.x = Mathf.Round(tempPos.x);

        moveBlock(tempPos);
    }

 

아래의 코드를 블럭에 추가하고 블럭이 콜라이더를 가지고 있으면 정상 작동한다.

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

public class Block : MonoBehaviour
{
    const string HIDDEN_PLANE = "HiddenPlane";

    public float blockMoveTime = .3f;

    RaycastHit hit, hitLayerMask;
    GameObject hitPos;

    Vector3 getContactPoint(Vector3 normal, Vector3 planeDot, Vector3 A, Vector3 B)
    {
        Vector3 nAB = (B - A).normalized;
        return A + nAB * Vector3.Dot(normal, planeDot - A) / Vector3.Dot(normal, nAB);
    }
    void OnMouseDown()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out hit))
        {
            hitPos = new GameObject("Empty");
            hitPos.transform.position = hit.point;
            this.transform.SetParent(hitPos.transform);
        }
    }

    void OnMouseDrag()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        int layer = 1 << LayerMask.NameToLayer(HIDDEN_PLANE);
        if (Physics.Raycast(ray, out hitLayerMask, Mathf.Infinity, layer))
        {
            Vector3 normal = hitLayerMask.transform.up;
            Vector3 planeDot = hitPos.transform.position;
            Vector3 A = Camera.main.transform.position;
            Vector3 B = hitLayerMask.point;

            Vector3 temp = getContactPoint(normal, planeDot, A, B);
            Vector3 setPos = hitPos.transform.position;

            setPos.x = temp.x;
            hitPos.transform.position = setPos;
        }
    }

    void OnMouseUp()
    {
        this.transform.parent = null;
        Destroy(hitPos);

        Vector3 tempPos = this.transform.position;
        tempPos.x = Mathf.Round(tempPos.x);

        moveBlock(tempPos);
    }

    public void moveBlock(Vector3 targetPosition)
    {
        StartCoroutine(moveBlockCoroutine(targetPosition));
    }

    private IEnumerator moveBlockCoroutine(Vector3 targetPosition)
    {
        float elapsedTime = 0.0f;
        Vector3 currentPosition = transform.position;

        while (elapsedTime < blockMoveTime)
        {
            elapsedTime += Time.deltaTime;

            transform.position = Vector3.Lerp(currentPosition, targetPosition, elapsedTime / blockMoveTime);

            yield return null;
        }

        transform.position = targetPosition;
    }
}

 

Unity Plus:

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Unity Pro:

 

Unity Pro

The complete solutions for professionals to create and operate.

unity.com

 

Unity 프리미엄 학습:

 

Unity Learn

Advance your Unity skills with live sessions and over 750 hours of on-demand learning content designed for creators at every skill level.

unity.com

반응형

댓글