제작 시스템
제작 시스템은 재료와 재화를 소모하여 아이템을 생성하는 구조이다.
검색 기능을 통해 제작 가능한 아이템을 빠르게 탐색할 수 있다.
전체 플레이 영상
구현 내용
전체 코드
using System.Text;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class CraftSystem : MonoBehaviour
{
[Header("프리팹 및 패널")]
public GameObject itemSlotPrefab; // 제작 아이템 슬롯 프리팹
public Transform contentPanel; // 아이템 슬롯이 배치될 부모 패널
public GameObject craftPanel; // 아이템 제작 패널
[Header("아이템 데이터")]
public List<Item> craftableItems; // 제작 가능한 아이템 목록
public List<Item> materialItems; // 각 아이템 제작에 필요한 재료 목록
[Header("UI 표시 요소")]
public Image itemImage; // 제작 아이템 이미지 표시
public Image materialImage; // 제작 재료 아이템 이미지 표시
public TextMeshProUGUI statTxt; // 제작 아이템 스탯 정보
public TextMeshProUGUI haveItemTxt; // 현재 소지 재료 아이템 수량
public TextMeshProUGUI haveCoinTxt; // 현재 보유 재화 수량
public TextMeshProUGUI needItemTxt; // 필요한 재료 수량
public TextMeshProUGUI needCoinTxt; // 필요한 재화 수량
public GameObject cantCraftMsg; // 재료 부족 시 표시할 메시지
[Header("아이템 검색")]
public TMP_InputField serch_InputField;
ItemManager itemManager;
private int selectedIndex = -1; // 현재 선택된 아이템 인덱스
private void Start()
{
itemManager = GameManager_LDW.instance.itemManager;
if (serch_InputField != null) serch_InputField.onValueChanged.AddListener(OnSearchValueChanged);
CreateItemSlots();
}
private void Update()
{
// 선택된 아이템이 있을 때, 재료/재화 실시간 갱신
if (selectedIndex >= 0 && selectedIndex < materialItems.Count)
{
haveItemTxt.text = materialItems[selectedIndex].count.ToString();
haveCoinTxt.text = itemManager.coin.ToString();
}
}
/// --- 아이템 검색 --- ///
// 한글 완성형 초성 테이블
private static readonly char[] InitialConsonants =
{
'ㄱ','ㄲ','ㄴ','ㄷ','ㄸ','ㄹ','ㅁ','ㅂ','ㅃ',
'ㅅ','ㅆ','ㅇ','ㅈ','ㅉ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'
};
// 아이템 이름 → 초성 문자열 (예: "생명의 반지" → "ㅅㅁㅇㅢㅂㅈ")
private string GetInitialsFromName(string source)
{
if (string.IsNullOrEmpty(source))
return string.Empty;
StringBuilder sb = new StringBuilder();
foreach (char ch in source)
{
// 한글 완성형: AC00 ~ D7A3
if (ch >= 0xAC00 && ch <= 0xD7A3)
{
int unicode = ch - 0xAC00;
int initialIndex = unicode / (21 * 28);
sb.Append(InitialConsonants[initialIndex]);
}
// 나머지(공백, 영어 등)는 그냥 무시
}
return sb.ToString();
}
// 검색어 → 초성 패턴 (예: "생반" → "ㅅㅂ", "ㅅ" → "ㅅ")
private string GetInitialPatternFromFilter(string filter)
{
if (string.IsNullOrEmpty(filter))
return string.Empty;
StringBuilder sb = new StringBuilder();
foreach (char ch in filter)
{
// 완성형 한글이면 초성 뽑기
if (ch >= 0xAC00 && ch <= 0xD7A3)
{
int unicode = ch - 0xAC00;
int initialIndex = unicode / (21 * 28);
sb.Append(InitialConsonants[initialIndex]);
}
// 자모(ㄱ~ㅎ)면 그대로 사용
else if (ch >= 0x3131 && ch <= 0x314E)
{
sb.Append(ch);
}
// 그 외(숫자, 영어 등)는 무시
}
return sb.ToString();
}
/// --- 제작 슬롯 생성 --- ///
public void CreateItemSlots()
{
CreateItemSlots(string.Empty);
}
private void CreateItemSlots(string filter)
{
// 기존 슬롯 전부 삭제
foreach (Transform child in contentPanel)
Destroy(child.gameObject);
string lowerFilter = string.IsNullOrEmpty(filter) ? string.Empty : filter.ToLower();
string initialFilter = GetInitialPatternFromFilter(filter); // 검색어의 초성 패턴
for (int i = 0; i < craftableItems.Count; i++)
{
Item item = craftableItems[i];
Item material = materialItems[i];
// 검색어가 있을 때만 필터링
if (!string.IsNullOrEmpty(lowerFilter))
{
string nameLower = item.itemName.ToLower();
string nameInitials = GetInitialsFromName(item.itemName);
bool nameMatch = nameLower.Contains(lowerFilter); // "반지", "체력" 등 일반 검색
bool initialMatch = false;
// 초성 패턴이 있을 때만 초성 비교
if (!string.IsNullOrEmpty(initialFilter))
{
initialMatch = nameInitials.Contains(initialFilter);
}
if (!nameMatch && !initialMatch)
continue; // 둘 다 아니면 스킵
}
GameObject slot = Instantiate(itemSlotPrefab, contentPanel);
SetSlotInfo(slot, item, material, i);
}
}
private void SetSlotInfo(GameObject slot, Item item, Item material, int index)
{
slot.transform.Find("ItemNameTxt").GetComponent<TextMeshProUGUI>().text = item.itemName;
slot.transform.Find("ItemIcon").GetComponent<Image>().sprite = item.itemImage;
slot.transform.Find("MaterialIcon").GetComponent<Image>().sprite = material.itemImage;
TextMeshProUGUI matAmountTxt = slot.transform.Find("MaterialAmountTxt").GetComponent<TextMeshProUGUI>();
TextMeshProUGUI coinAmountTxt = slot.transform.Find("CoinAmountTxt").GetComponent<TextMeshProUGUI>();
// 아이템별 제작 필요 수량 설정
if (item.itemCode == "200_01_01")
{
matAmountTxt.text = "x 3";
coinAmountTxt.text = "x 100";
}
else
{
matAmountTxt.text = "x 1";
coinAmountTxt.text = "x 50";
}
slot.GetComponent<Button>().onClick.AddListener(() => OnItemClicked(item, index));
}
// 검색 결과에 따라 제작 슬롯 생성
private void OnSearchValueChanged(string searchText)
{
// 양쪽 공백 제거
searchText = searchText.Trim();
// 아무 것도 없으면 전체 다시 생성
if (string.IsNullOrEmpty(searchText))
{
CreateItemSlots();
}
else
{
CreateItemSlots(searchText);
}
// 검색할 때는 선택 초기화 / 제작 패널 닫기
selectedIndex = -1;
craftPanel.SetActive(false);
}
/// --- 아이템 클릭 시 제작창 표시 --- ///
private void OnItemClicked(Item item, int index)
{
selectedIndex = index;
craftPanel.SetActive(true);
itemImage.sprite = item.itemImage;
materialImage.sprite = materialItems[index].itemImage;
// 스탯 표시
statTxt.text = string.Join("\n", item.stats);
// 현재 재료 및 재화 표시
haveItemTxt.text = materialItems[index].count.ToString();
haveCoinTxt.text = itemManager.coin.ToString();
// 필요 수량 계산
bool isHpPotion = item.itemCode == "200_01_01";
needItemTxt.text = isHpPotion ? "3" : "1";
needCoinTxt.text = isHpPotion ? "100" : "50";
// 버튼 이벤트 설정
Button craftBtn = craftPanel.transform.Find("CraftBtn").GetComponent<Button>();
craftBtn.onClick.RemoveAllListeners();
craftBtn.onClick.AddListener(() => TryCraft(item, index));
}
/// --- 제작 시도 및 검증 --- ///
private void TryCraft(Item item, int index)
{
int haveItems = int.Parse(haveItemTxt.text);
int needItems = int.Parse(needItemTxt.text);
int haveCoins = int.Parse(haveCoinTxt.text);
int needCoins = int.Parse(needCoinTxt.text);
if (haveItems >= needItems && haveCoins >= needCoins)
CraftItem(item, index, needItems, needCoins);
else
StartCoroutine(ShowTemporaryMsg(cantCraftMsg, 0.5f));
}
/// --- 제작 처리 --- ///
private void CraftItem(Item item, int index, int requiredItem, int requiredCoin)
{
materialItems[index].count -= requiredItem;
itemManager.coin -= requiredCoin;
itemManager.AddItem(item.itemCode, 1);
UpdateUIAfterCraft(index);
}
private void UpdateUIAfterCraft(int index)
{
haveItemTxt.text = materialItems[index].count.ToString();
haveCoinTxt.text = itemManager.coin.ToString();
}
private IEnumerator ShowTemporaryMsg(GameObject obj, float duration)
{
obj.SetActive(true);
yield return new WaitForSeconds(duration);
obj.SetActive(false);
}
}전체 흐름도

