제작대에서 아이템 제작을 위해선 자원, 재료, 도구의 설정과 이미지가 필요로 합니다. 아래는 임시로 만든 리스트 입니다.
| 영문이름 | 한글이름 | 설명 |
|---|---|---|
| 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를 써봅시다.
Window->Package Manager+버튼 클릭 후 Add Package from git URLhttps://github.com/dbrizov/NaughtyAttributes.git#upm 입력후 Add다시 스크립트를 열게 되면 해당 패키지를 쓸 수가 있습니다.
// 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으로 변경합시다.
// 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);
}
}
}
// 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;
}
}
추후 Scanner을 통해 물건을 스캔하면 unlockedRecipes에 추가되게끔 할 예정입니다.
// PlayerState.cs
public ItemData[] unlockedRecipes; // 플레이어가 잠금 해제한 레시피 목록
Player의 PlayerState에서 unlockedRecipes를 임의로 여러개 등록합시다.
제작대의 해당 아이템이 정상적으로 뜨는 것을 확인할 수 있습니다.
이제 정말로 아이템을 제작할 수 있게 해야합니다. 클릭 이벤트로 ItemData가 담긴 것을 확인했으니, ItemData의 Ingredients 정보값을 들고와 제작하게 할 겁니다.
제작은 재료가 있어야만 가능하게 해야합니다. using System.Linq를 통해 알아봅니다.
System.Linq: foreach를 통해 특정 Key안에 해당 Key를 가지고 있는 요소가 몇개인지 알려줍니다. 만약 [A, B, B, C] 라면 Key는 A,B,C로 Count는 1, 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;
}
}
// 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);
}
제작대의 아이템을 클릭하면 그 아이템 재료는 인벤토리에서 삭제되고, 제작된 아이템은 인벤토리에 들어오게 됩니다.
다음 글에서는: