2024. 7. 14. 18:45ㆍSTAC 개발일지
일지라면서 매일 쓰는 게 아닌 이상한 모양새지만 어쨌든 일지이다.
오늘은 플레이어와 적들이 모두 가지는 Agent에게 필요한 Status를 구현할 것이다.
게임에서 스테이터스는 중요하다.
플레이어의 성장에 깊게 관여하고 이를 유저가 직접 느끼도록 만들어주는 가장 대표적이고 확실한 수단이다.
그래서 이 기능에 대한 개발에서는 더욱 신중하게 코드를 짜야한다.
우선 Stat이라는 클래스를 하나 만들 것이다.
[Serializable]
public class Stat
{
// 무언가 코드
}
이 Stat이라는 놈을 묶어서 Status를 구성할 것이다.
이러한 수치들을 SO로 관리할 것이기 때문에
Agent에서 관리하기 편하게 AgentStat이라는 SO클래스도 하나 만들어서
위에 만들었던 Stat들을 몇 개 추가해 준다.
public class AgentStat : ScriptableObject
{
public Stat damage;
public Stat health;
public Stat moveSpeed;
}
나중에 가져오기 편하게 타입 Enum도 하나 만들어둔다
public enum StatType
{
Damage,
MaxHealth,
MoveSpeed
}
굳이 Stat을 나눠놓은 데에는 이유가 있는데,
스킬을 사용하거나 버프에 걸렸을 때 공격력이 증가하고 레벨업을 했을 때 체력이 증가하는 것과
같은 기능들을 구현함에 있어서 좀 더 편하게 접근할 수 있도록 구조를 짤 것이다.
(다른 기능들에서 유동적으로 변경하기 편하게 만들겠다는 뜻)
일단 지금 만드는 이 Stat은 일단 기본값을 가지고 있고 Modifier이란 놈을 건드려서 값을 증감시키고
GetValue()를 통해 통합된 수치를 반환하는 방식으로 만들 것이다.
public class Stat
{
[SerializeField] private int _baseValue;
public List<int> modifiers;
public int GetValue()
{
int total = _baseValue;
foreach (int value in modifiers)
{
total += value;
}
return total;
}
public void AddModifier(int value)
{
if(value != 0)
modifiers.Add(value);
}
public void RemoveModifier(int value)
{
if (value != 0)
modifiers.Remove(value);
}
public void SetDefaultValue(int value)
{
if (value != 0)
{
_baseValue = value;
}
}
}
따라서 위와 같이 코드를 짤 수 있다.
사용할 때는 AddModifer로 추가적인 값을 적용하고 RemoveModifer로 뺄 수 있다.
또한 modifers가 적용된 값을 GetValue() 를 통해 가져온다.
List<int>으로 구현했기 때문에 값 추가및 변동에 문제가 없고 중복값 또한 문제되지 않는다.
이렇게 만들어진 Stat들을 AgentStat으로 묶어서 Agent 내에서 관리할 것이다.
AgentStat에는 Stat의 기능들을 실질적으로 활용할 수 있게 해주어야 한다.
나중에 가져오기 편하게 Enum과 Stat으로 이루어진 Dictionary를 만들어 두자
그리고 Dictionary에 Stat에 해당하는 애들을 넣어줘야 하는데
Add로 하나하나 넣어주게 되면 나중에 스테이터스가 종류가 늘어나게 되면 그거에 따라서
하드코딩을 해줘야 하기 때문에 리플렉션을 통해 간단히 해결한다.
코드는 이러하다
protected virtual void OnEnable()
{
_statDictionary = new Dictionary<StatType, Stat>();
Type agentStatType = typeof(AgentStat); //이 클래스의 타입 정보를 불러오기
foreach(StatType typeEnum in Enum.GetValues(typeof(StatType)))
{
try
{ // 혹시나 Enum과 Stat개수를 매칭시키지 않았거나 여러 예외를위해 예외처리를 해줘야함
string fieldName = LowerFirstChar(typeEnum.ToString()); // Enum의 첫글자를 lower(소문자)로 바꿔서
FieldInfo statField = agentStatType.GetField(fieldName); // 필드에 해당 이름을 가진 필드를 가져옴
_statDictionary.Add(typeEnum, statField.GetValue(this) as Stat); // 그리고 Dictionary에 추가
}catch(Exception ex)
{
Debug.LogError($"There are no stat - {typeEnum.ToString()} {ex.Message}");
}
}
}
private string LowerFirstChar(string input)
=> $"{char.ToLower(input[0])}{input.Substring(1)}";
따라서 이렇게 해주면 AgentStat에서 언제든지 스텟에 간섭할 수 있다
외부에서 수정하기 쉽도록 몇가지 작업을 더 해주자면
이렇게 해서 Type과 값을 통해 바로바로 Modifier를 등록하고 해제할 수 있다.
다만 없는 값을 뺄수도 있으니 예외처리를 추가로 해야될 수도 있다.
이는 Dictionary의 TryGetValue함수로 간단하게 막을 수 있다.
이렇게 구현된 AgentStat을 Agent가 가지고 있도록 하고
Agent를 상속받거나 Agent를 통해 구현되는 하위 기능들은 이 AgentStat을 가져옴으로써
스테이터스를 실시간으로 변경하고 적용시킬 수 있다.
이제 그 하위기능들 구현하러 가야한다.
히히