내일배움캠프 Unity 51일차 TIL - Voxel로 만든 맵 밟기 연출

Wooooo·2024년 1월 9일
0

내일배움캠프Unity

목록 보기
53/94

[오늘의 키워드]

프로젝트에 이런 연출이 들어가면 어떨까~ 싶어서 심심풀이로 만들어봤다.


[밟으면 살짝 들어가는 Voxel 맵 만들기]

[VoxelCube.cs]

using System.Collections;
using UnityEngine;

public class VoxelCube : MonoBehaviour
{
    [SerializeField] private MeshRenderer meshRenderer;
    private Transform rendererTransform;
    private Coroutine coroutine;

    public enum VoxelFieldType
    {
        Grass,
        Stone,
    }
    public VoxelFieldType type;
    [SerializeField] private Material[] materials;

    private void Start()
    {
        meshRenderer.material = materials[(int)type];
        rendererTransform = transform.GetChild(0);
    }

    public void OnPlayerSteps()
    {
        if (coroutine != null)
            StopCoroutine(coroutine);
        StartCoroutine(RendererPostionChange(rendererTransform.localPosition, new(0, -0.2f, 0), 0.3f));
    }

    public void ExitPlayerSteps()
    {
        if (coroutine != null)
            StopCoroutine(coroutine);
        StartCoroutine(RendererPostionChange(rendererTransform.localPosition, new(0, 0, 0), 0.3f));
    }

    private IEnumerator RendererPostionChange(Vector3 from, Vector3 to, float duration)
    {
        for (float t = 0; t < duration; t += Time.deltaTime)
        {
            rendererTransform.localPosition = Vector3.Lerp(from, to, t / duration);
            yield return null;
        }
        rendererTransform.localPosition = to;
    }
}

간단한 Voxel Cube를 만들어봤다.
OnPlayerSteps() 메서드가 호출되면 큐브가 살짝 내려앉고, ExitPlayerSteps()가 호출되면 원상태로 복구된다.

[PlayerContoller.cs]

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    private CharacterController controller;
    private VoxelCube currentVoxel;

    private void Start()
    {
        controller = GetComponent<CharacterController>();
    }

    private void Update()
    {
        Move();
        VoxcelRay();
    }

    private void VoxcelRay()
    {
        var hits = Physics.RaycastAll(transform.position, Vector3.down, float.PositiveInfinity);
        foreach (var hit in hits)
        {
            if (hit.collider.CompareTag("Player"))
                continue;

            if (hit.collider.TryGetComponent<VoxelCube>(out var voxel))
            {
                if (voxel != currentVoxel)
                {
                    if (currentVoxel != null)
                        currentVoxel.ExitPlayerSteps();
                    voxel.OnPlayerSteps();
                    currentVoxel = voxel;
                }
            }
        }
    }

    private void Move()
    {
        Vector3 moveVelocity = Vector3.zero;
        moveVelocity.z = Input.GetAxisRaw("Vertical");
        moveVelocity.x = Input.GetAxisRaw("Horizontal");
        controller.Move(7f * Time.deltaTime * moveVelocity.normalized);
    }
}

플레이어는 발밑으로 레이를 쏴서 탐지한 Voxel이 현재 밟고 있는 Voxel과 다르다면 새로운 Voxel를 밟은 것이다.
따라서 currentVoxel의 ExitPlayerSteps를 호출해주고 새롭게 탐지한 voxel에 OnPlayerSteps를 호출해준 다음, currentVoxel를 갱신해준다.


[구현 모습]

밟은 Voxel이 내려앉는다.


[고민해야할 것]

아무래도 실제 프로젝트에선 맵이 훨씬 거대할텐데, 어떤 방식으로 현재 밟고 있는 Voxel을 찾을지는 많은 고민이 필요할 것 같다.

Collision을 이용하는 건 아무래도 많은 연산이 필요할 것 같아서 제일 먼저 기각했다.

위의 테스트 프로젝트는 캐릭터 발밑으로 raycast를 쏴서 탐색했는데, 솔직히 이 방법도 맵이 커졌을 때 연산에 어떤 부하가 걸릴지는 잘 모르겠다.

또 다른 방법으로는 voxel들을 딕셔너리와 같은 컨테이너에 넣어두고, (x, z) 좌표값으로 참조가 가능하게 한 다음 플레이어의 x, z좌표를 이용하여 탐색하는 방법도 있을 수 있겠다.

profile
game developer

0개의 댓글