[Unity / C#] 배터리, 연료 사용 아이템 제어

주예성·2025년 7월 1일

📋 목차

  1. 배터리 교체 UI
  2. 배터리 관리
  3. 배터리 사용 아이템 조건 추가
  4. 중간 결과
  5. 연료 교체 및 관리
  6. 연료 사용
  7. 최종 결과
  8. 오늘의 배운 점
  9. 다음 계획

🔋 배터리 교체 UI

배터리를 넣거나 바꾸려면 UI가 필요합니다. HUD_PanelBatterySlots로 칸을 관리해줍니다.

// 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도 로직도 모두 같습니다.
이럴 경우에는 다른 함수를 만드는 것이 아닌, 같은 함수에서 조건만 나누어 관리하는 것이 훨씬 효율적입니다.
BatteryManagerChargeManager로 바꾸어 진행합시다.

// 제트팩과 스캐너에 넣어 에디어의 인스펙터에서 설정합니다
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;
}

🎮 최종 결과

배터리와 연료통 모두 교체 및 사용이 가능해집니다.


📚 오늘의 배운 점

  • 제네릭을 이용한 Get, Set 활용
  • 오브젝트 교체

🎯 다음 계획

다음 글에서는:

  1. 제트팩 구현
profile
Unreal Engine & Unity 게임 개발자

0개의 댓글