개요
구글시트에서 데이터를 불러오는 작업을 하던중
데이터를 불러와서 기존 ScriptableObject에 덮어씌우기만 하지말고
있다면 덮어씌우고 없다면 생성하는 시스템을 만들고싶어졌다.
그래서 작업을 진행했고 해당내용을 기록한다.
사용한 에셋
https://assetstore.unity.com/packages/tools/utilities/google-sheets-to-unity-73410?locale=ko-KR
해당에셋의 초기세팅은 국내 블로그들에 충분히 많이 있기때문에 이 글에서는 생략한다.
본문
우선 시트의 데이터는 위와같은 속성들을 가지고있다.
구현한 코드와 인스펙터에서의 속성은 아래와 같다.
- 코드
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GoogleSheetsToUnity;
using Sirenix.OdinInspector;
using UnityEditor;
using Jamong;
[CreateAssetMenu(fileName = "TowerDataSheetLoader", menuName = "SheetLoader/TowerDataSheetLoader")]
public class TowerDataSheetLoader : ScriptableObject
{
[SerializeField]
private string associatedSheet;
[SerializeField]
private string associatedWorkSheet;
[SerializeField]
private List<TowerData> existDatas;
[SerializeField]
private int lastIndex;
[SerializeField]
private string FilePath => "Assets/04.Data/TowerData";
[Button]
public void CreateDatas()
{
SpreadsheetManager.Read(new GSTU_Search(associatedSheet, associatedWorkSheet), CreateDatas_OnLoaded);
}
public void CreateDatas_OnLoaded(GstuSpreadSheet ss)
{
#if UNITY_EDITOR
for (int i = 0; i <= lastIndex; i++)
{
if (!ss.rows.ContainsKey(i.ToString()))
break;
int towerIndex = int.Parse(ss[i.ToString(), "타워ID"].value.ToString());
string towerName = ss[i.ToString(), "이름"].value.ToString();
TowerData td = existDatas.Find(t => t.TowerIndex == towerIndex);
if(td == null)
{
td = CreateInstance<TowerData>();
AssetDatabase.Refresh();
AssetDatabase.CreateAsset(td, string.Format("{0}/{1}.{2}.asset", FilePath, towerIndex.ToString("00"), towerName));
}
td.TowerIndex = towerIndex;
// 등급
switch(ss[i.ToString(), "등급"].value.ToString())
{
case "노말":
td.TowerGrade = Enums.TowerGrade.Normal;
break;
case "매직":
td.TowerGrade = Enums.TowerGrade.Magic;
break;
case "레어":
td.TowerGrade = Enums.TowerGrade.Rare;
break;
case "유니크":
td.TowerGrade = Enums.TowerGrade.Unique;
break;
case "에픽":
td.TowerGrade = Enums.TowerGrade.Epic;
break;
}
// 속성
switch (ss[i.ToString(), "속성"].value.ToString())
{
case "물리":
td.ElementType = Enums.ElementType.Physical;
break;
case "폭발":
td.ElementType = Enums.ElementType.Explosion;
break;
case "빔":
td.ElementType = Enums.ElementType.Beam;
break;
}
// 공격타입
switch(ss[i.ToString(), "공격타입"].value.ToString())
{
case "어택커":
td.AttackType = Enums.AttackType.Attacker;
break;
case "디펜더":
td.AttackType = Enums.AttackType.Defender;
break;
case "해커":
td.AttackType = Enums.AttackType.Hacker;
break;
case "디펜더/해커":
td.AttackType = Enums.AttackType.DefenderHacker;
break;
default:
Debug.LogError("오타발견 " + ss[i.ToString(), "공격타입"].value.ToString());
break;
}
// 단일/범위
switch(ss[i.ToString(), "단일/범위"].value.ToString())
{
case "단일":
td.NormalAttackTargetSelectType = Enums.TargetSelectType.Single;
break;
case "범위":
td.NormalAttackTargetSelectType = Enums.TargetSelectType.Splash;
break;
}
// 공격력
td.AttackPower = int.Parse(ss[i.ToString(), "기본공격력"].value.ToString());
// 해킹 공격력
string hackingPowerString = ss[i.ToString(), "해킹 공격력"].value.ToString();
if(string.IsNullOrEmpty(hackingPowerString))
{
td.HackingPower = 0;
}
else
{
td.HackingPower = int.Parse(hackingPowerString);
}
// 넉백
string knockBackPowerString = ss[i.ToString(), "넉백"].value.ToString();
if(string.IsNullOrEmpty(knockBackPowerString))
{
td.KnockbackPower = 0;
}
else
{
td.KnockbackPower = float.Parse(knockBackPowerString);
}
// 명중률
string aimingRateString = ss[i.ToString(), "명중률"].value.ToString();
aimingRateString = aimingRateString.Replace("%", "");
td.AimingRate = float.Parse(aimingRateString) * 0.01f;
// 공속
td.AttackSpeed = float.Parse(ss[i.ToString(), "공속"].value.ToString());
// 사거리
td.AttackRange = float.Parse(ss[i.ToString(), "사거리"].value.ToString());
EditorUtility.SetDirty(td);
}
AssetDatabase.Refresh();
AssetDatabase.SaveAssets();
#endif
}
}
- 인스펙터
- 코드의 부연설명
1. [Button]?
우선 함수를 인스펙터의 버튼으로 보여주는 위 기능은 유로에셋인 오딘 인스펙터를 사용하였다.
오딘 인스펙터없이 함수를 버튼으로 만들려면 에디터 스크립트를 직접 작성해도 되지만 번거롭기 때문에
https://assetstore.unity.com/packages/tools/utilities/naughtyattributes-129996
이 무료에셋을 사용하면 된다.
2. 전처리문
코드를 보면 UNITY_EDITOR전처리문으로 감싸놓은것을 확인할 수 있는데
이는 AssetDatabase클래스와 EditorUtility같은 UnityEditor 네임스페이스 아래의 클래스들은
빌드에 포함되면 에러가 난다.
그래서 빌드에 포함되지않게 전처리문으로 감싸놓았다.
3. 존재하는 데이터라면 덮어씌우고 없다면 생성
미리 존재하는 데이터들을 리스트에 담아놓고 거기서 고유한 속성으로 찾아본 후
없는데이터라면 에셋을 생성하게 구현하였다.
그렇게 저장한 에셋을 코드 마지막부분에 저장한다.
EditorUtility.SetDirty(); 함수는 새로 생성한 데이터라면 굳이 실행시킬 필요없지만
기존걸 덮어씌우는 작업이라면 꼭 실행해줘야한다.
그렇지 않으면 덮어씌운데이터가 프로젝트저장을 했어도
프로젝트를 재실행하면 리셋돼버린다.
결론
이렇게 인스펙터에서 버튼한번 클릭으로
구글시트에서 데이터들을 불러와 스크립터블 오브젝트로 생성하는 시스템을 구현해봤다.
시간이 더 있었다면 상속을 활용해서 베이스 클래스를 만들고
데이터의 종류에따라(예를들면 캐릭터, 몬스터, 아이템) 베이스를 상속받아 속성값만 정의하게
만들었으면 더 좋았겠지만
프로토타입 프로젝트였고 시간이 촉박했기에 이정도까지만 구현하였다.
'Unity > C#' 카테고리의 다른 글
[C#] DateTime에서 남은시간 계산하기 (0) | 2023.02.25 |
---|---|
[Unity] Builder패턴으로 팝업 시스템 구현 (0) | 2023.02.16 |
[Unity, C#] 오브젝트 풀링 (0) | 2022.10.04 |
[Unity, C#] 박스 콜라이더안에 랜덤포인트 구하기 (0) | 2022.10.01 |
[C#] Enum에 Contains인지 확인하기, String을 Enum으로 변환하기 (0) | 2022.07.29 |