[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 17회차 미션 시작합니다.




이제부터는 게임내에 인벤토리 시스템을 구현하고, 장비 시스템과 아이템 시스템을 구현합니다.
오늘은 아이템 시스템 구현하기와 플레이어와 상점을 위한 인벤토리 시스템 구현하기의 시작입니다.

인벤토리 시스템은 Scriptable Object를 사용합니다. 이를 사용하면 Game내 데이터 관리와 전역적 접근을 쉽게 해줍니다.




+ 아이템 시스템 구현하기
+ 인벤토리 시스템 구현하기 - 구현된 아이템 시스템 이용.
+ 인벤토리 UI 구현하기 - 인벤토리 시스템에 UI 구현
+ 캐릭터 장비 교체 구현하기
+ 아이템 획득/사용 구현하기





가장 기본이되는 것은 Item 클래스입니다. 캐릭터의 스텟을 변화시키는 기능을 해주는 것이고, Item의 buffs에 ItemBuff 오브젝트를 생성하여 Attach하는 방식으로 구현할 수 있습니다.
ItemObject는 Item을 가지는 데이터 오브젝트입니다.
Item은 캐릭터에 대한 행동을 명시하고, ItemObject는 캐릭터의 행동과 표현방법, 설명 등을 포함하는 것입니다.
그리고 이를 List화하여 가지고 있는 ItemDatabaseObject를 구현하게 됩니다.

먼저 Item 행동에 해당하는 ItemBuff와 Item, ItemObject 컴포넌트 스크립트를 작성합니다.



ItemBuff Script

public enum CharacterAttribute
{
    Agility,
    Intellect,
    Stamina,
    Strength
}

[Serializable]
public class ItemBuff
{
    public CharacterAttribute stat;
    public int value;
    
    [SerializeField]
    private int min;
    [SerializeField]
    private int max;
    
    public int Min => min;
    public int Max => max;
    
    public ItemBuff(int min, int max) {
        this.min = min;
        this.max = max;
        
        GenerateValue();
    }
    
    public void GenerateValue() {
        value = UnityEngine.Random.Range(min, max); // 획득 아이템 랜덤 값
    }
    
    public void AddValue(ref int v) {
        v += value;
    }
}



Item Script

[Serializable]
public class Item
{
    public int id = -1;
    public string name;
    public ItemBuff[] buffs;
    
    public Item() {
        id = -1; // 비어있는 아이템
        name = "";
    }
    
    public Item(ItemObject itemobject) {
        name = itemObject.name;
        id = itemobject.data.id;
        buffs = new ItemBuff[itemObject.data.buffs.Length];
        for (int i = 0; i < buffs.Length; i++) {
            buffs[i] = new ItemBuff(itemobject.data.buffs[i].Min, itemObject.data.buffs[i].Max) {
                stat = itemObject.data.buffs[i].stat;
            };
        }
    }
}




ItemObject Script

public enum ItemType: int
{
    Helmet = 0,
    Chest = 1,
    Pants = 2,
    Boots = 3,
    Pauldrons = 4,
    Gloves = 5,
    LeftWeapon = 6,
    RightWeapon = 7,
    Food = 8,
    Default,
}

[CreateAssetMenu(fileName = "New Item", menuName = "Inventory System/Items/New Item")]
public class ItemObject : ScriptableObject
{
    public ItemType type;
    public bool stackable;
    public Sprite icon;
    public GameObject modelPrefab;
    public Item data = new Item();
    public List<string> boneNames = new List<string>();
    
    [TextArea(15, 20)]
    public string description;
   
    private void OnValidate() {
        boneNames.Clear();
        if (modelPrefab == null || modelPrefab.GetComponentInChildren<SkinnedMeshRenderer>() == null) {
            return;
        }
        
        SkinnedMeshRenderer renderer = modelPrefab.GetComponentInChildren<SkinnedMeshRenderer>();
        Transform[] bons = renderer.bones;
        
        for (Transform t in bones) {
            boneNames.Add(t.name);
        }
    }
    
    public Item CrateItem() {
        Item newItem = new Item(this);
        return newItem;
    }
}



여기까지하여 컴파일이 정상적으로 되면 아래와 같이 Create - Inventory System - Items - New Item 과 같이 추가된 것을 확인할 수 있습니다. 허거거걱... 머리가 핑핑... 눈도 핑핑 @v@~



 

 




이렇게 추가한 아이템들을 관리하는 ItemObjectDatabase 스크립트를 작성합니다. ㅠ


[CreateAssetMenu(fileName = "New Item Database", menuName = "Inventory System/Items/Database_New")]
public class ItemObjectDatabase : ScriptableObject
{
    public ItemObject[] itemObjects;
    
    public void OnValidate() {
        for (int i = 0; i < itemObjects.Length; i++) {
            itemObjects[i].data.id = i;
        }
    }
}


어마어마하네요.. CreateAssetMenu() 함수를 통해서 Unity에 원하는 형태로 Menu를 등록하고, Item 등의 컴포넌트도 생성할 수 있도록 구성한데다가, ItemObjectData를 구현해서 Unity UI 상에서 그냥 드래그해서 자동 추가가 되어버리다니... 헐...
무언가 할당하는 개념이 없고 UI에서 생성, 할당하는 부분들이 많아서 적응은 잘 되지 않고, 언제 왜 써야하는지 애매하긴 하지만 일단 어마한 기능이라고 밖에 말할 수 없겠네요.






이제 Inventory 시스템 구현입니다.
여기서의 메인은 InventorySlot 입니다. 이것은 아이템이 놓이는 공간이지요. 여기서 아이템이 추가, 삭제될 때마다 갱신 메시지를 발생시켜서 Action으로 관리하고, type 지정으로 해당하는 위치에만 놓이도록 하게 만들 예정입니다.

Inventory 부분은 Inventory UI와 연결되는 부분입니다

 



Item을 ItemObject에서 분리하고, Inventory를 InventoryObject와 구분하여 만든 이유는,
Item과 ItemBuff 내용을 그대로 Json으로 저장하기 편하며, Inventory도 클래스 그대로 Json으로 저장하기 편하기 때문입니다.
ItemObject나 InventoryObject는 데이터를 뽑아내는 과정을 거쳐야 처리할 수 있는데 말이지요.
Json으로 저장하지 않는다면 한 클래스로 구현하는 것이 관리상의 편의점은 있겠습니다.


[Serializable]
public class InventorySlot
{
    public ItemType[] allowedItems = new ItemType[0];
    
    [NonSerialized] // Json 저장시 제외
    public InventoryObject parent;
    [NonSerialized]
    public GameObject slotUI;
    [NonSerialized]
    public Action<InventorySlot> OnPreUpdate;
    [NonSerialized]
    public Action<InventorySlot> OnPostUpdate;
    
    public Item item;
    public int amount;
    public ItemObject ItemObject {
        get {
            return item.id >= 0 ? parent.database.itemObjects[item.id] : null;
        }
    }
    
    public InventorySlot() => UpdateSlot(new Item(), 0);
    public InventorySlot(Item item, int amount) => UpdateSlot(item, amount);
   
    public void RemoveItem() => UpdateSlot(new Item(), 0);
    public void AddAmount(int value) => UpdateSlot(item, amount += value);
    
    public void UpdateSlot(Item item, int amount) {
        OnPreUpdate?.Invoke(this);
        this.item = item;
        this.amount = amount;
        OnPostUpdate?.Invoke(this);
    }
    
    public bool CanPlaceInSlot(ItemObject itemObject) {
        if (allowedItems.Length <= 0 || itemObject == null || itemObject.data.id < 0) {
            return true;
        }
        
        foreach (ItemType type in allowedItems) {
            if (itemObject.type == type) {
                return true;
            }
        }
        
        return false;
    }
}



[Serializable]
public class Inventory
{
    public InventorySlot[] slots = new InventorySlot[24];
    
    public void Clear() {
        foreach (InventorySlot slot in slots) {
            slot.RemoveItem(); // or slot.UpdateSlot(new Item(), 0);
        }
    }
   
    public bool IsContain(ItemObject itemObject) {
        return IsContain(itemObject.data.id);
    }
    
    public bool IsContain(int id) {
        return slots.FirstOrDefault(i => i.item.id == id) != null;
    }
}





<위의 코드들은 제가 보면서 주요한 함수, 코드를 확인하기 위해 타이핑한 용도로, 전체 소스코드가 아님에 주의해 주세요. 전체 코드는 교육 수강을 하면 완벽하게 받으실 수가 있답니다 ^^>

패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0


 

유니티 게임 포트폴리오 완성 올인원 패키지 Online. | 패스트캠퍼스

게임 콘텐츠 프로그래머로 취업하고 싶다면, 포트폴리오 완성은 필수! '디아블로'와 '배틀그라운드' 게임을 따라 만들어 보며, 프로그래머 면접에 나오는 핵심 개념까지 모두 잡아 보세요!

www.fastcampus.co.kr

 

+ Recent posts