참고
마우스를 클릭했을 때, 블럭을 움직이게 하는 것은 쉽지만, 정확히 한 칸만 이동하는 것은 꽤 까다롭다.
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:
Unity Pro:
Unity 프리미엄 학습:
'개발 > Unity' 카테고리의 다른 글
유니티 - 마우스로 카메라 회전하기 (오일러 각) (4) | 2022.03.05 |
---|---|
유니티 - 선택한 블럭 이동하기 (0) | 2022.02.28 |
유니티 - 오브젝트를 선택된 상태로 만들기 : (1) bool (0) | 2022.02.25 |
유니티 - 화면 클릭 시, RayCast 그리기 (0) | 2022.02.25 |
유니티 - 스크립트로 오브젝트 이동하기, RayCast로 멈추기 (2) (0) | 2022.02.23 |
댓글