TIL

[멋쟁이사자처럼 부트캠프 TIL회고] Unity 게임개발 3기 25-26일차

Cadi 2024. 12. 15. 22:28

 토요일(어제)은 여의도에 갔다오느라 공부를 못하고 오늘은 어제 25000보, 5Km 런닝으로 뻗어버려.....(변명)

아무튼 금요일날 진행했던, 인벤토리 만들기의 코드 해석부터 시작 ! 

 

 

1. ItemData

 

ScriptableObject로 아이템 데이터라는 새로운 '데이터 상자'를 만들어준다. (Class)

참조 :https://docs.unity3d.com/kr/2023.2/Manual/class-ScriptableObject.html

 

ScriptableObject - Unity 매뉴얼

ScriptableObject는 클래스 인스턴스와는 별도로 대량의 데이터를 저장하는 데 사용할 수 있는 데이터 컨테이너입니다. ScriptableObject의 주요 사용 사례 중 하나는 값의 사본이 생성되는 것을 방지하

docs.unity3d.com

 

ItemData는 이름과 아이콘을 지녔고, ScriptableObjet를 상속받을 것이다. 

 

그리고 ItmeInfo라는 ItemData와 int(수량 표시)로 이루어진 클래스도 만든다.

  • ItemInfo : 우리가 인벤창에서 관리할 것, 아이템데이터와 아이템 수량으로 이루어져 있음
  • ItemData : 한 아이템이 어떤 이름과 아이콘을 갖고 있는지, 데이터 상자에 저장.

 

아이템 데이터들을 관리하기 위해, 새로운 게임 오브젝트에 들어갈 ItemManager class도 만들고

리스트로 itemData들을 넣어준다. 

 

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "ItemData", menuName = "Datas/ItemData")]
public class ItemData : ScriptableObject
{
    public string itemName;
    public Sprite icon;
}

public class ItemInfo
{
    public ItemData itemData;
    public int amount;
}

public class ItemManager : MonoBehaviour
{
    public List<ItemData> itemDatas = new List<ItemData>();
    
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

* Sprite는 2D 그래픽 오브젝트

 

2. ItemButton

 

데이터를 캡슐화 해준다. (의도되지 않은 방법으로의 접근을 막기 위해)

iteminfo를 get( 읽기) 할 수 있고, set( 변경) 할 때는 SetItemImage로만 가능하게 한다. 그리고 SetItemImage에 

itemInfo.ItemdData.icon 을 넣어 사진이 icon으로 변경되게 한다. 

 

SetItemImage( Sprite sprite) 는 간단하게 아무것도 들어가 있지 않을 때는 투명도가 0(투명)

사진이 들어갔을 때에는 투명도가 1(불투명) 하게 해준다. 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ItemButton : MonoBehaviour
{
    private ItemInfo itemInfo;
    public ItemInfo ItemInfo
    {
        get => itemInfo;
        set
        {
            itemInfo = value;
            SetItemImage(itemInfo.itemData.icon);
        }  
    }
    
    [SerializeField]Image itemImage;

    void SetItemImage(Sprite sprite)
    {
        itemImage.sprite = sprite;
        if (sprite == null)
        {
            var color = itemImage.color;
            color.a = 0;
            itemImage.color = color;
        }
        else
        {
            var color = itemImage.color;
            color.a = 1.0f;
            itemImage.color = color;
        }
    }
    
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

* value : 프로퍼티의 set 블럭에서 사용되는 특별한 키워드 / 외부에서 프로퍼티에 값을 설정할 때 전달된 값

 

 

3. Inventory

 

 GridLayoutGruop(정렬을 위한) , ItemButton[](버튼들을 저장] 의 변수를 선언해준다. 

 

Awake 함수를 사용한다. 

*Awake 함수는 Start 함수와 다르게, 스크립트가 비활성화 되어 있어도 호출되며 Start보다 빨리 호출됨.

 

girdLayoutGroup에서 버튼들을 가져와 buttons 배열에 할당해준다. 

ItemMamager는 그 타입이 하나이므로 FindObjectOfType을 써서 찾아주고 변수에 할당해준다. 

 

지금은, 인벤토리의 모든 칸에 랜덤 아이템을 집어넣는 것이 목적이므로 버튼에 랜덤한 아이템들을 추가해주고

버튼에 클릭 이벤트를 추가해준다. ( 아이템을 서로 바꾸는 기능을 추가할 것이다)

* onClick.AddListner() : 버튼에 새로운 기능을 추가해 줄 때 쓰는 함수 

https://docs.unity3d.com/2018.3/Documentation/ScriptReference/UI.Button-onClick.html

 

Unity - Scripting API: UI.Button.onClick

You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see: You've told us there are code samples on this page which don't work. If you know ho

docs.unity3d.com

그리고 버튼의 itemInfo에 새로운 수량인 1과 , 랜덤으로 결정한 itemData들을 추가해준다. 

여기서, var i1 = i라고 설정한 이유는 클로저(Closure)란 것이고 따로 포스팅할 것이다. 

 

 

이제 클릭 이벤트로 추가해준 함수의 기능을 만들 차례이다. 

우선 한 번, 두 번 클릭했다는 상태를 나타내줄 변수 두 개 (selectedItemIndex 1, 2 )를 선언해준다. 

OnClickItemButton(int index) 는 첫 번째로 클릭한 버튼에 있는 아이템의 정보를 저장하고, 두 번 째로 클릭한 아이템의 정보를 저장하고 그것들을 서로 바꾸고 다시 바꿀 수 있게 selectedItemIndex를 초기화 해주는 것이다. 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Inventory : MonoBehaviour
{
    [SerializeField]GridLayoutGroup gridLayoutGroup;
    private ItemButton[] buttons;

    private int selectedItemIndex1 = -1;
    private int selectedItemIndex2 = -1;
    
    // Start is called before the first frame update
    void Awake()
    {
        buttons = gridLayoutGroup.
            GetComponentsInChildren<ItemButton>();

        ItemManager itemManager = FindObjectOfType<ItemManager>();
        for (var i = 0; i < buttons.Length; i++)
        {
            var itemData = itemManager.itemDatas[Random.Range(0, itemManager.itemDatas.Count)];
            
            var i1 = i;
            buttons[i].GetComponent<Button>().
                onClick.AddListener(() => 
                    OnClickItemButton(i1)
                    );

            buttons[i].GetComponent<ItemButton>().ItemInfo = new ItemInfo()
            {
                amount = 1,
                itemData = itemData
            };
        }
    }

    void OnClickItemButton(int index)
    {
        if (0 > selectedItemIndex1)
        {
            selectedItemIndex1 = index;
        }
        else if (0 > selectedItemIndex2)
        {
            selectedItemIndex2 = index;
        
            var itemInfo1 = buttons[selectedItemIndex1].ItemInfo;
            var itemInfo2 = buttons[selectedItemIndex2].ItemInfo;
            buttons[selectedItemIndex1].ItemInfo = itemInfo2;
            buttons[selectedItemIndex2].ItemInfo = itemInfo1;
            selectedItemIndex1 = -1;
            selectedItemIndex2 = -1;
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}