스텟을 구현하는 것은 크게 어렵지는 않았다.
하지만 이 스텟을 어떻게 관리할지가 제대로 판단되지않아 시간이 좀 걸렸다.
[Serializable]
public class Stat
{
[SerializeField] private int baseValue;
public List<int> modifiers;
public int GetValue()
{
int totalValue = baseValue;
foreach (int modifier in modifiers)
{
totalValue += modifier;
}
return totalValue;
}
public void AddModifier(int value) => modifiers.Add(value);
public void RemoveModifier(int value) => modifiers.Remove(value);
public void SetBaseValue(int value)
{
baseValue = value;
}
}
Stat이란 클래스이고 스텟으로서 쓰일 예정이다.
List에 추가능력치를 추가할 수도있고 추가된 능력치를 볼 수도있으며
GetValue함수로 추가 능력치를 탐색하여 모두 합한 능력치를 가져올 수도있다.
스텟을 지정하고 관리하는 클래스이다.
public class CharacterStats : MonoBehaviour
{
[Header("Offensive stat")]
public Stat damage;
public Stat critChance;
public Stat critDamage;
[Header("Defensive stat")]
public Stat maxHealth;
public Stat defense;
[Header("Primary stat")]
public Stat strength;
public Stat agility;
public Stat vitality;
public Stat luck;
public int currentHealth;
public Action onHealthChanged;
public void DoDamage(CharacterStats enemyStats, float attackPower = 1) =>
StatsCalculate.CalculateTotalDamage(this, enemyStats,attackPower);
private void Start()
{
currentHealth = StatsCalculator.StatsCalculate.GetMaxHealth(this);
}
public void DecreaseHealth(int amount)
{
currentHealth -= amount;
if(currentHealth <= 0)
{
Die();
}
if(onHealthChanged != null)
onHealthChanged();
}
protected virtual void Die()
{
Debug.Log("죽었습니다!");
}
}
객체와 객체의 의존성을 줄이고 함수 깊이가 깊어지기를 방지 하기위해 중재자를 두었다.
using UnityEngine;
using UnityEngine.TextCore.Text;
namespace StatsCalculator
{
public static class StatsCalculate
{
static int strDamage = 1;
static int strMaxHealth = 1;
static int agiDamage = 1;
static int agiCritDamage = 1;
static int agiCritChance = 1;
static int vitMaxHealth = 1;
static int vitDefense = 1;
public static void CalculateTotalDamage(CharacterStats myStats, CharacterStats enemyStats, float attackPower)
{
float totalDamage;
totalDamage = GetDamage(myStats) * attackPower;
if(CanCrit(myStats))
{
totalDamage *= (GetCritDamage(myStats) / 100);
}
totalDamage = CheckTargetDefense(totalDamage, enemyStats);
enemyStats.DecreaseHealth((int)totalDamage);
}
public static bool CanCrit(CharacterStats myStats)
{
if(GetCritChance(myStats) > Random.Range(0,100))
{
return true;
}
return false;
}
public static float CheckTargetDefense(float totalDamage,CharacterStats enemyStats)
{
totalDamage -= totalDamage * (GetDefense(enemyStats) / (GetDefense(enemyStats) + 50));
return totalDamage;
}
#region Get Stats
public static int GetDamage(CharacterStats Stats)
{
return Stats.damage.GetValue() +
(Stats.strength.GetValue() * strDamage + Stats.agility.GetValue() * agiDamage);
}
public static float GetDefense(CharacterStats stats)
{
return stats.defense.GetValue() +
stats.vitality.GetValue() * vitDefense;
}
public static int GetMaxHealth(CharacterStats stats)
{
return stats.maxHealth.GetValue() +
stats.vitality.GetValue() * vitMaxHealth + stats.strength.GetValue() * strMaxHealth;
}
public static int GetCritChance(CharacterStats stats)
{
return stats.critChance.GetValue() +
stats.agility.GetValue() * agiCritChance;
}
public static int GetCritDamage(CharacterStats stats)
{
return stats.critDamage.GetValue() +
stats.agility.GetValue() * agiCritDamage;
}
#endregion
}
}
객체와 객체가 서로 의존성을 가지는 것은 지금은 문제가 되지않지만
나중에 다양한 시스템이 추가되고 그 시스템으로인해 서로의 소통 수가 늘어난다면 함수의 깊이는 더 깊어질
것이고 매개변수도 더 많이 전달이 될 것이기에 불안정할 수 있다고 생각하여 중재자를 두었다.
가해자가 중제자에게 피해자를 공격해줘라고 떠넘기면 중제자는 스텟에 대한 계산들을 끝낸 이 후
피해자의 체력을 깎는 함수는 호출시킨다.
static Class이므로 계산식을 자유롭게 호출할 수 있는 것도 장점이다.
하지만
static으로 선언한 만큼 이 중재자는 내가 사용하지 않을때에도 어딘가에 존재하여 메모리 누수를 일으킨다.
그리고 씬이 전환될때 만약 계산이 이루어지는 도중이었다면 가해자와 피해자의 정보를 그대로 가진채로
씬이 전환 될 수 있기 때문에 완전히 안전하지는 않다.
이 계산식은 아직 기본적인 계산식이라 무겁지는 않지만 나중에 반격, 패링, 반사 등 복잡한 식이 들어가게 되면
이 중재자 자체가 무거워지게 되는 현상이 발생하고 이로인해 중재자에 의한 메모리 누수가 커질 수 있다.
그래서?
제네릭 싱글톤으로 하여 관리하면 처음으로 가져올때 생성되기에 더 효율적이다.
하지만 이 제네릭도 처음가져오면 쭉 유지되기에 이런 상황에서는 별로 효율이 좋지는 않다고 생각한다.
그럼 남은건 내 생각엔 싱글톤중 WeakReference(약한참조) 싱글톤인데 바꿀 가능성도 있겠다.
현재 시스템은 설계단계로 언제든 바뀔 수 있지만 현상태의 동작은 잘 작동되는것으로 테스트 했기때문에
동작 자체의 변경은 크게 없을 것이고 추가를 한다던지 아니면 중재자를 더 효육적으로 사용하는 방법을 생각 해볼 수는 있겠다.