유니티/모뉴먼트 밸리 모작

[Unity] 모뉴먼트 밸리 모작 - 2. 플레이어 움직이기

이름?없음 2024. 2. 22. 19:29
반응형

 

반응형

모뉴먼트 밸리 플레이어 움직임을 만들어 보겠습니다

우선 저번글에서 맵과 착시현상을 구현했는데요

 

이번 글에서는 플레이어 움직임을 구현해 보도록 하겠습니다

우선 플레이어가 움직일 수 있는 길을 표현하는 스크립트를 작성해야 합니다

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

public class Road : MonoBehaviour
{

    //연결되어있는 길을 저장하는 List
    [field:SerializeField] public List<Road> connectRoad { get; private set; } = new();

    //Ray의 레이어마스크
    [SerializeField] private LayerMask roadMask;

    private void Awake()
    {
        
        //Ray를 쏠 방향을 담아놓은 배열
        Vector3[] dirs = 
            {
            Vector3.forward, Vector3.up, 
            Vector3.back, Vector3.down, 
            Vector3.right, Vector3.left
        };

        foreach(var dir in dirs)
        {

            if(Physics.Raycast(transform.position,  dir, out var info, 1.3f, roadMask))
            {

                //자신의 주변에 길이 있다면 리스트에 추가해주기
                connectRoad.Add(info.transform.GetComponent<Road>());

            }

        }

    }

}

 

Road스크립트는 저희가 저번에 만들었던 맵의 블록에 들어가게 될 것이며 자신과 인접한 블록을 가지고 있습니다

 

그다음은 플레이어입니다

플레이어는 자신이 지금 있는 길로부터 타깃이 되는 길까지 최단거리를 찾아 이동합니다

BFS알고리즘을 약간 참고하여 작성했습니다

using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class Player : MonoBehaviour
{

    //플레이어가 지금 있는 길
    [SerializeField] private Road currentRoad;

    public void Move(Road targetRoad)
    {

        var poss = GetPositions(targetRoad);

        if (poss == null) return;

        currentRoad = targetRoad;

        Sequence seq = DOTween.Sequence();


        //DOTWeen을 이용해 움직이기
        foreach(var item in poss)
        {

            seq.Append(transform.DOMove(item + new Vector3(0, 1, 0), 0.3f).SetEase(Ease.Linear));

        }

    }

    /// <summary>
    /// 최단거리의 길 탐색하기
    /// </summary>
    /// <param name="targetRoad"></param>
    /// <returns></returns>
    private List<Vector3> GetPositions(Road targetRoad)
    {

        Queue<(List<Vector3> roads, Road oldRoad, Road serchRoad)> visitedQ = new();

        foreach(var road in currentRoad.connectRoad)
        {

            visitedQ.Enqueue((new() { currentRoad.transform.position }, currentRoad, road));

        }

        while(visitedQ.Count > 0)
        {

            var current = visitedQ.Dequeue();

            if(current.serchRoad == targetRoad)
            {

                current.roads.Add(current.serchRoad.transform.position);

                return current.roads;

            }

            foreach(var road in current.serchRoad.connectRoad)
            {

                if(road == current.oldRoad) continue;

                //값 복사
                var copyList = current.roads.ToList();
                copyList.Add(current.serchRoad.transform.position);

                visitedQ.Enqueue((copyList, current.serchRoad, road));

            }

        }

        return null;

    }

}

 

그리고 PlayerController에서 플레이어에게 움직이라는 명령을 내리는 식으로 디자인하였습니다

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

public class PlayerController : MonoBehaviour
{

    [SerializeField] private LayerMask roadMask;
    [SerializeField] private Player player;

    private void Update()
    {

        //마우스 좌클릭 시
        if (Input.GetMouseButtonDown(0))
        {

            CameraRay();

        }

    }

    private void CameraRay()
    {

        //마우스 클릭위치로 발사되는 Ray
        var ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        if(Physics.Raycast(ray, out var hitInfo, 1000, roadMask)) 
        {

            player.Move(hitInfo.transform.GetComponent<Road>());

        }

    }

}

 

자 이제 코드는 끝났습니다

 

우선 저번에 만든 큐브들에 모두 Road 스크립트를 넣어주세요

그리고 Road라는 LayerMask를 만들어주세요

 

Main카메라의 컬링마스크에 Road도 포함되게 설정해 주세요

 

Road스크립트의 마스크를 Road로 설정해 주세요

 

 

그 후 착시현상이 일어나는 부분의 Road를 서로 이어주세요

이런 식으로 드래그 드롭으로 이어주시면 됩니다

 

그 후 플레이어 오브젝트를 만든 후 적절히 배치해 줍니다

플레이어에 Top레이어를 넣고 Player와 PlayerController스크립트를 넣어주세요 그 후 인자들을 알맞게 설정해 주세요

 

플레이어에게 Top레이어를 넣어서 이상하게 보이는 부분이 있을 수 있습니다 그런 부분은 저번글에서 했던 것처럼 Quad를 넣어주시면 됩니다

저는 추가로 스페이스바를 누르면 다리가 돌아가는 코드도 작성했습니다

using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Handle : MonoBehaviour
{

    private void Update()
    {

        if (Input.GetKeyDown(KeyCode.Space))
        {

            transform.DORotate(new Vector3(90, 0, 0), 0.3f);

        }

    }

}

 

이제 완성입니다 실행해 보시면 잘 작동하는 것을 확인할 수 있습니다

 

마지막으로 여렵거나 따라 하기 힘드신 분들을 위하여 프로젝트를 유니티 페키지화 시켜 놓았습니다

BLOG_2.unitypackage
0.26MB

 

반응형