📁 개발 히스토리

유니티 - 인벤토리 구현하기(1)

일단몸통박치기 2025. 2. 22. 17:55

참고한 인벤토리 시스템은 농장 시뮬레이터 스타듀밸리

 

어느 게임이든 인벤토리는 거의 무조건 구현할 필요가 있다.

이번에는 간단히 2D 서바이벌 게임에서 주로 사용되는 인벤토리를 구현해본다.

 

 

3주차: 인벤토리 시스템 구현

구현목표 :
1. 가지고 있는 아이템 인벤토리 칸에 표시
2. 인벤토리에 아이템 추가 및 삭제
3. 아이템에 마우스 올리면 툴팁 출력
4. 우클릭으로 아이템 버리기 및 사용
5. 아이템 드래그로 인벤토리 내에서 위치 변경

추가 디벨롭
a. 인벤토리 핫바 구현 (단축키 설정, 아이템 사용, 아이템 드랍 등)

 

 

 

스프라이트는 itch.io에서 적당한 아트를 구매했다.(이것도 사연이 길다...)

 

 

가장 먼저 타일맵을 이용해서 맵을 찍고 캐릭터 이동을 구현해줬다.

 

 

 

타일맵은 패키지 매니저로 추가해주면 윈도우 창에서 켤 수 있다

 

 

팔레트에 잘라둔 스프라이트를 드래그해 넣고 팔레트에서 복사한 스프라이트를 그리드에 찍으면 된다.

그리드는 스프라이트 셋팅에 맞게 타일 사이즈를 수정한다.

 

 

 

 

스프라이트를 팔레트로 옮기면 이렇게 각 이미지들이 각각 저장된다.
타일 사이즈가 16*16, 픽셀 퍼 유닛도 16으로 세팅하자 사이즈가 적당해졌다.

 

 

 

캐릭터 움직임은 저번(뱀서 모작)과 동일하게 플레이어가 보고 있는 방향(enum)값을 바탕으로 애니메이션을 적용하기 위해 PlayerStates와 PlayerSeeing 변수로 방향과 상태값을 저장하게끔 했다.

 

public class IV_Player : MonoBehaviour
{
    [Header("Datas")]
    public ViewWay PlayerSeeing;
    public PlayerStates PlayerState;

    [Space(5)]

    [Header("Settings")]
    public float PlayerSpeed;


    [Space(5)]

    [Header("UI Components")]
    public Image PLAYER;
    public Animator PlayerAni;
    public IV_Inventory Inventory;

    public enum ViewWay
    {
        F,
        B,
        R,
        L,
        none
    }

    public enum PlayerStates
    {
        IDLE,
        WALK,
        none
    }

 

 

조금 다르긴 하나 이전과 동일한 움직임으로 구현했는데 대각선 이동 문제도 해결할 겸 Input.GetAxis를 이용했다.

(참고:https://backback.tistory.com/422)

 

대각선 이동 문제는 우상단으로 이동한다 칠때

우측(0,1)으로 1만큼, 상단(1,0)으로 1만큼 이동할 경우

우상단(1,1)에 도착한 거리는 대각선으로 이동했기 때문에 루트2로

한 방향으로 이동하는 것보다 빠르게 이동하게 되는 문제다.

 

그 해결책으로 Normalize()를 통해 백터 사이즈를 1로 줄이고

방향값만 남겨 내가 원하는 이동속도를 곱해 이동하도록 구현했다.

 

마지막으로 들어오는 입력에 따라 플레이어 캐릭터가 보는 방향을 규정하고

IDLE 상태인지 WALK 상태인지에 따라 애니메이션을 플레이시켰다.

 

void Update()
    {
        if (Input.GetAxis("Horizontal") != 0 || Input.GetAxis("Vertical") != 0)
        {
            float dirX = Input.GetAxis("Horizontal");
            float dirY = Input.GetAxis("Vertical");

            Vector3 dirXY = Vector3.right * dirX + Vector3.up * dirY;

            dirXY.Normalize();

            transform.position += dirXY * PlayerSpeed * Time.deltaTime;

            if(Math.Abs(dirXY.x) > Math.Abs(dirXY.y))
            {
                if (dirXY.x > 0)
                {
                    PlayerSeeing = ViewWay.R;
                }
                else
                {
                    PlayerSeeing = ViewWay.L;
                }
            }
            else
            {
                if (dirXY.y > 0)
                {
                    PlayerSeeing = ViewWay.B;
                }
                else
                {
                    PlayerSeeing = ViewWay.F;
                }
            }
            PlayerState = PlayerStates.WALK;
        }
        else
        {
            PlayerState = PlayerStates.IDLE;
        }

        switch (PlayerState)
        {
            case PlayerStates.IDLE:
                if (!PlayerAni.GetCurrentAnimatorStateInfo(0).IsName(new string("R_IDLE_" + PlayerSeeing)))
                {
                    PlayerAni.Play(new string("R_IDLE_" + PlayerSeeing));
                }
                break;
            case PlayerStates.WALK:
                if (!PlayerAni.GetCurrentAnimatorStateInfo(0).IsName(new string("R_WALK_" + PlayerSeeing)))
                {
                    PlayerAni.Play(new string("R_WALK_" + PlayerSeeing));
                }
                break;
        }
    }