게임을 만들다 보니 반복적인 데이터를 인스턴스화 된 스크립트에 입력해야 하는 경우가 있습니다. 몇 줄 정도는 수작업이 가능하지만 아이템 리스트 같이 대량의 데이터를 반복적으로 할당해야 하는 경우 난감한데요. 이럴 때 엑셀로 데이터를 정리해서 일괄적으로 밀어 넣는 방법을 작성해보겠습니다. 보통 정적인 데이터는 메모리 최적화 때문에 데이터가 필요할 때 스크립터블 오브젝트에서 데이터를 읽어가는데 이번 포스팅에서 작성되는 코드는 대량의 반복 데이터를 지속적으로 계속 참고해야 되는 경우에 고려해 볼만 한 것 같습니다. 예시 방향은 디아블로 2의 세트 아이템 리스트로 잡았습니다. 전제 조건은 세트 아이템의 수는 4개로 고정하고 각 아이템은 투구, 갑옷, 장갑, 신발 순으로 구조화해서 작성하였습니다. 혹 세트별로 숫자가 다를 경우에는 추가 코드가 필요합니다.
1. 데이터 만들기
저는 구글 스프레드시트 사용해서 아이템 정보를 정리하고 cvs 형식으로 뽑아서 구조화된 스크립터블 오브젝트에 입력했습니다.
▶ 엑셀 구조는 아래와 같습니다.
테스트용으로 간단하게 구성하였습니다. 실제 게임에서 사용할 것이라면 더 많은 속성이 필요할 것입니다.
▶ 스크립터블 오브젝트 ItemBundleBaseData.cs
using UnityEngine;
[CreateAssetMenu(fileName = "ItemBundleBaseData Data",
menuName = "Scriptable Object/ItemBundleBaseData Data", order = int.MaxValue)]
public class ItemBundleBaseData : ScriptableObject
{
[SerializeField]
private Sprite[] ItemIcon; //아이콘에 사용 될 Sprite배열
public Sprite[] itemIcon {get{ return ItemIcon;}}
[SerializeField][TextArea(12,20)] // TextArea는 데이터 가공에 줄바꿈을 인식해야하므로 추가
private string Data; // 콤마 분활 형식의 통텍스트가 입력되는 변수
public string data {get{ return Data;}}
}
코드 작성 후 스크립터블 오브젝트를 만들어서 data에 엑셀 추출 값을 넣고, 해당되는 아이콘을 ItemIcon배열에 순서대로 할당했습니다. 아이콘 Sprite를 담을 때는 Properties를 활용합니다. 첫 번째 필드의 숫자는 아이템의 형태를 가리킵니다.(0 - 투구, 1 - 갑옷, 2 - 장갑, 3 - 신발)
2. 스크립트 구조
ItemBundleData.cs (스크립터블 오브젝트에 있는 데이터를 처리한 뒤 할당할 데이터 구조물)
DataProcessing.cs (데이터 처리 기능을 하는 스크립트)
▶ ItemBundleData
여러 아이템을 포함한 세트 아이템의 구조를 리스트 화한 구조물?? 쓰다 보니 이상하지만 말은 맞는 것 같습니다. 아이템 부위 식별을 위한 enum도 하나 선언하였는데 추후에 엑셀 첫 번째 열에 있는 문자 값을 형 변환하여 할당합니다.
using System.Collections.Generic;
using UnityEngine;
// 아이템 부위 식별을 위한 enum
public enum ItemType { HEAD, BODY, HAND, FOOT}
public class ItemBundleData : MonoBehaviour
{
// 아이템 이름, 형태, 아이콘, 설명을 담을 수 있는 구조
// 아이템
[System.Serializable]
public class Item
{
public string itemName;
public ItemType itemType;
public Sprite itemIcon;
public string itemDesc;
}
// 세트로 구성된 아이템을 담을 수 있는 구조
// 세트아이템
[System.Serializable]
public class ItemBundle
{
public int numberOfBundledItems;
public Item[] items;
}
// 아이템 세트를 담을 수 있는 구조
// 세트아이템 리스트
public List<ItemBundle> itemBundles;
}
▶ DataProcessing
전체적인 과정은 DataProcessing.cs의 게임 오브젝트에 자식으로 ItemBundleData를 인스턴스화 하고 스크립터블 오브젝트 ItemBundleBaseData의 데이터를 처리해서 할당하는 과정입니다. 부분 설명은 주석으로 추가했습니다.
using System.Collections.Generic;
using UnityEngine;
public class DataProcessing : MonoBehaviour
{
public ItemBundleBaseData itemBundleBaseData;
ItemBundleData itemBundleData;
void Start()
{
// ItemBundleData.cs가 컨포넌트로 붙을 GameObject를 생성하여 자식으로 붙입니다.
GameObject ItemBundleDataGO = new GameObject ("ItemBundle DB");
ItemBundleDataGO.transform.position = transform.position;
ItemBundleDataGO.transform.parent = transform;
// 생성한 게임오브젝트에 ItemBundleData 컨포넌트를 추가
itemBundleData = ItemBundleDataGO.AddComponent<ItemBundleData>();
// ItemBundleData의 리스트에 메모리를 할당
itemBundleData.itemBundles = new List<ItemBundleData.ItemBundle>();
// 세트 아이템 숫자 확인
// 세트 아이템은 아이템이 4개로 구성되어 4로 나누기
int itemBundleCount = itemBundleBaseData.data.Split('\n').Length/4;
// 확인 된 세트아이템의 수만큼 메모리를 할당
for(int j = 0; j < itemBundleCount; j++)
{
itemBundleData.itemBundles.Add(new ItemBundleData.ItemBundle());
}
// 생성 된 세트아이템의 Item에 대한 메모리 할당 과정
for(int i = 0; i < itemBundleCount; i++)
{
//세트아이템별 아이템 배열 생성
itemBundleData.itemBundles[i].items = new ItemBundleData.Item[4];
//배열내부 아이템 데이터 메모리 할당
for(int j = 0; j < 4; j++)
{
itemBundleData.itemBundles[i].items[j] = new ItemBundleData.Item();
}
}
// 스크립터블 오브젝트에 입력된 엑셀데이터를 줄별로 짤라 string배열에 할당
string[] row = itemBundleBaseData.data.Split('\n');
string[] columns;
int rowSize = row.Length; // 행의 숫자 확인
int columnSize = row[0].Split(',').Length; // 열의 숫자 확인
int currentColumns = 0; // 현재 열의 커서
int currentBundle = 0; // 현재 세트아이템의 커서
for(int j = 0; j < rowSize/4; j++) // 세트아이템별 한바퀴 돌리는 for 구문
{
for(int k = 0; k < 4; k++) //세트아이템내 아이템을 한바퀴 돌리는 for 구문
{
columns = row[currentColumns].Split(','); // 현재 행을 열별로 짤라 string배열에 할당
//string인 숫자를 ItemType으로 형변환 하여 데이를 할당한다.
itemBundleData.itemBundles[currentBundle].items[k].itemType = ConvertFromString(columns[0]);
itemBundleData.itemBundles[currentBundle].items[k].itemName = columns[1]; //이름 할당
itemBundleData.itemBundles[currentBundle].items[k].itemDesc = columns[2]; //디스크립션 할당
// 아이콘 별도 처리
if(itemBundleBaseData.itemIcon.Length > currentColumns)
itemBundleData.itemBundles[currentBundle].items[k].itemIcon = itemBundleBaseData.itemIcon[currentColumns];
// 열 커서를 +1
if(currentColumns < rowSize)
currentColumns++;
}
// 세트아이템 커서 +1
currentBundle++;
}
}
// string 입력을 받아 정수로 파싱하고 다시 ItemType으로 형변환 하여 리턴
ItemType ConvertFromString(string itemTypeString)
{
return (ItemType)int.Parse(itemTypeString);
}
}
3. 실행 결과
실행해보면 예상했던 데로 ItemBundleData에 정상적으로 값이 할당됩니다.
4. 결론
이번 포스팅에 사용된 코드를 작성하면서 조금? 복잡한 데이터 구조에 메모리 생성 과정이 헷갈리는 걸 느꼈는데 저처럼 이런 부분이 아직 자리잡지 않으신 분들이 보면 도움이 될 거라 생각합니다. 마지막으로 이번 포스팅에 사용되었던 스크립터블 오브젝트의 사용 방법 링크를 남깁니다.
'유니티 이야기' 카테고리의 다른 글
[Unity] 2022 Codeless 인앱 결제(IAP) Tutorial (2) (0) | 2022.08.30 |
---|---|
[Unity] 2022 Codeless 인앱 결재(IAP) Tutorial (1) (0) | 2022.08.29 |
[Unity] json 직렬화 저장 및 읽어오기 + 암호화 (utf8) (1) | 2022.08.27 |
[Unity] CultureInfo를 통해 언어 설정 하기 (0) | 2022.08.26 |
[Unity] 게임 데이터 바이너리 파일로 저장하기 (0) | 2022.08.25 |
댓글