본문 바로가기
개발/Unity

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

by 피로물든딸기 2022. 2. 28.
반응형

Unity 전체 링크

 

참고

- 드래그로 블럭 옆으로 한 칸 움직이기

 

마우스를 클릭했을 때, 블럭을 움직이게 하는 것은 쉽지만, 정확히 한 칸만 이동하는 것은 꽤 까다롭다.

Update에서 Translate + delteTime으로 움직이기 때문에

매 프레임마다 정확히 한 칸 움직인 것을 체크하기도 까다롭고 구현하기도 까다롭다.

 

이런 경우 유니티의 코루틴(coroutine)을 이용하면 해결할 수 있다.

코루틴은 다음 프레임에서 중지한 곳부터 실행을 계속할 수 있는 기능이다.

 

아래의 함수를 보자.

 

먼저 클릭 이벤트가 발생하면 moveBlockTranslate라는 함수가 코루틴으로 실행된다.

moveBlockTranslate에서는 move = true로 변경된다.

여기서 move는 이동 중인지 판단할 때 필요하다. (이동 중일 때  마우스 클릭을 막는다.)

이후 targetPosition으로 Translate를 이용해 움직인다.

 

1 프레임 이후 yield return으로 moveBlockTranslate가 종료되고,

다음 프레임에서 종료된 시점(while문 내부)에서 함수가 실행된다.

 

targetPosition과 현재 위치의 차이가 0.01f보다 적어지면 while문이 종료되고,

남은 보정칸으로 tranform.position = targetPosition을 해주면 정확히 왼쪽으로 한 칸 움직인다.

    void Update()
    {
        Debug.DrawRay(transform.position, blockDown * rayLength, Color.red);

        if (down) transform.Translate(0, -Time.deltaTime * speed, 0);

        if(Input.GetMouseButtonDown(0) && move == false)
        {
            StartCoroutine(moveBlockTranslate(Vector3.left));
        }
    }

	private IEnumerator moveBlockTranslate(Vector3 dir)
    {
        move = true;

        Vector3 targetPosition = transform.position + dir;

        while (Vector3.Magnitude(targetPosition - transform.position) >= 0.01f)
        {           
            transform.Translate(dir * Time.deltaTime * blockMoveSpeed);   
            yield return null;
        }

        transform.position = targetPosition;

        move = false;
    }

 

위의 경우 거리만큼 Translate로 움직였다.

블럭을 한 칸 움직일 때는 특정 시간동안 움직이게 하는 것이 더 편하다.

경과시간을 체크해서 Lerp(선형 보간)로 움직이는 것이 좀 더 깔끔한 처리가 된다.

    private IEnumerator moveBlockTime(Vector3 dir)
    {
        move = true;

        float elapsedTime = 0.0f;

        Vector3 currentPosition = transform.position;
        Vector3 targetPosition = currentPosition + dir;

        while(elapsedTime < blockMoveTime)
        {
            transform.position = Vector3.Lerp(currentPosition, targetPosition, elapsedTime / blockMoveTime);
            elapsedTime += Time.deltaTime;
            yield return null;
        }
        
        transform.position = targetPosition;

        move = false;
    }

 

Translate(파란색)와 Lerp(빨간색)을 비교해보자.

파란색은 블럭은 testFlag = true로 빨간색은 false로 해두고 아래와 같이 코드를 수정한다.

    public bool testFlag = true;

    void Update()
    {
        if(Input.GetMouseButtonDown(0) && move == false)
        {
            if(testFlag) StartCoroutine(moveBlockTranslate(Vector3.left));
            else StartCoroutine(moveBlockTime(Vector3.left));
        }
    }

 

하지만 파란색의 경우 Time.deltaTime * blockMoveSpeed에 벡터의 크기가 0에서 다시 증가할 수 있으므로

while (Vector3.Magnitude(targetPosition - transform.position) >= 0.01f)을 정상적으로 감지 못한다.

따라서 아래와 같이 계속 이동하는 현상이 발생할 수 있다.

 

속도를 줄이면 (BLOCK_MOVE_SPEED = 2.0f) 현상이 덜하다.


Translate를 이용해서 블럭을 움직이면 정지 조건이 까다롭다. 

속도를 조절하거나 정지 조건 (magnitude < 0.1f)을 완화해야 하고, 원하는 부드러운 움직임을 찾기 어렵다.

그러므로 Lerp를 이용하는 것이 좋다.

 

최종 코드는 아래와 같다.

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

public class BlockMove : MonoBehaviour
{
    public bool move = false;
    private float blockMoveTime = GlobalManager.BLOCK_MOVE_TIME;
    private float blockMoveSpeed = GlobalManager.BLOCK_MOVE_SPEED;
    public bool testFlag = true;

    void Update()
    {
        if(Input.GetMouseButtonDown(0) && move == false)
        {
            if(testFlag) StartCoroutine(moveBlockTranslate(Vector3.left));
            else StartCoroutine(moveBlockTime(Vector3.left));
        }
    }

    private IEnumerator moveBlockTime(Vector3 dir)
    {
        move = true;

        float elapsedTime = 0.0f;

        Vector3 currentPosition = transform.position;
        Vector3 targetPosition = currentPosition + dir;

        while(elapsedTime < blockMoveTime)
        {
            transform.position = Vector3.Lerp(currentPosition, targetPosition, elapsedTime / blockMoveTime);
            elapsedTime += Time.deltaTime;
            yield return null;
        }
        
        transform.position = targetPosition;

        move = false;
    }

    private IEnumerator moveBlockTranslate(Vector3 dir)
    {
        move = true;

        Vector3 targetPosition = transform.position + dir;

        while (Vector3.Magnitude(targetPosition - transform.position) >= 0.01f)
        {           
            transform.Translate(dir * Time.deltaTime * blockMoveSpeed);   
            yield return null;
        }

        transform.position = targetPosition;

        move = false;
    }
}

 

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

반응형

댓글