플레이어의 스탯을 관리하기위에 구현하는 스탯 찍기 시스템은 게임 진행과 함께 플레이어의 동기 부여와 캐릭터 발전을 장려해줌으로써 게임의 재미와 오래 즐길 수 있는 장점을 줄 수 있습니다. 지난 글에 이어 이번 글에서는 플레이어의 스탯을 관리하는 시스템을 구현하고 이 내용을 정리하였습니다.
💬 목차
[📌현재 글] 2. [유니티] 스탯 시스템(2) - 플레이어 스탯관리
💬 서론
- 이 글은 게임을 대부분 완성한 상태에서 정리하고자 쓰는 글이기에 구현하는 클래스의 핵심 이외의 다른 클래스의 기능들이 일부 포함되어 있습니다.
- 이 기능은 [직전 글]에서부터 이어지는 글입니다.
✅ 구현
- 플레이어의 레벨업을 통해 받은 스탯 포인트를 기반으로 플레이어의 기본스탯을 향상시킬 수 있는 기능을 구현합니다.
· StatManager
/// <summary>
/// 플레이어의 스탯 포인트를 관리하는 매니저
/// </summary>
public class StatManager : Singleton<StatManager>
{
public static bool IsStatDialogEnable { private set; get; } = false;
[Header("플레이어의 스탯")]
[SerializeField] private PlayerController mPlayerController;
// 레벨 영역
[Header("레벨, 스탯포인트")]
[SerializeField] private TextMeshProUGUI mLvLabel; // 레벨 레이블
[SerializeField] private TextMeshProUGUI mStatPtLabel; // 스탯포인트 레이블
[HideInInspector] public int CurrentStatPoint { private set; get; } = 1; // 현재 스탯 포인트 (1로 시작하도록 의도)
[Space(30)]
[Header("스탯(현재)")]
[SerializeField] private TextMeshProUGUI mHpCurrentLabel; // 현재 체력 레이블
[SerializeField] private TextMeshProUGUI mMpCurrentLabel; // 현재 마나 레이블
[SerializeField] private TextMeshProUGUI mAttackCurrentLabel; // 현재 공격 레이블
[SerializeField] private TextMeshProUGUI mSpeedCurrentLabel; // 현재 속도 레이블
[SerializeField] private TextMeshProUGUI mDefenseCurrentLabel; // 현재 방어력 레이블
[Space(30)]
[Header("스탯(최대)")]
[SerializeField] private TextMeshProUGUI mHpMaxLabel; // 최대 체력 레이블
[SerializeField] private TextMeshProUGUI mMpMaxLabel; // 최대 마나 레이블
[Space(30)]
[Header("스탯 증가 버튼들")]
[SerializeField] private GameObject[] mStatButtons; // 스탯을 찍는 버튼들
[Space(30)]
[Header("토글할 스탯 UI 오브젝트")]
[SerializeField] private GameObject mStatUiGo;
private void Awake()
{
// 초기화시 전역 활성화상태 해제
StatManager.IsStatDialogEnable = false;
}
private void Update()
{
if (Input.GetKeyDown(KeyManager.Instance.GetKeyCode("Stat")))
{
this.ToggleStatDialog();
}
}
public void LevelUp()
{
mPlayerController.StatData.UpgradeBaseStat(StatType.LEVEL); // 레벨 1 증가
CurrentStatPoint += 2; // 스탯포인트 2 증가
ParticleController particle = ParticlePool.Instance.GetFromPool(PARTICLE_TYPE.LEVEL_UP);
particle.transform.SetParent(mPlayerController.transform);
particle.transform.localPosition = Vector3.up;
SoundManager.Instance.PlaySound2D("Levelup" + SoundManager.Range(1, 2));
UpdateStatTexts();
}
/// <summary>
/// 세이브파일로부터 로드된경우 스탯 포인트를 설정
/// </summary>
public void LoadFromData(PlayerGameData playerGameData)
{
this.CurrentStatPoint = playerGameData.statPoint;
}
#region 스탯 창 열고닫기
/// <summary>
/// 스탯 다이얼로그 창을 토글합니다.
/// </summary>
public void ToggleStatDialog()
{
if (mStatUiGo.activeSelf)
TryCloseStatDialog();
else
TryOpenStatDialog();
}
private void TryOpenStatDialog()
{
UtilityManager.UnlockCursor();
mStatUiGo.SetActive(true);
IsStatDialogEnable = true;
transform.SetAsLastSibling();
UpdateStatTexts();
}
private void TryCloseStatDialog()
{
IsStatDialogEnable = false;
UtilityManager.TryLockCursor();
mStatUiGo.SetActive(false);
}
#endregion
#region 스탯 창 업데이트
public void UpdateStatTexts()
{
foreach (GameObject go in mStatButtons)
go.SetActive(CurrentStatPoint > 0);
mLvLabel.text = mPlayerController.StatData.level.ToString();
mStatPtLabel.text = CurrentStatPoint.ToString();
UpdateCurrentHpStat();
UpdateCurrentMpStat();
mHpMaxLabel.text = $"/{mPlayerController.StatData.hpMax.ToString()}";
mMpMaxLabel.text = $"/{mPlayerController.StatData.mpMax.ToString()}";
mAttackCurrentLabel.text = $"{mPlayerController.StatData.baseAttack} <size=9>+{mPlayerController.StatData.AttackCurrent - mPlayerController.StatData.baseAttack}";
mSpeedCurrentLabel.text = $"{mPlayerController.StatData.baseMovementSpeed} <size=9>+{mPlayerController.StatData.MovementSpeedCurrent - mPlayerController.StatData.baseMovementSpeed}";
mDefenseCurrentLabel.text = $"{mPlayerController.StatData.baseDefense} <size=9>+{mPlayerController.StatData.DefenseCurrent - mPlayerController.StatData.baseDefense}";
}
public void UpdateCurrentHpStat()
{
mHpCurrentLabel.text = mPlayerController.StatData.HpCurrent.ToString();
}
public void UpdateCurrentMpStat()
{
mMpCurrentLabel.text = mPlayerController.StatData.HpCurrent.ToString();
}
#endregion
#region Ui 입력
public void BTN_UpgradeStat(int statIndex)
{
// 스탯 포인트 1 감소
--CurrentStatPoint;
// 스탯 증가
mPlayerController.StatData.UpgradeBaseStat((StatType)statIndex);
// 스탯 Ui 갱신
UpdateStatTexts();
}
#endregion
}
public static bool IsStatDialogEnable { private set; get; } = false;
- 현재 스탯 다이얼로그 창이 활성화 되어있는지 확인하기위해 사용합니다.
[Header("플레이어의 스탯")]
[SerializeField] private PlayerController mPlayerController;
- 스탯 매니저는 플레이어의 레벨업으로 기본 스탯을 강화하기위해 디자인 되었습니다.
- 플레이어 컨트롤러의 스탯데이터를 레퍼런스하기위해 플레이어를 직접 가져옵니다.
// 레벨 영역
[Header("레벨, 스탯포인트")]
[SerializeField] private TextMeshProUGUI mLvLabel; // 레벨 레이블
[SerializeField] private TextMeshProUGUI mStatPtLabel; // 스탯포인트 레이블
[HideInInspector] public int CurrentStatPoint { private set; get; } = 1; // 현재 스탯 포인트 (1로 시작하도록 의도)
- 플레이어의 현재 레벨을 보여주고, 스탯포인트를 보여주는 라벨을 위치시킵니다.
- 아래 그림의 빨간색 영역이 이에 해당됩니다.
[Space(30)]
[Header("스탯(현재)")]
[SerializeField] private TextMeshProUGUI mHpCurrentLabel; // 현재 체력 레이블
[SerializeField] private TextMeshProUGUI mMpCurrentLabel; // 현재 마나 레이블
[SerializeField] private TextMeshProUGUI mAttackCurrentLabel; // 현재 공격 레이블
[SerializeField] private TextMeshProUGUI mSpeedCurrentLabel; // 현재 속도 레이블
[SerializeField] private TextMeshProUGUI mDefenseCurrentLabel; // 현재 방어력 레이블
- 현재 스탯 상태를 보여주는 레이블들을 가져옵니다.
- 아래 그림의 빨간색 영역이 이에 해당됩니다.
[Space(30)]
[Header("스탯(최대)")]
[SerializeField] private TextMeshProUGUI mHpMaxLabel; // 최대 체력 레이블
[SerializeField] private TextMeshProUGUI mMpMaxLabel; // 최대 마나 레이블
- 최대 체력과 마나를 보여주는 레이블을 가져옵니다.
- 아래 그림의 빨간색 영역이 이에 해당됩니다.
[Space(30)]
[Header("스탯 증가 버튼들")]
[SerializeField] private GameObject[] mStatButtons; // 스탯을 찍는 버튼들
- 스탯을 찍기 위한 버튼들을 가져옵니다.
- 스탯포인트가 있을때 스탯을 찍을 수 있도록 버튼들을 노출시키고, 스탯포인트가 없어 스탯을 찍을 수 없으면 버튼을 보여주지 않게 하기위해 오브젝트를 레퍼런스합니다.
[Space(30)]
[Header("토글할 스탯 UI 오브젝트")]
[SerializeField] private GameObject mStatUiGo;
- 스탯창 자체를 토글하기위해 오브젝트를 레퍼런스합니다.
private void Update()
{
if (Input.GetKeyDown(KeyManager.Instance.GetKeyCode("Stat")))
{
this.ToggleStatDialog();
}
}
- "Stat" 키를 눌러 스탯창을 열고 닫을 수 있습니다.
- 이 기능은 별도로 구현한 KeyManager(키 설정 시스템)을 이용하며, 이것을 사용하지 않고 다른 키를 직접 확인하여 사용할 수 있습니다.
- 키 설정 매니저에 대한 내용은 [여기] 에서 확인할 수 있습니다.
public void LevelUp()
- 플레이어가 어떤 경우에의해 레벨업을 하게되면 호출됩니다.
- CurrentStatPoint += 2; 로 스탯포인트가 2 증가되게 설정하였습니다.
- 부수적인 기능으로 파티클 및 사운드를 재생하게 하였습니다.
/// <summary>
/// 세이브파일로부터 로드된경우 스탯 포인트를 설정
/// </summary>
public void LoadFromData(PlayerGameData playerGameData)
{
this.CurrentStatPoint = playerGameData.statPoint;
}
- 세이브 & 로드 기능을 이용한 함수이며 플레이어의 현재 남은 스탯포인트를 가져옵니다.
public void UpdateStatTexts()
- 어떠한 경우에 현재 스탯창의 레이블이 갱신되어야 할 경우 호출됩니다.
- 매 프레임별로 업데이트를 하지 않고 특정한 조건 (창을 열을때, 피해를 받을때, 회복을 할 때, 아이템 착용 등)에만 호출하게 하였습니다.
public void BTN_UpgradeStat(int statIndex)
{
// 스탯 포인트 1 감소
--CurrentStatPoint;
// 스탯 증가
mPlayerController.StatData.UpgradeBaseStat((StatType)statIndex);
// 스탯 Ui 갱신
UpdateStatTexts();
}
- 플레이어가 스탯포인트를 소모하여 기본 스탯을 증가시키도록 합니다.
- Ui에 의해 호출되는 함수이며, 플레이어의 기본 스탯을 증가시킵니다.
- StatData 디자인은 [여기]에서 확인할 수 있습니다.
· Ui 디자인
- 아래의 이미지와 같이 Ui 컴포넌트들을 설정해줍니다.
- SerializeField가 많아 복잡해 보이지만, 같은 패턴이 반복되어 크게 어렵지 않습니다.
- 버튼에 BTN_UpgradeStat 함수를 등록합니다.
- 매개변수는 enum의 번호로 설정합니다. 예) HP는 1번, MP는 2번..
✅ 사용
- [유니티] 레벨, 경험치 시스템에서 플레이어가 레벨업을 하면 LevelUp() 함수를 호출하여 레벨업 효과 및 스탯포인트를 받게 합니다.
- "Stat"키를 눌러 다이얼로그 창을 활성화하거나, 비활성화합니다.
- 플레이어가 스탯찍기 버튼을 눌러 스탯포인트를 소모하면 버튼의 인덱스 번호에 맞게 BaseStat이 갱신됩니다.
'unity game modules' 카테고리의 다른 글
[유니티] 미니맵 (0) | 2023.03.25 |
---|---|
[유니티] 키 설정 시스템 (1) | 2023.03.25 |
[유니티] 스탯 시스템(1) - 디자인 (0) | 2023.03.24 |
[유니티] 레벨, 경험치 시스템 (0) | 2023.03.24 |
[유니티] 환경 사운드 (Ambient Area) (0) | 2023.03.23 |