플레이어가 몬스터를 죽이고 레벨 업을 하기 전에 먼저 코드를 수정한다.
지금까지 체력이 주는 상황을 대미지를 주는 주체의 컨트롤러에서 OnHitEvent()함수를 통해 처리했는데 사실 체력이 주는 상황은 대미지를 받는 주체의 컨트롤러에서 처리하는 것이 합리적이다.
이유는 대미지를 받는 주체의 상태에 따라 받는 대미지가 완전히 달라지는데(버프, 디버프나 상태이상 등) 그걸 대미지를 주는 주체에서 받는 주체의 정보를 가지고 와서 처리하기 보다는 대미지를 받는 주체에서 처리하는 것이 훨씬 간단하기 때문이다.
public class Stat : MonoBehaviour
{
...
public virtual void OnAttacked(Stat attacker)
{
int damage = Mathf.Max(0, attacker.Attack - Defense);
Hp -= damage;
if (Hp <= 0)
{
Hp = 0;
OnDead();
}
}
protected virtual void OnDead()
{
Managers.Game.Despawn(gameObject);
}
}
우선 체력 게이지를 위한 함수를 Stat 클래스에 작성하도록 한다.
void OnHitEvent()
{
if (_lockTarget != null)
{
Stat targetStat = _lockTarget.GetComponent<Stat>();
targetStat.OnAttacked(_stat);
...
}
}
이후에 몬스터, 플레이어 컨트롤러의 OnHitEvent()를 수정해준다.
이러면 targetStat, 즉 대미지를 받는 주체의 컴포넌트에서 받는 대미지를 계산해서 체력을 줄이게 된다.
{
"stats": [
{
"level": "1",
"maxHp": "200",
"attack": "20",
"totalExp": "0"
},
{
"level": "2",
"maxHp": "250",
"attack": "25",
"totalExp": "10"
},
{
"level": "3",
"maxHp": "300",
"attack": "30",
"totalExp": "20"
}
]
}
우선 StatData.json 파일을 수정한다. 레벨업을 위한 총 경험치를 추가했다.
private void Start()
{
_level = 1;
_exp = 0;
_defense = 5;
_moveSpeed = 5.0f;
_gold = 0;
SetStat(_level);
}
public void SetStat(int level)
{
Dictionary<int, Data.Stat> dict = Managers.Data.StatDict;
Data.Stat stat = dict[level];
_hp = stat.maxHp;
_maxHp = stat.maxHp;
_attack = stat.attack;
}
그리고 PlayerStat 클래스의 함수들을 수정한다. 원래는 일일히 스탯의 값을 넣어줬지만 이제는 DataManager를 통해 스탯값을 불러오도록 한다.
protected virtual void OnDead(Stat attacker)
{
PlayerStat playerStat = attacker as PlayerStat;
if (playerStat != null) // PlayerStat이 null이라는 것은 attacker가 몬스터라는 의미
{
playerStat.Exp += 10;
}
Managers.Game.Despawn(gameObject);
}
그리고 Stat 클래스의 OnDead() 함수를 수정한다. 몬스터를 죽일 때마다 10의 경험치를 얻도록 하였다.
이제 경험치를 얻도록 했으니 얻은 경험치로 레벨업을 할 수 있는지 체크해야 한다. 이를 어느 함수에서 체크할 지가 문제인데, PlayerStat 클래스의 Exp를 Set하는 프로퍼티에서 체크하는 것이 가장 합리적으로 보인다.
만약에 경험치를 올리는 함수에서 레벨업 체크를 한다면 모든 경험치를 올리는 함수에서 일일히 레벨업 체크를 해줘야 할 것이다. 그러면 경험치를 올리는 상황이 많아질수록 코드가 복잡해지게 된다. 따라서 Exp를 Set하는 프로퍼티에서 Exp를 올림과 동시에 레벨업 체크를 하는 것이 효율적일 것이다.
public int Exp
{
get { return _exp; }
set
{
_exp = value;
int level = Level;
while (true)
{
Data.Stat stat;
if (Managers.Data.StatDict.TryGetValue(level + 1, out stat) == false)
break; // 다음 레벨의 스탯을 불러온다. 만약 다음 레벨의 스탯이 없다면 break
if (_exp < stat.totalExp)
break; // 다음 레벨의 스탯을 불러온다. 만약 다음 레벨의 totalExp보다 현재 exp가 낮다면 break
level++; // 다음 레벨의 totalExp보다 현재 exp가 더 높다면 레벨을 올린다.
} // 이를 더는 레벨업을 못하는 상황이 될 때까지 반복한다.
if (level != Level)
{
Debug.Log("Level Up!");
Level = level; // 위 반복문을 통해 레벨이 변화한 상태라면 다시 프로퍼티를 사용해 레벨을 저장한다.
SetStat(level); // 변화된 레벨에 스탯을 맞춘다.
}
}
}
이후에 게임을 실행해보니 정상적으로 작동하는것을 확인했다.