[Unity] Item Crafting

Lingtea_luv·2025년 6월 18일
0

Unity

목록 보기
27/30
post-thumbnail

Crafting


언젠가는 꼭 구현해보고 싶던 기능인 아이템 조합 기능이다. 재료 아이템과, 레시피를 Scriptable Object로 만들고 이를 UI와 연동하여 구현하고자 했다.

Scriptable Object

우스갯소리지만 Scriptable Object는 무적이라는 말이 있다. 그만큼 쓰임이 많다는 것을 의미하는데, 이를 아이템 조합 레시피와 재료 아이템을 구현할 때 사용했다.

Scriptable Object를 만드는 방법은 간단하다. 일단 ScriptableObject 을 상속 받고, 위에 attribute로 [CreateAssetMenu(fileName = "NewStuff", menuName = "Stuff/StuffData")] 를 작성하면 끝이다.

attribute는 Unity 상단 Menu에 해당 Scriptable Object를 생성할 수 있도록 경로를 만들어주는 역할을 한다.

Stuff

[CreateAssetMenu(fileName = "NewStuff", menuName = "Stuff/StuffData")]
public class Stuff : ScriptableObject
{
	// 구분을 위한 이름
    public string _name;
    
    // UI와 연동되는 Icon Sprite
    public Sprite Icon;
    
    // 필드에 생성될 때 사용되는 프리팹
    public GameObject Prefab;
    
    // 미니맵에 생성될 때 사용되는 프리팹
    public GameObject MinimapPrefab;
}

Recipe

[CreateAssetMenu(fileName = "NewRecipe", menuName = "Crafting/Recipe")]
public class Recipe : ScriptableObject
{
	// 레시피를 인스펙터 상에서 만들 수 있도록 내부 구현
    [System.Serializable]
    public class Ingredient
    {
        public Stuff Stuff;
        public int Amount;
    }

	// 레시피에 필요한 재료 목록
    public List<Ingredient> Ingredients;
    
    // 조합 결과 아이템 -> player status와 연동
    public Item Result;
    
    // UI와 연동되는 조합 결과 아이템의 Icon Sprite
    public Sprite Icon;
}

Scripts

사용한 script는 2개로 하나는 제작 기능 전반을 담당하는 Crafting, 다른 하나는 제작 결과를 담당하는 CraftResult이다.

Crafting

public class Crafting : MonoBehaviour
{
	// 존재하는 모든 레시피 등록
    [Header("Drag&Drop")] 
    [SerializeField] private List<Recipe> _recipes;
    
    // 플레이어가 건넨 재료를 저장하는 List
    public List<Stuff> CraftStuff { get; private set; }

    private void Awake()
    {
        Init();
    }
    
    // 외부 참조는 Start에서 처리
    private void Start()
    {
    	// UI와 연동하는 작업
        UIBinder.Instance.Mediate(this);
    }
    
    // 제작 가능한 레시피 확인하는 메서드
    public Recipe CanCraft()
    {
    	// 모든 레시피에 대해
        foreach (var recipe in _recipes)
        {
            int count = 0;
            // 올바른 재료에 해당하는지 검사하여
            foreach (var ingredient in recipe.Ingredients)
            {
            	// 올바른 재료에 해당하는 경우 count 증가
                if (CraftStuff.Contains(ingredient.Stuff))
                {
                    count++;
                }
            }
            // 올바른 재료가 2개인 경우 해당 레시피 반환
            if (count == 2)
            {
                return recipe;
            }
        }
        // 해당하는 레시피가 없는 경우 null 반환
        return null;
    }
    
    // 제작 메서드 : 재료를 없애고 레시피 결과 반환
    public Item Craft(Recipe recipe)
    {
        foreach (var ingredient in recipe.Ingredients)
        {
            CraftStuff.Remove(ingredient.Stuff);
        }
        return recipe.Result;
    }
	
    // UI Button에 호출되는 메서드
    // Stuff를 클릭하면 인벤토리 <-> NPC 이동하는데 이때 해당 Stuff를 확인
    public Stuff FindObject(int index)
    {
        if (index >= CraftStuff.Count) return null;
        return CraftStuff[index];
    }
    
    // 플레이어가 UI창을 닫을 때 건넨 재료를 돌려주는 메서드
    public void RemoveStuff(Stuff stuff)
    {
        CraftStuff.Remove(stuff);
    }
    
    private void Init()
    {
        CraftStuff = new List<Stuff>();
    }
}

CraftResult

public class CraftResult : MonoBehaviour
{
    [Header("Drag&Drop")] 
    // 재료 조합 결과를 보여주는 이미지 칸
    [SerializeField] private Image _resultImage;
    
    // 이미지 칸에 붙어있는 버튼
    [SerializeField] private Button _resultBtn;
    
    // 재료 조합 결과가 없는 경우, 재료가 없는 경우 표시되는 Sprite
    [SerializeField] private Sprite _basicSprite;

	// 조합 결과 출력(CanCraft) 기능을 가지고 있는 Crafting Class
    // 조합 결과를 실시간으로 표시하기 위해 필요
    private Crafting _crafting;
    
    // 조합 결과 레시피
    private Recipe _resultRecipe;

	// 이벤트 구독 : 이벤트에 외부 참조가 있어 Start에서 처리
    private void Start()
    {
        _resultBtn.onClick.AddListener(()=>OnResultBtnClicked());
    }
    
    // 조합 결과를 표시하는 이미지는 실시간 Update에서 처리
    private void Update()
    {
        _resultRecipe = _crafting.CanCraft();
        if (_resultRecipe != null)
        {
            _resultImage.sprite = _resultRecipe.Icon;
        }
        else
        {
            _resultImage.sprite = _basicSprite;
        }
    }

	// 조합 결과를 클릭했을 때 호출되는 메서드
    // 제작 아이템을 실제로 조합하는 메서드 호출
    private void OnResultBtnClicked()
    {
        if (_resultRecipe != null)
        {
        	// 인벤토리에 제작 아이템을 넣어주는 메서드
            // 사실상 _crafting.Craft(_resultRecipe)도 호출하는 것이므로
            // 사용된 재료 삭제 + 아이템 인벤토리에 추가
            TransferManager.Instance.GetItem(_crafting.Craft(_resultRecipe));
        }
    }
    
    public void SetProperty(Crafting crafting)
    {
        _crafting = crafting;
    }
}
profile
뚠뚠뚠뚠

0개의 댓글