개요
게임에서 인벤토리는 아이템이 수납되는 장소를 일컫는다.* 이번에는 강의에서 언급된 Scroll View
의 한계에 대해서 살펴보고, 간단한 필터와 정렬 기능이 들어간 인벤토리를 구현해보고자 한다. 리소스는 강의에서 제공한 것을 그대로 사용하도록 하고 여기서는 핵심 코드에만 집중하도록 한다.
* 인벤토리에 대한 자세한 설명이 필요하다면 나무위키를 참고하기를 바란다.
Scroll View의 한계
Unity로 채팅창과 같이 스크롤링이 필요한 UI 요소를 구현하고 싶다면 Scroll View
를 이용하면 된다. Scroll View
에 대한 자세한 설명은 이 영상과 UGUI 매뉴얼로 대신한다. Scroll View
에는 2가지 아쉬운 점이 있다. 첫 번째는 최적화다. UGUI는 자동으로 Content
의 자식오브젝트를 재사용하지 않는다. 그래서 이를 부주의하게 사용한다면 급격한 프레임 드랍을 맛볼 수 있다.* 두 번째는 Scroll View
를 제어하는 기능이 부족하다. 그래서 애셋 스토어에서 관련된 애셋을 구매하거나 직접 확장하여 구현하는 수밖에 없다. 강의에서는 NHN에서 개발한 GPM을 소개하고 있다. GPM은 무료기에 GPM에서 제공하는 기능만으로도 충분하다면 괜찮은 선택지가 될 것으로 보인다. 다른 애셋은 여기서 직접 찾아볼 수 있다.
* 이에 대한 것은 이 영상에 잘 나타난다.
데이터 클래스 정의
우선 아이템을 나타낼 ItemModel
부터 정의한다. 아이템은 탕탕특공대처럼 장비 아이템만 나타낼 것이며, 등급과 종류가 있다. 식별자로부터 ItemGrade
와 ItemType
을 추출할 메소드도 포함시켰다.
using System.Text;
using System;
public enum ItemType
{
Weapon = 1,
Shield,
ChestArmor,
Gloves,
Boots,
Accessery
}
public enum ItemGrade
{
Common = 1,
Uncommon,
Rare,
Epic,
Legendary
}
[Serializable]
public class ItemModel
{
public int item_id;
public string item_name;
public int attack_power;
public int defense;
public static ItemGrade GetGrade(int id)
{
return (ItemGrade)((id / 1000) % 10);
}
public static ItemType GetType(int id)
{
return (ItemType)(id / 10000);
}
public static string GetIconPath(int id)
{
StringBuilder sb = new(id.ToString());
sb[1] = '1';
return sb.ToString();
}
}
이를 바탕으로 인벤토리에 넣을 데이터인 UserInventoryData
클래스를 정의한다. 레벨 별 정렬을 위해서 던전앤파이터 모바일처럼 획득 시 아이템 레벨이 결정된다고 가정하고 item_level
필드를 추가했다. serial_number
는 게임 내에서 고유하게 식별되어야 하는 번호이다.
[Serializable]
public class UserInventoryData
{
public long serial_number;
public int item_level;
public int item_id;
}
아이템 종류로 필터링하기
아이템 종류로 필터링을 해보자. 우선 FilterType
을 정의했다.
enum FilterType
{
All,
Weapon,
Shield,
ChestArmor,
Gloves,
Boots,
Accessery,
Max
}
GPM의 설명에 따르면 Predicate<InfiniteScrollData>
으로 필터링 함수를 만들어야 한다.
private bool OnFilter(InfiniteScrollData data)
{
if (_filterType == FilterType.All)
{
return false;
}
var slotData = data as InventoryItemSlotData;
return (int)_filterType != (int)ItemModel.GetType(slotData.UserInventoryData.item_id);
}
그리고 InfiniteScroll.SetFilter()
에 위 함수를 전달하면 된다.
private void Refresh()
{
_infiniteScroll.Clear();
foreach (var item in _userItems)
{
_infiniteScroll.InsertData(new InventoryItemSlotData() { UserInventoryData = item });
}
_infiniteScroll.SetFilter(OnFilter);
// 후략
}
아이템 등급으로 정렬하기
다음으로는 정렬 기능을 구현해보자. 정렬의 경우 GPM에서 기본으로 제공되는 것이 아니기 때문에 코드를 고쳐야 한다. InfiniteScroll.ItemData.cs
파일을 열어보면 InfiniteScroll
이 DataContext
타입의 객체를 다루는 것을 확인할 수 있다. 해당 파일 아래에 메소드를 추가한다.
public void SortList(Comparison<DataContext> comparison)
{
dataList.Sort(comparison);
needUpdateItemList = true;
}
그리고 코드에서 DataContext
내부의 data
필드에 접근해야 하므로 internal
을 public
으로 바꿔준다.
public class DataContext
{
public DataContext(InfiniteScrollData data, int index)
{
this.index = index;
this.data = data;
}
public InfiniteScrollData data; // public으로 변경
internal int index = -1;
// 후략
}
정렬 방식은 등급, 레벨 2가지만 제공한다. 각각에 대응하는 함수를 InventoryUI
에 추가한다.
private int CompareByLevel(InventoryItemSlotData lhs, InventoryItemSlotData rhs)
{
int lhsLevel = lhs.UserInventoryData.item_level;
int rhsLevel = rhs.UserInventoryData.item_level;
return rhsLevel.CompareTo(lhsLevel);
}
private int CompareByGrade(InventoryItemSlotData lhs, InventoryItemSlotData rhs)
{
var lhsGrade = ItemModel.GetGrade(lhs.UserInventoryData.item_id);
var rhsGrade = ItemModel.GetGrade(rhs.UserInventoryData.item_id);
return rhsGrade.CompareTo(lhsGrade);
}
SortInventory()
를 만들어준다.
private void SortInventory()
{
_infiniteScroll.SortList((lhs, rhs) =>
{
var lhsData = lhs.data as InventoryItemSlotData;
var rhsData = rhs.data as InventoryItemSlotData;
int compareResult = 0;
switch (_sortType)
{
case SortType.Grade:
compareResult = CompareByGrade(lhsData, rhsData);
if (compareResult == 0)
{
compareResult = CompareByLevel(lhsData, rhsData);
}
break;
case SortType.Level:
compareResult = CompareByLevel(lhsData, rhsData);
if (compareResult == 0)
{
compareResult = CompareByGrade(lhsData, rhsData);
}
break;
}
return compareResult;
});
}
결과물과 모든 코드는 여기에서 볼 수 있다.
참고자료
- https://namu.wiki/w/%EC%9D%B8%EB%B2%A4%ED%86%A0%EB%A6%AC
- https://docs.unity3d.com/Packages/com.unity.ugui@1.0/manual/index.html
- https://youtu.be/3Y3wFC33mpU?si=f1U2SEgWdlfS4HXk
- https://www.inflearn.com/course/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-1/dashboard
'Study > Unity' 카테고리의 다른 글
앵커와 피봇은 가만히 놔두는 게 좋습니다. (0) | 2024.10.30 |
---|---|
UI 이벤트 메소드를 연결 전에 잘 살펴봐야 하는 이유 (0) | 2024.10.29 |
[Unity System Programming Pt.1] 2주차 (5) | 2024.09.24 |
[Unity System Programming Pt.1] 1주차 (7) | 2024.09.10 |
Unity에 싱글톤(Singleton) 패턴 적용하기 (0) | 2022.07.17 |