[Unity / C#] 제작대 시스템

주예성·2025년 6월 20일

📋 목차

  1. 아이템 리스트 작성
  2. 중간 결과
  3. 최종 결과
  4. 오늘의 배운 점
  5. 다음 계획

🖼️ 아이템 리스트 작성

제작대에서 아이템 제작을 위해선 자원, 재료, 도구의 설정과 이미지가 필요로 합니다. 아래는 임시로 만든 리스트 입니다.

자원

영문이름한글이름설명
Alloy합금두 가지 이상의 금속이 합쳐진 물질로 강화합금주석 제작에 사용
BasicOre기초 광석기초 도구나 제작대 재료로 대량으로 채굴 가능
BioEgg바이오 에그외계 생물의 알로, 연구 또는 생명체 복구 연구에 사용
BioFiberLoop생체 섬유 루프외계 식물 줄기에서 채집한 유연한 구조체로 방수 복이나 공예품 제작에 활용
CyanCrystal청록 결정고에너지 전도체로 장비 업그레이드나 추진 코어에 사용됨
EmeraldGeode에메랄드 지오드바위 내부에서 자라나는 결정으로 탐사로 발견 가능, 외계 장치 핵심 재료
Fish생선바다에 사는 물고기로 식량으로 사용 가능
HighEnergyOre고에너지 광석에너지 저장성이 높아 연료 재료로 사용 가능
OrangeCrystal주황 결정희귀 에너지 저장 결정체로 고급 배터리나 외계 장치에 필요
PurpleCrystal보라 결정정신 안정 및 생체 강화에 쓰이는 결정으로 의료 모듈 등에 사용
Slate석판무기나 도구로 직접 가공할 수 있는 원초적 자원
UnidentifiedCylinder미확인 실린더외계 기술이 내장된 정체불명의 부품으로 특정 제작 설계도에만 사용 가능
VolcanicRocks화산암내부에 열 에너지가 남아 있는 희귀 광물로 열 전환 발전기 등에 사용

재료

영문이름한글이름설명필요재료
Battery배터리손전등, 건축기기, 스캐너 전력에 쓰임주황 결정(1) + 합금(1)
CircuitBoard회로보드전자기기를 제작할때 사용강화합금주석(1) + 에메랄드 지오드(1) + 화산암(1)
ReinforcedAlloyIngot강화합금주석무기나 여러가지의 기계 재료합금(2)
ReinforcedFiber강화섬유수트 재료생체 섬유 루프(2)

도구

영문이름한글이름설명필요재료
BuildingMachine건축기기건축하는 기계회로보드(1) + 배터리(1) + 석판(1)
BioSuit생체수트업그레이드 할 때마다 방어력 증가강화섬유(5) + 바이오 에그(2) + 보라 결정(2)
EnergyBar에너지바포만감을 채움필요재료 누락
Flashlight손전등앞을 밝힘회로보드(1) + 배터리(1) + 석판(1)
FuelTank연료통제트팩에 쓰임고에너지 광석(1) + 석판(4)
HealKit응급상자체력을 채움보라 결정(1) + 강화섬유(2)
Jetpack제트팩일정시간 날 수 있는 이동수단강화합금주석(2) + 회로보드(1) + 연료통(2)
Knife나이프전투할 때 쓰임기초광석(1) + 석판(1)
Scanner스캐너생물이나 물건을 스캔하고 재료를 파악함회로보드(1) + 배터리(1) + 석판(2)
Water수분을 채음생선(1)

✨ 조건에 맞는 헤더만 노출 (NaughtyAttributes)

아이템은 여러가지 타입으로 나뉘어져 있습니다. 해당 아이템의 설정에서 불필요한 데이터도 있을 겁니다. 그래서 해당하는 타입의 헤더만 노출되도록 NaughtyAttributes를 써봅시다.

1. 다운받기

  1. UnityEditor에서 Window->Package Manager
  2. 왼쪽 상단의 +버튼 클릭 후 Add Package from git URL
  3. URL란에 https://github.com/dbrizov/NaughtyAttributes.git#upm 입력후 Add

2. 사용하기

다시 스크립트를 열게 되면 해당 패키지를 쓸 수가 있습니다.

// ItemData.cs

// 다운받은 패키지
using NaughtyAttributes;

public class ItemData : scriptableObject
{
	[Header("Item Information")]
    public string itemName;	// 한글이름
    public string description; // 설명
    public Sprite icon; // 이미지
    public GameObject itemPrefab; // 프리팹 오브젝트
    
    [Header("ingredient list")]
    public ItemData[] ingredients; // 재료정보

    [ShowIf("itemType", ItemType.BioState)]
    [Header("BioState Properties")]
    public BioStateType bioStateType; // 캐릭터 생명(체력, 포만감, 수분)타입
    [ShowIf("itemType", ItemType.BioState)] 
    public float amount; // 생명정보 증감 수치

    [ShowIf("itemType", ItemType.Product)]
    [Header("Product Properties")]
    public ProductType productType; // 제작아이템 타입

    [ShowIf("productType", EquipmentType.BatteryUsing)]
    public float batteryUsageRate; // 배터리 소모율

	[ShowIf("productType", EquipmentType.FuelUsing)]
	public float fuelUsageRate; // 연료 소모율
    
    [ShowIf("itemType", ItemType.Weapon)]
    [Header("Weapon Properties")]
    public float attackPower; // 무기 데미지
    [ShowIf("itemType", ItemType.Weapon)]
    public float attackSpeed; // 무기 공속

    [ShowIf("itemType", ItemType.Armor)]
    [Header("Armor Properties")]
    public float defensePower; // 방어구 체력 감소율
    [ShowIf("itemType", ItemType.Armor)]
    public float durability; // 내구도
    [ShowIf("itemType", ItemType.Armor)]
    public float maxDurability; // 최대 내구도
}

public enum ItemType
{
    BioState,   // 구급키트, 식량, 물
    Resources, // 재료가 없는 순수 자원
    Product,  // 재료가 필요한 아이템
    Weapon,     // 무기
    Armor       // 수트
}

public enum BioStateType
{
    Healing, // 구급상자
    Satiety, // 포만감
    Hydration // 수분
}   

public enum ProductType
{
    None, // 없음
    BatteryUsing, // 배터리 소모
    FuelUsing // 연료 소모
}

🎉 제작대에 제작 아이템 동적 할당

제작대는 인벤토리와 달리 제작 가능한 아이템만 표시합니다. 그래서 스크립트에서 가능한 아이템의 수만큼 동적 할당해야만 합니다.
제작대를 담당할 BenchSlot, Bench 스크립트를 제작하고, Inventory에서 쓰인 UI_Slot을 복제하여 Bench_Slot으로 변경합시다.

1. BenchSlot에서 클릭 이벤트 생성

// BenchSlot.cs

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;


public class BenchSlot : MonoBehaviour, IPointerClickHandler
{
    [Header("UI Elements")]
    [SerializeField] private Image itemIcon;

    private GameObject UIManager;
    private UIManager uiManager;
    public int slotIndex;

    private void Start()
    {
        UIManager = GameObject.Find("UIManager");
        if (UIManager)
        {
            uiManager = UIManager.GetComponent<UIManager>();
        }
    }

    public void SetSlotIndex(int index)
    {
        slotIndex = index;
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        if (eventData.clickCount == 1)
        {
            uiManager.OnBenchSlotClick(slotIndex);
        }
    }
}

2. UIManager에서 제작대 초기화 및 클릭 함수 정의

// UIManager.cs

[Header("Slot")]
public GameObject benchSlotPrefab;

[Header("UI Panels")]
public GameObject benchPanel;
public GameObject benchSlotPanel;

void Start()
{
	InitializeBenchUI();
}

public void OnBenchSlotClick(int slotIndex)
{
    Debug.Log(playerState.unlockedRecipes[slotIndex].itemName + " 제작법을 클릭했습니다!");
}

// 제작대 초기화
public void InitializeBenchUI()
{
    if (!benchPanel || !benchSlotPanel) return;
    for (int i = 0; i < playerState.unlockedRecipes.Length; i++)
    {
        GameObject slot = Instantiate(benchSlotPrefab, benchSlotPanel.transform);

        BenchSlot slotData = slot.GetComponent<BenchSlot>();
        slotData.SetSlotIndex(i); // 아이템 정보를 인덱스로 얻어오기 위함

        Image itemImage = slot.transform.Find("ItemIcon").GetComponent<Image>();
        itemImage.sprite = playerState.unlockedRecipes[i].icon;
        itemImage.color = Color.white;
    }
}

3. 얻은 레시피 정보를 PlayerState에 저장

추후 Scanner을 통해 물건을 스캔하면 unlockedRecipes에 추가되게끔 할 예정입니다.

// PlayerState.cs

public ItemData[] unlockedRecipes; // 플레이어가 잠금 해제한 레시피 목록

4. 스크립트 할당

  1. BenchSlotBench_Slot 프리팹 컴포넌트
  2. BenchBench 프리팹 컴포넌트

5. 임의로 초기 정보값을 저장

PlayerPlayerState에서 unlockedRecipes를 임의로 여러개 등록합시다.


🎮 중간 결과

제작대의 해당 아이템이 정상적으로 뜨는 것을 확인할 수 있습니다.


아이템 제작

이제 정말로 아이템을 제작할 수 있게 해야합니다. 클릭 이벤트로 ItemData가 담긴 것을 확인했으니, ItemData의 Ingredients 정보값을 들고와 제작하게 할 겁니다.

1. 인벤토리 아이템 확인

제작은 재료가 있어야만 가능하게 해야합니다. using System.Linq를 통해 알아봅니다.
System.Linq: foreach를 통해 특정 Key안에 해당 Key를 가지고 있는 요소가 몇개인지 알려줍니다. 만약 [A, B, B, C] 라면 KeyA,B,CCount1, 2, 1이 됩니다.

// Bench.cs

using System.Linq;
using UnityEngine;

public class Bench : MonoBehaviour
{
    [SerializeField] private GameObject slotPrefab;

    private ItemData[] items;
    private GameObject player;

    void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player");
    }

	// 재료가 인벤토리에 있는지 확인
    public bool CanCraft(PlayerState playerState, ItemData craftData)
    {
        ItemData[] invenDatas = playerState.inventoryItems;
        ItemData[] requiredItems = craftData.ingredients;

        // 필요한 아이템(재료)들을 그룹화
        var requiredGroups = requiredItems.GroupBy(item => item.itemName);

        foreach (var group in requiredGroups)
        {
            string itemName = group.Key;
            int requiredCount = group.Count();
            
            // 인벤토리 안에 해당 아이템이 몇 개인지 검사
            int invenCount = GetItemCount(invenDatas, itemName);
            
			// 만약 인벤토리 안의 재료 아이템이 필요한 아이템 수보다 적다면 'false'로 리턴
            if (invenCount < requiredCount)
                return false;
        }
		
        return true;
    }

    public int GetItemCount(ItemData[] datas, string name)
    {
        int count = 0;
        for (int i = 0; i < datas.Length; i++)
        {
            if (!datas[i]) return count;
            if (datas[i].itemName == name)
            {
                count++;
            }
        }
        return count;
    }
}

2. 쓴 아이템 제거 및 제작 아이템 생성

// UIManager.cs

public void OnBenchSlotClick(int slotIndex)
{
    Debug.Log(playerState.unlockedRecipes[slotIndex].itemName + " 제작법을 클릭했습니다!");
bool isCan = bench.GetComponent<Bench>().CanCraft(playerState, playerState.unlockedRecipes[slotIndex]);
if (isCan)
{
    bench.GetComponent<Bench>().CraftItem(player, playerState, playerState.unlockedRecipes[slotIndex]);
    Debug.Log("제작을 완료했습니다!");
    
    // 만들어졌을 경우 인벤토리 UI 업데이트
    UpdateItemUI();
}
else
{
    Debug.Log("제작에 필요한 재료가 부족합니다!");
}
// Bench.cs

public void CraftItem(GameObject player, PlayerState playerState, ItemData craftData)
{
    Inventory inventory = player.GetComponent<Inventory>();
    if (!inventory) return;

	// 이름을 비교하여 해당하는 아이템 순차적으로 삭제
    foreach (var ingredient in craftData.ingredients)
    {
        inventory.RemoveItemFromName(ingredient.itemName);
    };
    
    // 만들어진 아이템을 인벤토리에 추가
    inventory.AddItem(craftData);
}

🎮 최종 결과

제작대의 아이템을 클릭하면 그 아이템 재료는 인벤토리에서 삭제되고, 제작된 아이템은 인벤토리에 들어오게 됩니다.


📚 오늘의 배운 점

  • NaughtyAttributes 활용
  • 아이템 동적 할당
  • System.Linq 활용

🎯 다음 계획

다음 글에서는:

  1. 마우스 커서를 올릴시 제작할 아이템의 재료 시각화
  2. Scanner로 아이템을 스캔하여 unlockedRecipes에 할당
profile
Unreal Engine & Unity 게임 개발자

0개의 댓글