오늘부터 개인 과제를 해보았습니다. 일단 강의는 다 듣지는 못했지만
2D부분은 다 들었기 때문에 강의에서 배운 스크립터블 오브젝트를 이용하여
캐릭터의 스탯을 보여주는 기능을 만들어보았습니다.
스크립터블 오브젝트 활용
일단 캐릭터에 공통적으로 들어가는 스탯을 StatSo라는 스크립터블 오브젝트로 만들어보았습니다.
StatSO.cs
[CreateAssetMenu(fileName = "DefaultStatData", menuName = "SpartaDungeonUnity/Stats/Default", order = 0)] public class StatSO : ScriptableObject { [Header("Stat Info")] public float Atk; public float Def; public float Health; public float Critical; }
그리고 직업마다 스탯을 다르게 하고 싶어서 이 StatSO를 상속 받는 JobStatData를 만들었습니다.
JobStatData.cs
[CreateAssetMenu(fileName = "JobStatData", menuName = "SpartaDungeonUnity/Stats/Job", order = 1)] public class JobStatData : StatSO { [Header("Job Stat Data")] public string JobName; public string Descript; }
일단 JobStatData로 스크립터블 오브젝트를 하나 만들었습니다.
그리고 캐릭터의 스탯을 가지고 있는 CharacterStats스크립트를 만듭니다.
CharacterStats.cs
public enum StatsChangeType { Add, Multiple, Override, } [Serializable] public class CharacterStats { public StatsChangeType statsChangeType; public string characterName; public int level; public int exp; public int gold; public StatSO statSO; }
캐릭터는 고유의 이름, 레벨, 경험치, 골드, 그리고 스탯 데이터를 가지고 있고 이 스탯들을 더하거나 곱하거나 덮어씌울 수 있습니다.
그리고 이 데이터를 관리하는 CharacterStatsHandler를 만들고 플레이어에게 부여해줍니다.
CharacterStatsHandler.cs
public class CharacterStatsHandler : MonoBehaviour { [SerializeField] private CharacterStats baseStats; public CharacterStats CurrentStats { get; private set; } public event Action OnLevelUP; public List<CharacterStats> statsModifiers = new List<CharacterStats>(); private void Awake() { UpdateCharacterStats(); } private void UpdateCharacterStats() { StatSO statSO = null; if (baseStats.statSO != null) { statSO = Instantiate(baseStats.statSO); } CurrentStats = new CharacterStats { statSO = statSO }; // TODO CurrentStats.statsChangeType = baseStats.statsChangeType; CurrentStats.characterName = baseStats.characterName; CurrentStats.level = baseStats.level; CurrentStats.exp = baseStats.exp; CurrentStats.gold = baseStats.gold; while (true) { if(CurrentStats.exp < CurrentStats.level +2) { break; } CurrentStats.exp -= CurrentStats.level + 2; CurrentStats.level++; OnLevelUP?.Invoke(); } } }
이것을 플레이어에게 붙혀주면 이름, 레벨 가지고있는 경험치, 골드 등을 초기화할 수 있습니다.
그리고 게임 매니저를 만들어서 화면에 있는 UI들에게 스탯이나 이름 등의 값을 전달해 주었습니다.
GameManager.cs
public class GameManager : MonoBehaviour { public static GameManager instance; public GameObject Player { get; private set; } [SerializeField] private string playerTag = "Player"; private CharacterStatsHandler _statsHandler; [SerializeField] private TextMeshProUGUI jobText; [SerializeField] private TextMeshProUGUI nameText; [SerializeField] private TextMeshProUGUI levelText; [SerializeField] private TextMeshProUGUI expText; [SerializeField] private TextMeshProUGUI descriptText; [SerializeField] private TextMeshProUGUI goldText; [SerializeField] private Slider expGaugeSlider; private void Awake() { instance = this; Player = GameObject.FindGameObjectWithTag(playerTag); _statsHandler = Player.GetComponent<CharacterStatsHandler>(); _statsHandler.OnLevelUP += UpdateExpUI; } void Start() { JobStatData jobStatData = _statsHandler.CurrentStats.statSO as JobStatData; jobText.text = jobStatData.JobName; nameText.text = _statsHandler.CurrentStats.characterName; levelText.text = _statsHandler.CurrentStats.level.ToString(); expText.text = $"{_statsHandler.CurrentStats.exp} / {_statsHandler.CurrentStats.level + 2}"; descriptText.text = jobStatData.Descript; goldText.text = _statsHandler.CurrentStats.gold.ToString(); } private void UpdateExpUI() { float gauge =_statsHandler.CurrentStats.exp / (float)(_statsHandler.CurrentStats.level + 2); expGaugeSlider.value = gauge; } }
게임 매니저에 이렇게 데이터를 많이 연결 시켜놓는게 좋아보이지는 않지만 일단 보이는데는 문제 없으므로 이렇게 해놓게 되었습니다.
결과창입니다.
원하는 값들이 잘 출력이 되는 모습니다. UI에 값을 전달하는 방법이 더 효율적인 것이 있는지 좀 더 찾아보겠습니다.
스크립터블 오브젝트
유니티에서 제공하는 대량의 데이터를 저장하는 데 사용할 수 있는 데이터 컨테이너.
프로젝트의 메모리 사용을 줄일 수 있음.
모노비헤이비어(MonoBehaviour) 스크립트에 변경되지 않는 데이터를 저장하는 프리팹을 사용하는 프로젝트에서 유용.
작동 방식
변경되지 않는 데이터를 사용하는 프리팹의 데이터를 일반 변수로 구현할 경우, 인스턴스화 할때마다 프리팹에 이 데이터에 대한 자체 사본이 생성됨.
스크립터블 오브젝트를 사용하면 메모리에 스크립터블 오브젝트의 데이터 사본만을 저장하고 이를 참조하는 방식으로 작동.
모노비헤이비어와 달리, 게임 오브젝트에 컴포넌트로 부착할 수 없고, 프로젝트에 에셋으로 저장.
09:00 ~ 10:00 : 알고리즘 코드카타
10:00 ~ 10:30 : 팀 회의
10:30 ~ 14:00 :
12시-1시: 점심식사
14:00 ~ 18:00
6시-7시: 저녁식사
19:00 ~ 20:00 : 집중 코딩 시간 부족한 부분 해결해보기
20:00 ~ 21:00: TIL 작성, 마무리 회고 진행
21:00 : 내일은 위한 휴식!