개발 TIL

6/5 부트캠프 개발 TIL (발판 이동 구현)

HJTL 2024. 6. 5. 21:16

이번에는 이동플랫폼이 가로방향일 때 지정된 범위까지 왔다갔다하는 구현을 해 보았다.

위의 영상처럼 플랫폼이 어느 방향이든 지정 방향과 거리만큼 왔다갔다 할 것이다.

 

구현할때 이렇게 생각했다.

먼저 Start함수를 호출할 때 시작 위치와 그 위치의 방향의 거리를 계산해서 이동시키려고 했다.

    private void MovePlatform() //발판 움직임
    {
        int moveReverse = isReverse ? -1 : 1;

        if (moveData.movableType == MovablePlatformType.Horizontal)
        {
            //x축 이동
            //현재 위치에서 최대 거리보다 넘어가면
            if (transform.position.x >= endPos.x && !isReverse)
                WaitToReverse();
            else if(transform.position.x <= startPos.x && isReverse)
                WaitToReverse();
            else
            {
                transform.Translate(Vector3.right * moveData.platformSpeed * moveReverse * Time.deltaTime);
            }
        }
        else if (moveData.movableType == MovablePlatformType.Vertical)
        {
            //y축 이동
            if (transform.position.y >= endPos.y && !isReverse)
                WaitToReverse();
            else if (transform.position.y <= startPos.y && isReverse)
                WaitToReverse();
            else
            {
                transform.Translate(Vector3.up * moveData.platformSpeed * moveReverse * Time.deltaTime);
            }
        }
    }

어제 TIL의 코드의 일부분인데, 조건문의 "transform.position.x >= endPos.x"같은 문장을 보면 

endPos가 startPos에서 지정된 거리만큼 떨어져 있는 지점인데(startPos가 시작했을때 위치를 저장한다)

예를 들어 endPos가 5일때 오브젝트 transform.rotation값이 50˚로 설정한다면 50˚에서 로컬에서 x좌표를 해당 거리까지 이동해야 하는데 월드 좌표계에서 계산이 되어 버려 각도가 높게 나오면 좀 더 멀리 나갈수가 있고 90˚로 하면 끝까지 나가버리는 현상이 발생한다.

 

그리고 코드를 저렇게 짜면 읽기 복잡할 뿐 아니라 코드가 반복적인 것이 나오고 WaitToReverse함수를 많이 호출되는 것이 나와서 효율적이지 못하다.

 

이런 문제를 해결하는 방법은 Vector3에 내장되어있는 Distance함수로 비교하는 것이다.

Vector3.Distance(시작위치, 목표위치);

public class MovablePlatform : Platform
{
	...

    private float nowTime = 0;
    private Vector3 startPos;
    private Vector3 endPos;
    private Vector3 moveDirection;  //이동하려는 방향
    
    ...

    private void Start()
    {
        startPos = transform.localPosition;  //원래 자리로 돌아오기 위한 위치

        endPos = transform.TransformPoint(new Vector3(maxDistance, 0f, 0f));
        //Debug.Log(Vector3.Distance(startPos, endPos));	두 벡터의 길이가 어디까지인지를 계산한다.

        if (moveData.movableType == MovablePlatformType.Horizontal)
            moveDirection = Vector3.right;
        else if (moveData.movableType == MovablePlatformType.Vertical)
            moveDirection = Vector3.up;
    }

    private void Update()
    {
        MovePlatform();
    }

    private void MovePlatform() //발판 움직임
    {
        int moveReverse = isReverse ? -1 : 1;

        //현재 위치에서 최대 거리보다 넘어가면
        if (Vector3.Distance(startPos, transform.position) > maxDistance && !isReverse)
            WaitToReverse();
        else if(Vector3.Distance(transform.position, startPos) < 0.1f && isReverse)	//0으로 하면 그냥 지나가버린다.
            WaitToReverse();
        else
        {
            transform.Translate(moveDirection * moveData.platformSpeed * moveReverse * Time.deltaTime);
        }
    }
    
    ...
}

 

이전 스크립트와 비교하면 우선 조건문으로 MovablePlatformType으로 받지 않았다. 그리고 방향을 결정할때 moveDirection이라는 벡터를 만들고 Start에서 방향을 결정하고 이동할때 Translate를 Vector3.up, Vector3.right로 하는것보다 moveDirection으로 하는 것이 효율적이라고 생각한다. 그리고 WaitToReverse함수가 덜 호출이 되었다.

 

이건 개인과제하면서 만들었던 건데 똑같이 이동하는 플랫폼을 만든 스크립드다.

    void Update()
    {
        if (startPos.x != endPos.x)
            MovePlatform(transform.position.x, startPos.x, endPos.x);
        if (startPos.y != endPos.y)
            MovePlatform(transform.position.y, startPos.y, endPos.y);
        if (startPos.z != endPos.z)
            MovePlatform(transform.position.z, startPos.z, endPos.z);
    }

    void MovePlatform(float nowTransfom, float startTransform, float endTransform)
    {

        if (startTransform < endTransform)
        {
            //endPos까지 닿으면 대기시간 후 다시 반대로
            if (nowTransfom >= endTransform)
            {
                transform.Translate(Vector3.zero);

                time += Time.deltaTime;
                if (time >= waitTime)
                {
                    time = 0;
                    ChangePosition();
                }
            }
            else
            {
                transform.Translate((endPos - startPos).normalized * Time.deltaTime * speed);
            }
        }
        if (startTransform > endTransform)
        {
            if (nowTransfom <= endTransform)
            {
                transform.Translate(Vector3.zero);

                time += Time.deltaTime;
                if (time >= waitTime)
                {
                    time = 0;
                    ChangePosition();
                }
            }
            else
            {
                transform.Translate((startPos - endPos).normalized * Time.deltaTime * -speed);
            }
        }
    }

이 코드는 두 벡터로 비교를 하여 수행시켰다.

 

이렇게 하면 조건문들이 덕지덕지 붙어있고 기다리는 역시 코드가 두번 중복되어있다.

 

그래서 이동 플랫폼을 구현할 때 이전 스크립트를 참고하는게 효율적이다.

 


 

 

한가지 발견한 문제가 있는데, 플레이어가 발판을 밟을 때 플레이어가 앞 방향을 바라보지 않고 이상한 방향으로 틀어버리는 현상이 발생했다.

 

이러한 이유는 Player의 회전하는 코드를 transform.localEulerAngles = new Vector3(0, yaw, 0);로 짜놨기 때문에 발판에 닿으면 플레이어가 부모 오브젝트의 영향을 받아 회전값이 같이 돌아버리게 된다.

그래서 코드만 살짝 바꾸기만 하면 되는데 transform.rotation = Quaternion.Euler(0, yaw, 0);로 바꾸면 플레이어는 부모 오브젝트의 transform의 영향을 받지 않을 것이다. (권관우 튜터님)

발판 오브젝트가 플레이어와 콜라이전 했으면 플레이어의 부모 오브젝트가 발판으로 설정한 코드를 짜 놨다.

설명대로 코드를 바꾼 결과

 

 

by 스파르타 코딩클럽