인벤토리, 아이템 사용, 버프 효과 적용
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
[SerializeField, Tooltip("인벤토리 보이는 부분 전체")] private Transform visiblePart;
[SerializeField, Tooltip("슬롯의 프리펩")] private Transform slotPrefab;
[SerializeField, Tooltip("입벤토리 정렬 패널")] private Transform layoutPanel;
[SerializeField, Tooltip("아이템 정보 출력 패널")] private InfoPanel infoPanel;
// 인벤토리 아이템 정보
List<Slot> slots = new List<Slot>();
public List<Slot> GetSlots { get { return slots; } }
void Start()
{
// 아이템 추가 메서드를 플레이어의 액션에 등록
GameManager.Instance.Player.addItem += AddItem;
}
// 인벤토리 활성/비활성
public void ActiveChange()
{
// 활성/비활성 전환
visiblePart.gameObject.SetActive(!visiblePart.gameObject.activeSelf);
// 인벤토리 출현 때는 마우스가 나타나고 닫을 때는 마우스가 사라지도록
if(visiblePart.gameObject.activeSelf)
Cursor.lockState = CursorLockMode.None;
else
Cursor.lockState = CursorLockMode.Locked;
}
// 인벤토리에 아이템 추가
void AddItem(InventorySlot addItemInfo)
{
// 쌓을 수 있는 아이템이라면
if (addItemInfo.item.canStack)
{
// 얼만큼 쌓아야하는지 추적하기 위한 변수
int stackAmount = addItemInfo.count;
// 기존의 칸들에 동일한 아이템 슬롯이 있다면 쌓기
for (int i = 0; i < slots.Count; i++)
{
// 동일한 아이템이면
if (slots[i].slotInfo.item.id == addItemInfo.item.id)
{
// 해당 칸에 쌓고 남는 값 반환
stackAmount = slots[i].AddCount(addItemInfo.count);
// 해당 칸에 다 쌓아서 남는 갯수가 없다면, 더 이상 루프를 진행할 필요가 없기에 break
if(stackAmount.Equals(0))
break;
}
}
// 새로 슬롯을 생성하면서 넣어주기
// 쌓으려는 수를 모두 인벤토리에 넣었다면 끝!
while (stackAmount > 0)
{
// 새 칸 생성 및 아이템 정보 표시, 적재하지 못한 값을 반환
stackAmount = CreateASlot(addItemInfo);
}
}
// 적재 불가능한 아이템이라면 새 슬롯 생성
else
{
CreateASlot(addItemInfo);
}
}
int CreateASlot(InventorySlot addItemInfo)
{
int exceedAmount = 0;
// 인벤토리 새로 한칸 생성하여 인벤토리 자동정렬, Slot 컴포넌트를 가져오는데 성공하였다면
if (Instantiate(slotPrefab.gameObject, layoutPanel).TryGetComponent(out Slot slot))
{
// 정보를 넘겨주고 이를 표시
exceedAmount = slot.CreateSlot(addItemInfo);
// 슬롯 리스트에 추가
slots.Add(slot);
}
return exceedAmount;
}
public void OpenInfoPanel(int slotIndex)
{
// 해당 슬롯으로 위치 이동
infoPanel.transform.position = slots[slotIndex].transform.position;
// 활성화
infoPanel.gameObject.SetActive(true);
// 정보 표시
ItemData data = slots[slotIndex].slotInfo.item;
infoPanel.InitInfos(data.itemName, data.description);
}
public void CloseInfoPanel()
{
infoPanel.gameObject.SetActive(false);
}
}
public class InventorySlot
{
public ItemData item; // 아이템 정보
public int count; // 보유 갯수
public InventorySlot(ItemData _item, int _count)
{
item = _item;
count = _count;
}
}
인벤토리는 아이템을 획득함에 따라 슬롯을 생성합니다.
한 칸에 쌓는 양을 초과한 아이템은 새로운 슬롯을 생성하여 그곳에 쌓습니다.
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.EventSystems;
public class Slot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler, IDragHandler, IEndDragHandler
{
[SerializeField] private Image icon;
[SerializeField] private TextMeshProUGUI count;
public InventorySlot slotInfo { get; private set; }
// 아이템 슬롯을 생성할 때 호출
public int CreateSlot(InventorySlot _inventorySlot)
{
// 아이템 정보 가지고 있게끔
slotInfo = _inventorySlot;
// 아이콘 표시
icon.sprite = slotInfo.item.icon;
// 수량 표시
int exceedAmount = AddCount(0);
// 넘치는 수량을 반환
return exceedAmount;
}
// 아이템 수량 증가
public int AddCount(int amount)
{
// 수량 증가, 표시
slotInfo.count += amount;
count.text = Mathf.Clamp(slotInfo.count, 0, slotInfo.item.maxStack).ToString();
// 한 칸에 최대로 쌓을 수 있는 갯수를 초과하는 아이템 수
int exceedAmount = slotInfo.count - slotInfo.item.maxStack;
// 최대 적재량을 넘었다면 그만큼을 반환
if (exceedAmount > 0)
return exceedAmount;
// 넘지 않았다면 0을 반환
else
return 0;
}
void UseOrEquipItem()
{
switch (slotInfo.item.type)
{
// 소비 아이템
case ItemType.Consume:
// 해당 아이템 특수 효과 발동
slotInfo.item.UseEffect?.Invoke();
// 해당 소비 아이템의 소비 속성 수만큼
for(int i = 0; i < slotInfo.item.consumables.Length; i++)
{
// 해당 속성을 value에 맞게 회복
switch (slotInfo.item.consumables[i].type)
{
case ConsumableType.Health:
GameManager.Instance.Player.status.Heal(slotInfo.item.consumables[i].value);
break;
}
}
// 수량 1개 감소 >> 0개 이하가 되었다면
if (--slotInfo.count <= 0)
{
// 아이템 설명 패널 사라지게
GameManager.Instance.Inventory.CloseInfoPanel();
// 해당 슬롯 파괴
GameManager.Instance.Inventory.GetSlots.Remove(this);
Destroy(gameObject);
}
// 수량 감소 표시
else
count.text = slotInfo.count.ToString();
break;
// 장착 아이템
case ItemType.Equip:
break;
default:
break;
}
}
// 해당 슬롯 위에 마우스가 올라갔을 때
public void OnPointerEnter(PointerEventData eventData)
{
GameManager.Instance.Inventory.OpenInfoPanel(transform.GetSiblingIndex());
}
// 해당 슬롯 위에서 마우스가 벗어났을 때
public void OnPointerExit(PointerEventData eventData)
{
GameManager.Instance.Inventory.CloseInfoPanel();
}
public void OnPointerClick(PointerEventData eventData)
{
if (eventData.button == PointerEventData.InputButton.Right)
{
UseOrEquipItem();
}
}
// 해당 오브젝트 드래그 도중
public void OnDrag(PointerEventData eventData)
{
// 마우스 위치를 오브젝트가 따라다니게끔
}
// 드래그가 끝날 때
public void OnEndDrag(PointerEventData eventData)
{
// 마우스 위치가 인벤토리 밖이라면
// 아이템 버리기
}
}
인벤토리 슬롯은 기능을 만들던 도중에 제출 시간이 되어 여기까지 했습니다.
마우스 동작 관련 인터페이스를 이용하여 마우스가 해당 슬롯 위에 있을 때 정보 창이 뜨고
마우스가 슬롯 위를 벗어나면 사라지게끔 했으며
우클릭을 하면 아이템 사용/장착 메서드를 호출했습니다.
using UnityEngine;
[CreateAssetMenu(fileName = "ItemUseEffects", menuName = "New ItemEffects")]
public class ItemUseEffects : ScriptableObject
{
public void Carrot_UseEffect()
{
// 이동 속도 2배
float speed = 2 * GameManager.Instance.Player.control.GetOriginSpeed;
// 지속 시간
float time = 10f;
GameManager.Instance.Player.control.Call_Change_MoveSpeed(speed, time);
}
}
아이템 데이터를 스크립터블 데이터로 관리했는데
아이템에 유니티 이벤트로 메서드를 등록하려 하니 일반적인 스크립트로는 안되어서
특수능력 메서드들을 모아둘 스크립터블 오브젝트를 하나 생성하였습니다.
// 일정 시간 동안 이동속도 증가
public void Call_Change_MoveSpeed(float _moveSpeed, float _time)
{
if(ChangingMoveSpeed != null)
StopCoroutine(Change_MoveSpeed(_moveSpeed, _time));
StartCoroutine(Change_MoveSpeed(_moveSpeed, _time));
}
IEnumerator Change_MoveSpeed(float _moveSpeed, float _time)
{
// 바꿀 속도로 변화
moveSpeed = _moveSpeed;
// 버프 시간 동안 유지
yield return new WaitForSeconds(_time);
// 원래 속도로 되돌아가기
moveSpeed = moveSpeedOrigin;
}
이동 속도 변화 버프는 코루틴으로 일정 시간 동안 변화 후 다시 원래대로 돌아오도록 만들었습니다.
아마 시간이 조금 더 있었다면 스텟 종류별 버프를 모아 관리하는 버프매니저를 만들어서 처리하였을 듯 합니다.
'스파르타코딩클럽_Unity개발과정' 카테고리의 다른 글
유니티 숙련 팀 과제 - 서바이벌 (2) (0) | 2025.05.27 |
---|---|
유니티 숙련 팀 과제 - 서바이벌 (1) (1) | 2025.05.26 |
유니티 숙련 개인 과제 - 3D 게임 캐릭터 이동과 물리 (2) (0) | 2025.05.22 |
유니티 숙련 개인 과제 - 3D 게임 캐릭터 이동과 물리 (1) (0) | 2025.05.21 |
유니티 숙련 (3) - 서바이벌 강의 완강, 새로 배운 내용 정리 (0) | 2025.05.20 |