김김김의 게임개발
  • 유니티 게임 개발 숙련 #2
    2023년 09월 20일 20시 55분 25초에 업로드 된 글입니다.
    작성자: noun06

    개요

    개인 프로젝트인 RPG 플레이어 스탯 및 인벤토리 시스템의 기본적인 부분들을 완성하였음. 실제로 스크립터블 오브젝트 개념을 특히 아이템 쪽에 적용하여 활용해보았는데 이 기능의 확정성, 편의성 측면의 장점을 체감하였음. 현재 가장 큰 문제점은 플레이어 스크립터블 오브젝트에 직접적으로 아이템 장착/장착 해제 수치가 반영되기 때문에 고정되어 있는 플레이어 스크립터블 오브젝트를 리셋시키는 기능이 필요함. 


    플레이어 스탯

    • 플레이어 스크립터블 오브젝트 클래스인 PlayerSO는 플레이어의 정보 속성들을 가지고 있음.
    using UnityEngine;
    
    [CreateAssetMenu(fileName = "Player Info", menuName = "SO/Player")]
    public class PlayerSO : ScriptableObject
    {
        [Header("Basic Info")]
        public string playerName;
        public int level;
        public int gold;
    
        [Header("Stats Info")]
        public int atk;
        public int def;
        public int maxHP;
        public int maxMP;
        public int maxSP;
        public int luk;
    }

     

    • PlayerStatsUI 클래스 내에서 PlayerSO의 데이터를 가져와서 UI 상에서 나타냄.
        public PlayerSO playerData;
        
        private void Update()
        {
            UpdatePlayerStatsUI();
        }
    
        void UpdatePlayerStatsUI()
        {
            playerNameText.text = playerData.playerName;
            playerLevelText.text = "LV. " + playerData.level.ToString();
            playerGoldText.text = playerData.gold.ToString();
            playerAttackText.text = "ATK: " + playerData.atk.ToString();
            playerDefenseText.text = "DEF: " + playerData.def.ToString();
            playerMaxHPText.text = "HP: " + playerData.maxHP.ToString();
            playerMaxMPText.text = "MP: " + playerData.maxMP.ToString();
            playerMaxSPText.text = "SP: " + playerData.maxSP.ToString();
            playerLuckText.text = "LUK: " + playerData.luk.ToString();
        }

     

    인벤토리 시스템

    • 동일하게 아이템의 경우도 스크립터블 오브젝트를 통해 관리하며 이를 기반으로 여러 종류의 에셋들을 만들어 놓음.
    using UnityEngine;
    
    [CreateAssetMenu(fileName = "Item Info", menuName = "SO/Item")]
    public class ItemSO : ScriptableObject
    {
        public string itemName;
        public string itemDescription;
        public ItemType itemType;
    
        public int itemDamage;
        public int itemArmor;
    
        public Sprite itemImage;
    
        public enum ItemType
        {
            Weapon,
            Armor,
            Consumable
        }
    }

     

    • Item.cs는 실제 아이템 객체들의 동작을 가지고 있음. ItemSO의 데이터를 가져와 사용하며 플레이어와 충돌 시 인벤토리에 아이템을 추가하는 메서드를 호출함.
    using UnityEngine;
    
    public class Item : MonoBehaviour
    {
        [SerializeField] private ItemSO itemData;
    
        private void OnCollisionEnter2D(Collision2D collision)
        {   
            if (collision.gameObject.CompareTag("Player"))
            {
                InventoryManager.instance.AddItemToInventory(itemData);
                Destroy(gameObject);
            }
        }
    }

     

    • InventoryManager.cs는 메뉴 창 토글, 인벤토리에 아이템 추가의 기능을 수행함. 각 아이템 슬롯들을 배열 변수로 선언하여 반복문을 통해 각 슬롯 배열에 item.cs를 통해 추가된 아이템을 AddItem() 메서드를 통해 실제 아이템 정보들과 함께 인벤토리에 추가함.
    using UnityEngine;
    
    public class InventoryManager : MonoBehaviour
    {
        public GameObject menu;
        private bool isMenuActive;
    
        [SerializeField] private ItemSlot[] itemSlots;
    
        public static InventoryManager instance;
    
        private void Awake()
        {
            if (instance == null)
            {
                instance = this;
            }
            else
            {
                Destroy(gameObject);
            }
        }
    
        void Update()
        {
            if (Input.GetKeyDown(KeyCode.Escape))
            {
                ToggleMenu(!isMenuActive);
            }
        }
    
        public void AddItemToInventory(ItemSO itemData)
        {
            for (int i = 0; i < itemSlots.Length; i++)
            {
                if (itemSlots[i].isFull == false)
                {
                    itemSlots[i].AddItem(itemData);
                    return;
                }
            }
        }
    
        private void ToggleMenu(bool showMenu)
        {
            menu.SetActive(showMenu);
            isMenuActive = showMenu;
        }
    }

     

    • ItemSlot.cs는 인벤토리의 각 슬롯을 관리하는 역할을 함. InventoryManager을 거쳐 받아온 아이템 정보들을 아이템 정보창을 통해 나타냄과 동시에 각 슬롯 아이템의 장착관리를 함. IPointerEventData 인터페이스를 구현하여 좌클릭시 아이템 정보들을 보여주고 우클릭시 장착 상황을 토글함.
    using UnityEngine;
    using UnityEngine.EventSystems;
    using UnityEngine.UI;
    using TMPro;
    
    public class ItemSlot : MonoBehaviour, IPointerClickHandler
    {
        private ItemSO itemData;
        public bool isFull;
        private bool isEquipped = false;
    
        [SerializeField] private Image itemImage;
        [SerializeField] private GameObject selectedSlot;
        [SerializeField] private GameObject equippedImage;
        [SerializeField] private bool isSelected;
    
        [SerializeField] private Image itemDescriptionImage;
        [SerializeField] private TMP_Text itemDescriptionNameText;
        [SerializeField] private TMP_Text itemDescriptionText;
    
        public void AddItem(ItemSO itemData)
        {
            this.itemData = itemData;
            isEquipped = false;
            isFull = true;
    
            itemImage.sprite = itemData.itemImage;
        }
    
        public void OnPointerClick(PointerEventData eventData)
        {
            if (eventData.button == PointerEventData.InputButton.Left)
            {
                if (itemData != null)
                {
                    selectedSlot.SetActive(true);
                    isSelected = true;
                    itemDescriptionNameText.text = itemData.itemName;
                    itemDescriptionText.text = itemData.itemDescription;
                    itemDescriptionImage.sprite = itemData.itemImage;
                }
            }
            else if (eventData.button == PointerEventData.InputButton.Right)
            {
                if (itemData != null)
                {
                    ToggleItemEquip();
                }
            }
        }
    
        public void ToggleItemEquip()
        {
            isEquipped = !isEquipped;
    
            if (isEquipped)
            {
                PlayerStatsController.instance.EquipItem(itemData);
                equippedImage.SetActive(true);
            }
            else
            {
                PlayerStatsController.instance.UnequipItem(itemData);
                equippedImage.SetActive(false);
            }
        }
    }

     

    • PlayerStatsController.cs는 장착/장착해제 상황에서의 동작을 나타냄. PlayerSO의 데이터를 가져와 아이템의 공격력 수치만큼 더해주거나 빼줌. 
    using UnityEngine;
    
    public class PlayerStatsController : MonoBehaviour
    {
        [SerializeField] private PlayerSO playerData;
        private int originalAtk;
    
        public static PlayerStatsController instance;
    
        private void Awake()
        {
            if (instance == null)
            {
                instance = this;
            }
            else
            {
                Destroy(gameObject);
            }
        }
    
        private void Start()
        {
            originalAtk = playerData.atk;
        }
    
        public void EquipItem(ItemSO item)
        {
            if (item.itemType == ItemSO.ItemType.Weapon)
            {
                playerData.atk += item.itemDamage;
            }
        }
    
        public void UnequipItem(ItemSO item)
        {
            if (item.itemType == ItemSO.ItemType.Weapon)
            {
                playerData.atk -= item.itemDamage;
            }
        }
    }
    댓글