배터리를 넣거나 바꾸려면 UI가 필요합니다. HUD_Panel에 BatterySlots로 칸을 관리해줍니다.
// UIManager.cs
public GameObject batterySlotPrefab;
public GameObject batterySlotPanel;
// 현재 인벤토리에 있는 배터리 아이템을 보여줌
public void ShowBatterySlots(ItemInstance[] batterys, ItemInstance currentBattery)
{
batterySlotPanel.SetActive(true);
for (int i = 0; i < batterys.Length; i++)
{
GameObject slot = Instantiate(batterySlotPrefab, batterySlotPanel.transform);
Image itemImage = slot.transform.Find("ItemIcon").GetComponent<Image>();
TextMeshProUGUI batteryRate = slot.transform.Find("BatteryRate").GetComponent<TextMeshProUGUI>();
// 배터리가 없다면 None이라는 빈칸을 띄움
if (!batterys[i])
{
itemImage.color = Color.clear;
batteryRate.text = "None";
continue;
}
itemImage.sprite = batterys[i].itemData.icon;
int displayBattery = Mathf.Max(0, (int)batterys[i].Get<float>("batteryUsageRate"));
// 현재 남은 배터리량 표시
batteryRate.text = displayBattery.ToString() + "%";
// 현재 들어있는 배터리는 불투명하게 표시
if (currentBattery == batterys[i])
{
itemImage.color = Color.white;
continue;
}
// 현재 선택되어 있지 않은 배터리들은 반투명하게 표시
itemImage.color = new Color (1, 1, 1, 0.5f);
}
}
// 배터리 교체 창 닫기 전 초기화
public void ClearBatterySlots()
{
for (int i = batterySlotPanel.transform.childCount - 1; i >= 0; i--)
{
Destroy(batterySlotPanel.transform.GetChild(i).gameObject);
}
}
public void SwitchBatterySlot(int index)
{
for (int i = 0; i < batterySlotPanel.transform.childCount; i++)
{
GameObject slot = batterySlotPanel.transform.GetChild(i).gameObject;
Image itemImage = slot.transform.Find("ItemIcon").GetComponent<Image>();
// 현재 선택되어 있는 배터리
if (index == i)
{
itemImage.color = Color.white;
}
// 그 외
else
{
itemImage.color = new Color(1, 1, 1, 0.5f);
}
}
}
배터리를 전담할 스크립트로 BatteryManager 스크립트를 생성합시다.
// BatteryManager.cs
public class BatteryManager : MonoBehaviour
{
// 현재 들고 있는 아이템 (스캐너 등)
private ItemInstance thisItem;
// 아이템에 들어있는 배터리
private ItemInstance currentBattery;
// 현재 선택하고 있는 배터리 선택 창 인덱스
private int currentIndex;
private UIManager uiManager;
private Inventory inventory;
private void Start()
{
thisItem = GetComponent<ItemInstance>();
currentBattery = thisItem.Get<ItemInstance>("Battery");
uiManager = GameObject.Find("UIManager").GetComponent<UIManager>();
inventory = GameObject.Find("Player").GetComponent<Inventory>();
}
public void SetBatterySlots()
{
currentBattery = thisItem.Get<ItemInstance>("Battery");
uiManager.ShowBatterySlots(BatteryArray(inventory.items), currentBattery);
uiManager.SwitchBatterySlot(currentIndex);
}
// 현재 보여줄 배터리 창의 인스턴스 배열
public ItemInstance[] BatteryArray(ItemInstance[] items)
{
List<ItemInstance> list = new List<ItemInstance>();
// 만약 현재 장착하고 있는 배터리가 있다면, 현재 배터리도 표시하기 위해 list에 추가함
// 왜냐하면 장착한 배터리는 인벤토리에서 삭제할 것이기 때문
if (currentBattery) list.Add(currentBattery);
// 없다면 현재 배터리는 null상태임을 표시하기 위해 null을 추가함
else list.Add(null);
// 인벤토리에서 순차적으로 배터리만 꺼내 list에 넣음
for (int i = 0; i < items.Length; i++)
{
if (!items[i]) continue;
if (items[i].itemData.productType == ProductType.Battery)
{
list.Add(items[i]);
}
}
// 배터리를 아예 빼내는 선택창도 필요하기 때문에 현재 배터리가 있다면 마지막에 null도 추가함
if (currentBattery) list.Add(null);
return list.ToArray();
}
// 현재 배터리를 설정함
public void SetCurrentBattery(ItemInstance[] items)
{
// 설정 전 본래 배터리를 저장
ItemInstance defaultBattery = currentBattery;
currentBattery = BatteryArray(items)[currentIndex];
// 현재 장착된 배터리를 선택한 배터리로 변경
thisItem.Set<ItemInstance>("Battery", currentBattery);
// 장착한 배터리를 인벤토리에서 삭제
inventory.RemoveItemFromInstance(currentBattery);
if (defaultBattery)
{
// 새로 장착한 배터리라면
if (defaultBattery != currentBattery)
{
// 본래 배터리는 인벤토리에 추가하고 UI 업데이트
inventory.AddItem(defaultBattery);
uiManager.UpdateItemUI();
}
}
currentIndex = 0;
}
// 마우스의 왼클릭 우클릭으로 배터리를 선택함
public void SetCurrentBatteryIndex(string button)
{
switch(button)
{
case "left":
if (currentIndex > 0) currentIndex--;
break;
case "right":
if (currentIndex < BatteryArray(inventory.items).Length - 1) currentIndex++;
break;
default:
break;
}
uiManager.SwitchBatterySlot(currentIndex);
}
}
현재까지 만든 기능에는 Scanner가 배터리를 사용하고 있습니다. 그곳에 배터리가 있으며, 0% 가 초과할 때만 사용할 수 있게 조건을 추가합니다.
// UseItem.cs
private bool batteryUIOpen = false;
private void UseScannerInUpdate()
{
if (!playerItemHandler.currentItem)
{
scanTime = 0f;
isScan = false;
uiManager.scanBar.SetActive(false);
return;
}
ReloadBattery(playerItemHandler.currentItem);
bool isCan = false;
if (isScan && Input.GetMouseButton(1) && !batteryUIOpen)
{
// 현재 장착한 배터리가 있거나, 쓸 수 있는지 판별
isCan = CanUseBatteryProduct();
if (isCan)
{
scanTime += Time.deltaTime;
if (scanTime >= 2f)
{
isScan = false;
scanTime = 0f;
playerState.AddUnlockedItems(scanItem.itemData);
uiManager.scanBar.SetActive(false);
}
else
{
if (uiManager)
{
uiManager.UpdateScanUI(scanTime);
}
}
}
}
if ((isScan && Input.GetMouseButtonUp(1) && scanTime > 0f) || !interactionDetector.GetCurrentTarget() ||
playerItemHandler.currentItem.itemData.itemName != "스캐너" || !isCan)
{
scanTime = 0f;
isScan = false;
uiManager.scanBar.SetActive(false);
}
}
// 배터리 사용 아이템을 들고 있는 채로 R키를 누른다면 배터리 선택창이 뜸
private void ReloadBattery(ItemInstance item)
{
if (!inventory) return;
BatteryManager batteryManager = item.GetComponent<BatteryManager>();
if (Input.GetKeyDown(KeyCode.R) && batteryManager)
{
if (!batteryUIOpen)
{
batteryManager.SetBatterySlots();
batteryUIOpen = true;
}
else
{
batteryManager.SetCurrentBattery(inventory.items);
uiManager.ClearBatterySlots();
uiManager.batterySlotPanel.SetActive(false);
batteryUIOpen = false;
}
}
// 열려있는 채로 마우스를 누르면 배터리를 선택할 수 있음
if (batteryUIOpen)
{
if (Input.GetMouseButtonDown(0))
{
batteryManager.SetCurrentBatteryIndex("left");
}
else if (Input.GetMouseButtonDown(1))
{
batteryManager.SetCurrentBatteryIndex("right");
}
}
}
private bool CanUseBatteryProduct()
{
ItemInstance battery = playerItemHandler.currentItem.Get<ItemInstance>("Battery");
// 배터리가 없을 경우
if (!battery)
{
Debug.Log("배터리를 장착하세요!");
return false;
}
float rate = battery.Get<float>("batteryUsageRate");
// 현재 배터리 충전량이 0이하일 경우
if (rate <= 0)
{
Debug.Log("배터리가 모두 소진되었습니다.");
return false;
}
// Update에서 배터리의 batteryUsageRate가 아이템을 사용하는 동안 점차 줄어듬
rate -= Time.deltaTime;
battery.Set<float>("batteryUsageRate", rate);
Debug.Log(rate);
return true;
}
R키를 이용해 배터리 선택 창을 열고, 마우스 왼클릭, 우클릭으로 장착할 배터리를 고릅니다.
현재 장착한 배터리가 없다면 쓰지 못하고, 장착한 배터리가 있으며 해당 배터리에 충전량이 있을 때만 사용할 수 있습니다.
연료는 배터리와 같은 형식으로 관리됩니다. 그러니 쓰는 UI도 로직도 모두 같습니다.
이럴 경우에는 다른 함수를 만드는 것이 아닌, 같은 함수에서 조건만 나누어 관리하는 것이 훨씬 효율적입니다.
BatteryManager를 ChargeManager로 바꾸어 진행합시다.
// 제트팩과 스캐너에 넣어 에디어의 인스펙터에서 설정합니다
public Charge Type chargeType;
public enum ChargeType
{
None,
Battery,
Fuel
}
위의 타입을 추가하고, Batter를 관리했던 모든 함수에 chargeType에 따른 조건부를 추가합니다.
아직 제트팩 로직은 완성되지 않았으나, 시험용으로 UseItem에 코드를 추가합시다.
public void UseItems(ItemInstance item)
{
switch(item.itemData.itemName)
{
case "스캐너":
if (Input.GetMouseButtonDown(1)) UseScanner();
break;
case "나이프":
if (Input.GetMouseButtonDown(0)) UseKnife(item.itemData.attackPower);
break;
case "제트팩":
if (Input.GetMouseButtonDown(1)) UseJetPack();
break;
default:
return;
}
}
private void UseJetPack()
{
if (chargeUIOpen) return;
bool isCan = CanUseFuelProduct();
}
private bool CanUseFuelProduct()
{
ItemInstance fuel = playerItemHandler.currentItem.Get<ItemInstance>("Fuel");
if (!fuel)
{
Debug.Log("현재 연료통이 존재하지 않습니다!");
return false;
}
float rate = fuel.Get<float>("fuelUsageRate");
if (rate <= 0)
{
Debug.Log("연료가 모두 소진되었습니다!");
return false;
}
rate -= Time.deltaTime;
fuel.Set<float>("fuelUsageRate", rate);
Debug.Log("연료가 소진되는 중입니다...");
return true;
}
배터리와 연료통 모두 교체 및 사용이 가능해집니다.
다음 글에서는: