Unity ScriptableObject로 아이템(목숨, 코인 만들기)

농담고미고미·2024년 8월 17일

Unity 개발 일지

목록 보기
20/26
  • 우선 ScriptableObject란? (유니티 공식 문서 참조)

    ScriptableObject는 클래스 인스턴스와는 별도로 대량의 데이터를 저장하는 데 사용할 수 있는 데이터 컨테이너입니다.
    ScriptableObject의 주요 사용 사례 중 하나는 값의 사본이 생성되는 것을 방지하여 프로젝트의 메모리 사용을 줄이는 것입니다. 이는 연결된 MonoBehaviour 스크립트에 변경되지 않는 데이터를 저장하는 프리팹이 있는 프로젝트의 경우 유용합니다.

    이러한 프리팹을 인스턴스화할 때마다 해당 데이터의 자체 사본이 생성됩니다. 이러한 방법을 사용하여 중복 데이터를 저장하는 대신 ScriptableObject를 이용하여 데이터를 저장한 후 모든 프리팹의 레퍼런스를 통해 액세스할 수 있습니다. 즉, 메모리에 데이터 사본을 하나만 저장합니다.

    MonoBehaviour와 마찬가지로 ScriptableObject는 기본 Unity 오브젝트에서 파생되나, MonoBehaviour와는 달리 게임 오브젝트에 ScriptableObject를 연결할 수 없으며 대신 프로젝트의 에셋으로 저장해야 합니다.

    에디터 사용 시, ScriptableObject에 데이터를 저장하는 작업은 편집할 때나 런타임에 가능합니다. 이는 ScriptableObject가 에디터 네임스페이스와 에디터 스크립팅을 사용하기 때문입니다. 배포된 빌드에서는 ScriptableObject를 사용하여 데이터를 저장할 수 없으나, 개발 시 설정한 ScriptableObject 에셋의 저장된 데이터를 사용할 수 있습니다.

    에디터 툴에서 에셋 형태로 ScriptableObject에 저장한 데이터는 디스크에 작성되므로 세션 간에도 그대로 유지됩니다.

중요한 건 메모리에 데이터 사본을 하나만 저장한다는 것!!

그리고 “유니티 2D 게임 개발” 책을 보고 클론코딩 하는 내용입니다~.

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

[CreateAssetMenu(menuName = "Item")]

public class Item : ScriptableObject
{
    public string objectName;
    public Sprite sprite;
    public int quantity;
    public bool stackable;

    public enum ItemType
    {
        COIN,
        HEALTH
    }
    public ItemType itemType;
}

코드를 봅시다. [CreateAssetMenu(menuName = "Item")] 를 하면 유니티의 에셋메뉴에서 Item을 만들 수 있다. Item은 메모리에 데이터 사본 하나만 있으면 되므로 ScriptableObject의 좋은 예시다. 열겨형을 이용해 ItemType 을 적는다. 나는 Coin, Health만 만들거임. ItemType은 itemType이라 부를거다.

참고로 stackable을 동전과 같은 아이템들을 위한 속성이다.

이제 유니티로 돌아가서, 마우스 우클릭을 하고 아이템을 눌러서 Heart, Coin을 만든다.


sprite에 하트 이미지 넣어주고, 수량도 정해준다. 목숨은 동전 같은게 아니므로 Stackable은 체크 안한다. ItemType은 Health로 지정.

아이템들은 먹으면 사라져야한다. 그런 것들에는 Consumable cs를 붙여줄거다.

그리고 ScriptableObject는 MonoBehaviour와는 달리 게임 오브젝트에 ScriptableObject를 연결할 수 없으며 대신 프로젝트의 에셋으로 저장해야 하므로 Consumable cs을 통해 item을 게임오브젝트에 연결해야한다.

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

public class Consumable : MonoBehaviour
{
    public Item item;
    /* Consumable 스크립트를 게임 오브젝트에 추가할 때 item 속성에 Item 을 대입하려고 한다. 
     * 이렇게 하면 Consumable 안에 스크립팅 가능한 오브젝트 에셋의 참조를 저장할 수 있다.
     * public으로 선언했으므로 다른 스크립트에서도 사용가능. */
}

하트의 인스펙터 창에 Consumable을 추가하자. Item은 Heart를 드롭다운한다.

동전도 마찬가지로 consumable을 추가하고, Coin을 드롭다운한다.

HeartObject와 CoinObject에 둘 다 “CanBePickedUp” 태그를 추가해준다. Player와 닿이면 사라지도록 해서, 아이템을 획득한 효과를 줄거다.

그럼 이제 목숨과 동전을 Prefab에 추가하고, 게임 내에 내가 원하는대로 목숨과 동전을 깔아준다.

하지만 아직 동전과 하트를 먹는다 해서 변화가 생기진 않는다. 이제부터 Player cs를 수정하자. coin이랑 하트를 동시에 만들어서 순서가 좀 섞여있다. 기억을 더듬으면서 글을 적는 중이라 ㅋㅋㅋ

Coin 아이템

Player cs

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("CanBePickedUp"))
        {
            Item hitObject = collision.gameObject.GetComponent<Consumable>().item;

            if (hitObject != null)
            {
                bool shouldDisappear = false;

                switch (hitObject.itemType)
                {
                    case Item.ItemType.COIN:

                        shouldDisappear = inventory.AddItem(hitObject);
                        break;

                    case Item.ItemType.HEALTH:
                        shouldDisappear = AdjustHitPoints(hitObject.quantity);
                        break;
                    default:
                        break;
                }

                if (shouldDisappear)
                {
                    collision.gameObject.SetActive(false);
                }
            }
        }
    }

코드랑 구조가 너무 깔끔해서 마음이 편안하다…

if(collision.gameObject.CompareTag("CanBePickedUp")) : 만약 부딪힌 게임 오브젝트가 CanBePickedUp 태그를 단 게임오브젝트라면 아래의 내용을 실행한다는 뜻.

Item hitObject = collision.gameObject.GetComponent<Consumable>().item; : hitObject는 부딪힌 게임오브젝트의 속성 중 하나인 Consumable 스크립트에서 item을 참조로 얻는다.

Switch문을 이용해 ItemType에 따라 조금씩 다르게 반응한다. 지금 인벤토리까지 만든 이후 이 글을 적는거라, 갑자기 인벤토리가 튀어나온 건 무시하면 된다 . ㅎㅎ 중요한 건 Switch문으로 컨트롤한다는 거.

그리고 ShouldDisappear가 true면 SetActive(false)를 한다. 참고로 Distroy하는 것보다 SetActive를 쓰는게 더 좋다!! Distroy는 사용할 때마다 메모리에 접근해야 하기 때문에 비효율적이다.

profile
농담곰을 좋아해요 말랑곰탱이

0개의 댓글