Carrot
본문 바로가기
Unity/멋쟁이사자처럼 부트캠프

[멋쟁이사자처럼부트캠프] 유니티 게임 개발 5기(34일차) - 2D 플랫포머 게임 (9) 및 2D Rigging 실습

by 독기품은토끼 2025. 7. 3.
✅ 오늘의 학습 목표
1. 2D 플랫포머 게임 - 인벤토리 구현
2. 2D Rigging 실습

 

1. 인벤토리

1. UI 보충하기

어제 인벤토리 배경과 스크롤뷰만 만들고 마무리했었는데, 스크롤뷰가 보이는 부분을 고쳐주고

아이템이 들어갈 수 있도록 슬롯을 만들어줄 것이다.

 

 

스크롤 뷰의 불필요한 흰색 이미지 컬러는 모두 알파값(투명도)를 0으로 변경해 주었다.

스크롤 뷰에 대한 자세한 설명은 이전 포스팅에서 다루었으니 하단 게시글을 참고 바란다.

 

🎈 참고로 스크롤 뷰의 Movement Type은 Clamped로 해주었다.

 

 

[멋쟁이사자처럼부트캠프] 유니티 게임 개발 5기(29일차) - 2D 플랫포머 게임 (4)

✅ 오늘의 학습 목표1. 플레이어- Town 씬에서의 플레이어 동작 방식- 카메라 이동2. 상호작용 이벤트- 팻말- NPC- 집 안팎 이동1. 플레이어1. 플레이어 동작 방식 변경Town 씬에서는 탑다운뷰를 제공

toxicbunny.tistory.com

 

 

이제 Content 오브젝트 자식으로 Button을 만들어 주었고

앵커는 왼쪽 상단 / 크기는 130x130으로 설정해 주었다.

 

 

그런 다음 아이템의 이미지가 보일 수 있도록 Button의 자식으로 Image를 만들어주었고

사이즈는 100x100으로 설정해 주었다.

 

 

Content 자식 오브젝트들이 추가될 때마다 규칙적으로 삽입되도록 해주기 위해서

Content 오브젝트에 Grid Layout Group 컴포넌트를 추가해 주었다.

 

이 작업은 예전에 도어락 만들 때 사용해 보아서 아마 익숙할 거라 생각한다 😊

 

[멋쟁이사자처럼부트캠프] 유니티 게임 개발 5기(16일차) - UI 활용 및 C# 기초 학습(8)

✅ 오늘의 학습 목표1. Game Over / Input Field 구현2. 도어락 UI 구현3. C# 기초 학습 (8) - 반복문1. Cat1. 게임오버 구현게임 인트로를 구현했으니 이제 게임 오버도 구현 고고! 1.1. 오브젝트빈 게임 오브

toxicbunny.tistory.com

 

 

2. 아이템 → 인벤토리 저장 로직

이제 인벤토리 UI는 얼추 만들었으니 플레이어가 아이템을 먹으면 해당 아이템이 인벤토리 UI에 나타나도록 해주는 작업을 해주려고 한다.

🥕 예행 작업
1. Script 생성 (Slot)

 

우선 드랍한 아이템을 획득하면

드랍한 아이템의 Image가 인벤토리 UI에 나타나도록 하고

인벤토리 UI 내에서 버튼을 클릭하면 아이템이 사용되는 로직을 구현해 주겠다.

 

using UnityEngine;
using UnityEngine.UI;

public class Slot : MonoBehaviour
{
    private IItemObject item; // 슬롯에 들어올 아이템
    [SerializeField] private Image itemImage; // 먹은 아이템의 이미지가 들어갈 위치
    [SerializeField] private Button slotButton; // 아이템 Use()를 하기 위한 버튼

    public bool isEmpty = true;
    
    void Awake()
    {
        slotButton.onClick.AddListener(UseItem);
    }

    void OnEnable() // 오브젝트가 On될 때마다 1번 실행되는 기능
    {
        slotButton.interactable = !isEmpty;
        itemImage.gameObject.SetActive(!isEmpty);
    }

    public void AddItem(IItemObject newItem)
    {
        item = newItem;
        isEmpty = false;
        itemImage.sprite = newItem.Icon;
        itemImage.SetNativeSize();

        slotButton.interactable = !isEmpty;
        itemImage.gameObject.SetActive(!isEmpty);
    }

    public void UseItem()
    {
        if (item != null)
        {
            item.Use();
            ClearSlot();
        }
    }

    public void ClearSlot()
    {
        item = null;
        isEmpty = true;
        slotButton.interactable = !isEmpty;
        itemImage.gameObject.SetActive(!isEmpty);
    }
}

 

어제 IItemObject 인터페이스를 구현할 때 Get()과 Use() 함수를 만들어 두었는데

이는 인벤토리에 들어갈 아이템들이 공통적으로 가져야 할 기능을 정의해 둔 것이다.

그래서 슬롯에 들어갈 아이템도 IItemObject 타입으로 선언해 주었다.
이렇게 해두면 슬롯에서는 아이템의 종류를 몰라도 item.Use() 같은 방식으로 일관된 동작 처리가 가능하다.

 

▶ Slot 스크립트의 핵심

if (isEmpty) // 슬롯이 비어있을 때
{
    slotButton.interactable = false;
    itemImage.gameObject.SetActive(false);
}
else // 슬롯이 차있을 때
{
    slotButton.interactable = true;
    itemImage.gameObject.SetActive(true);
}

// 요약해서 쓴 코드
slotButton.interactable = !isEmpty;
itemImage.gameObject.SetActive(!isEmpty);

 

슬롯에 아이템이 있는지 isEmpty로 판단하여 슬롯이 비어있을 경우에만 버튼과 이미지의 활성화 상태를 결정해 주는 코드이다.

if문을 사용하지 않고 논리 연산자를 통해서 코드를 간략하게 적을 수 있다.

 

그리고 [AddItem()] 메서드에서 UI와의 연결을 수행한다.

  • 인터페이스 기반의 아이템을 할당
  • 아이템의 sprite 이미지를 이미지 컴포넌트에 연결
  • 이미지의 사이즈를 원본 크기로 맞춤
  • 슬롯이 비어있는 경우에만 슬롯 갱신

 

 

그런데 인벤토리는 말 그대로 여러 아이템을 담을 수 있는 공간이다.

위 Slot 컴포넌트는 하나의 아이템만을 저장할 수 있는 로직이기 때문에 ItemManager를 통해서 획득한 모든 아이템들이 각각의 슬롯과 직접 연결되어 저장되도록 하는 로직을 구현해주려고 한다.

 

public class ItemManager : MonoBehaviour
{
    public GameObject inventoryUI;
    public Button inventoryButton;

    [SerializeField] private GameObject[] items;

    [SerializeField] private Transform slotGroup;
    public Slot[] slots;

    void Start()
    {
        // 자신과 자식 중에서 Slot Component가 있는 대상을 모두 가져오는 기능
        slots = slotGroup.GetComponentsInChildren<Slot>(true); // true -> 비활성화 되어있는 것도 갖고옴

        // 인벤토리 버튼을 눌렀을 때 OnInventory 함수 실행
        inventoryButton.onClick.AddListener(OnInventory);
    }

    public void OnInventory()
    {
        // GameObject.activeSelf = 현재 Active 상태
        inventoryUI.SetActive(!inventoryUI.activeSelf);
    }

    public void GetItem(IItemObject item)
    {
        // 빈 슬롯을 찾아서 AddItem 수행
        foreach (var slot in slots)
        {
            if (slot.isEmpty)
            {
                slot.AddItem(item);
                break; // 브레이크를 해주지 않으면 모든 빈슬롯에 다 넣어버림
            }
        }
    }
}

 

아이템의 개수는 0개 이상이 될 수 있으므로 배열로 아이템을 갖고 오도록 해주었고

슬롯 또한 배열로 선언하여 아이템이 Slot 컴포넌트를 자동으로 찾도록 구현해 주었다.

 

여기서 GetComponentsInChildren<Slot>(true); 로 작성해 주면 비활성화된 슬롯까지 포함해서 가져올 수 있다.

즉, 게임 첫 실행 시 인벤토리 UI가 비활성화 되어있더라도 문제없이 갖고 올 수 있다는 뜻이다.

 

foreach (var slot in slots)
{
    if (slot.isEmpty)
    {
        slot.AddItem(item);
        break; // 브레이크를 해주지 않으면 모든 빈슬롯에 다 넣어버림
    }
}

 

해당 반복문 안에서 인벤토리의 슬롯을 순회하면서 빈 슬롯을 찾아 아이템을 저장한다.

이때 break 문이 없으면 여러 개의 빈 슬롯에 똑같은 아이템이 들어가는 문제가 생기니 꼭 추가해 주자

 

public void OnInventory()
{
    // GameObject.activeSelf = 현재 Active 상태
    inventoryUI.SetActive(!inventoryUI.activeSelf);
}

 

그리고 인벤토리 버튼을 클릭할 때마다 인벤토리를 On/Off 해주기 위하여 UI의 SetActive를  true ↔ false로 반전시켜 주었다.

 

 

 

3. 포탈

이제 Adventure 씬도 얼추 마무리가 되었으니 이 씬에도 포탈을 만들어줘서 Town 씬으로 이동하게 해 주겠다

IEnumerator PortalRoutine()
{
    portalEffect.SetActive(true);

    // Fade 코루틴이 끝날 때 까지 대기
    yield return StartCoroutine(fade.Fade(3f, Color.white, true));

    // 씬 변경
    loadingImage.SetActive(true);
    yield return StartCoroutine(fade.Fade(3f, Color.white, false));

    while (progressBar.fillAmount < 1f) // 로딩 페이크
    {
        progressBar.fillAmount += Time.deltaTime * 0.3f;
        yield return null;
    }

    if (sceneType == SceneType.Town)
        SceneManager.LoadScene(1);
    else
        SceneManager.LoadScene(0);
}

 

포탈 스크립트를 그대로 복사해 와서 아래 씬 전환 하는 부분을 if문으로 선언해 주었다.

(음악이 이중으로 재생되는 문제가 있는데 이 부분은 아직 수정해주지 않았다.)

 

 

4. 인트로 화면

마지막으로 인트로 화면을 만들어주었다.

 

Start 버튼에 On Click 메서드를 활용해서 버튼을 클릭하면 Intro 화면이 비활성화되도록 설정해 주었다.

 

 

2. 2D Rigging

🥕 예행 작업
1. Scene 생성 (2D Rigging)
2. Asset  다운로드
 

2D Character Stick 001 by Memstow | 2D 캐릭터 | Unity Asset Store

Elevate your workflow with the 2D Character Stick 001 by Memstow asset from Memstow. Find this & more 캐릭터 on the Unity Asset Store.

assetstore.unity.com

 

 

 

에셋을 다운로드 받은 후 스프라이트 에디터를 열어준다.

그런 다음 Skinning Editor를 열어준다.

 

 

Create Bone을 통해 오브젝트의 뼈대를 만들 수 있다

 

 

뼈대 작업을 해준 다음에는 오브젝트의 폴리곤이 제대로 적용되었는지 확인해주어야 한다.

Auto Geometry로 폴리곤이 제대로 적용되었는지 확인하고

 

 

제대로 적용되지 않은 부분은 Create Vertex 탭에서 조정해 주면 된다.

 

 

Auto Weights에서 이제 뼈대의 무게(?)를 만들어줄 수 있다.

Associate Bones를 체크하고 Generate를 클릭해 주면 된다.

 

잘 적용되었으면 상단에 Apply를 눌러 저장해 준다.

 

 

이제 스프라이트 이미지를 씬에 배치한 후 IK Manager 컴포넌트를 추가해 준다.

 

 

IK를 잡고 뼈대를 움직여주면 된다!

아무런 작업도 안 하고 일단 IK부터 넣어주었는데 저렇게 앙증맞은 자세를 하고 있어서 웃겼다 ㅋㅋㅋ

 

 


 

오늘 오전 수업은 거의 다 듣질 못했다.. 몸 컨디션이 너무 안 좋아서 엎드려서 좀 자고 있었다.....ㅠ

그래서 인벤토리 UI 구현한 부분은 저렇게 한 게 맞는지 잘 모르겠음ㅎ..ㅎ ... 근데 얼추 강사님이랑 똑같이 시스템이 돌아가는 것 같길래 그냥 그러려니 했다..

 

오후 수업에서는 코드 에러가 전혀 없는데 Log 기록까지 다 뒤져봤는데도 계속 에러가 나타나서 애탔던 적이 있었다.

그런데 다행히도(?) 나만 그런 게 아니라 다른 수강생분들도 나와 똑같은 에러가 나타나고 있었다

알고 보니 강사님이 구현할 때 로직을 잘 못 구현하셨던 거였고 바로 고쳐졌었따..

정말 다른 부분이 없는데 왜이러지 오전에 수업 안 들은 게 문젠가 이러면서 눈물 줄줄 흘릴뻔했다 ㅋㅋㅋㅠ