현재 강화시스템은 단일로 강화하면 모든 영웅들이 강화가 되도록 했습니다.
좀 더 전략적으로 강화 시키기 위해 개별로 강화 시킬 수 있도록 변경할 예정입니다.
- 이런 느낌으로 각 영웅들의 정보를 띄어주고 클릭하면 하단에 강화 버튼이 나오도록 할 예정입니다.
클래스 구조
1. 먼저는 UI_Hero 클래스에서 UI_Hero_Btn클래스를 초기화 해준다.
2. UI_HeroBtn을 클릭하면 UI_Status 클래스를 가진 오브젝트가 활성화가 된다.
3. 활성화된 UI_Status는 UI_Btn_Status_Upgrade클래스를 가진 버튼 오브젝트를 초기화 해준다.
4. 활성화된 UI_Btn_Status_Upgrade 버튼을 클릭하여 이벤트를 통해 Status를 StatsuUpgadeManager로 업그레이드 한다.
5. 업그레이드 순간 이벤트를 통해 업그레이드 한 영웅을 찾아 Player_Base에서 스테이터스를 더해준다.
각 영웅들 정보 UI 추가
- 유저가 장착한 영웅들의 정보를 보여주는 버튼 UI이며, 클릭 시 해당 영웅을 업그레이드 시킬 수 있다.
UI_Hero 클래스추가
using UnityEngine;
public class UI_Hero : MonoBehaviour
{
[SerializeField] UI_Hero_Btn[] _herobtn;
[SerializeField] UI_Status _uistatus;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
PlayManager._play_ready_event += HeroButtonSetting;
}
void OnDisable()
{
PlayManager._play_ready_event -= HeroButtonSetting;
}
void HeroButtonSetting()
{
var maxcount = _herobtn.Length;
var userheroidlist = GameManger._userdata._userherodata;
for (int i = 0; i < maxcount; i++)
{
_herobtn[i].Init(userheroidlist[i]._heroid);
}
}
}
- GameManager을 통해 유저가 장착한 정보를 가져온 후 그 정보를 UI_Hero_Btn으로 넘겨 초기화 하도록 한다.
UI_Hero_Btn 클래스 추가
using System;
using Mono.Cecil.Cil;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class UI_Hero_Btn : MonoBehaviour
{
[SerializeField] TextMeshProUGUI _name;
[SerializeField] TextMeshProUGUI _status;
[SerializeField] Image _icon;
const string STATUSDATA = "- {0}\n- {1}%\n- {2}%";
Player_Base _heroclass;
public static event Action<int> _hero_click_event;
void OnDisable()
{
StatusUpgradeManager._statusupgrade_event -= Init;
}
void OnEnable()
{
StatusUpgradeManager._statusupgrade_event += Init;
}
public void Init(int heroid)
{
//현재 필드에 있는 영웅들 리스트를 가져와 아이디 매칭
var herolist = PlayerSpawnManager.instance.GetHeroList();
var heroclass = herolist.Find(x => x.GetID() == heroid);
if (heroclass == null)
{
this.gameObject.SetActive(false);
return;
}
_heroclass = heroclass;
this.gameObject.SetActive(true);
//영웅의 기본 데이터를 가져와 이름, 아이콘을 매칭하고 스테이터스는 필드에 있는 거에서 매칭하기
var hero_origindata = PlayManager.instance.GetHeroData(_heroclass.GetID());
_name.text = hero_origindata._name;
_icon.sprite = hero_origindata._icon;
var criticalper = _heroclass.GetStatus()._critical * 100;
var criticaldamageper = _heroclass.GetStatus()._critical_damage * 100;
_status.text = string.Format(STATUSDATA, _heroclass.GetStatus()._damge, criticalper.ToString("F1"), criticaldamageper.ToString("F1"));
}
public void Btn_Click()
{
UI_Status._heroclass = () => _heroclass;
_hero_click_event?.Invoke(_heroclass.GetID());
}
}
- 받아온 유저의 영웅 정보를 확인하여 이름, 아이콘, 스테이터스를 셋팅한다.
- 클릭히 UI_Status에 현재 영웅의 클래스를 구독하여 보내어 사용할 수 있도록 한다.
UI_Status 클래스 추가
using System;
using UnityEngine;
public class UI_Status : MonoBehaviour
{
[SerializeField] UI_Btn_Status_Upgrade[] _btnstatusupgrades;
public static Func<Player_Base> _heroclass;
public static event Action _status_disable_event;
void OnEnable()
{
var maxcount = _btnstatusupgrades.Length;
for (int i = 0; i < maxcount; i++)
{
_btnstatusupgrades[i].Init(_heroclass.Invoke());
}
}
void OnDisable()
{
_status_disable_event?.Invoke();
}
}
- _btnStatusupgrades에는 각 강화 버튼들이 존재하며, _heroclass를 이용해 클릭한 영웅의 정보를 받아와 각 버튼들을 셋팅해준다.
UI_Btn_Status_Upgrade 클래스 추가
using System;
using TMPro;
using UnityEngine;
public class UI_Btn_Status_Upgrade : MonoBehaviour
{
[SerializeField] ESTATUSUPGRADE _estatusupgradekind;
[SerializeField] TextMeshProUGUI _level;
[SerializeField] TextMeshProUGUI _statusvalue;
public static event Action<int, ESTATUSUPGRADE> _statusupgrade_event;
const float ACTIVEDEALYTIME = 0.2f;//꾸욱 누를때 딜레이
const float POINTUPDEALYTIME = 1f;//첫 딜레이
Player_Base _heroclass;
bool _ispointdown;
float _delaytime;
void Update()
{
if (_ispointdown == false)
{
return;
}
_delaytime -= Time.deltaTime;
if (_delaytime > 0)
{
return;
}
_delaytime = ACTIVEDEALYTIME;
ActiveUpgarde();
}
public void Init(Player_Base heroclass)
{
this._heroclass = heroclass;
LevelAndValueSetting();
}
void LevelAndValueSetting()
{
var heroStatus = _heroclass.GetStatus();
var upgradeLevel = StatusUpgradeManager.instance.GetStatusUpgrade(_heroclass.GetID(), _estatusupgradekind);
float statusValue = 0f;
string statusText = "";
// _estatusupgradekind에 따라 해당하는 status 값 가져오기
switch (_estatusupgradekind)
{
case ESTATUSUPGRADE.ATTACKPER:
statusValue = heroStatus._damge;
statusText = _estatusupgradekind + ": " + statusValue.ToString("F1");
break;
case ESTATUSUPGRADE.CRITICALPER:
statusValue = heroStatus._critical * 100; // 퍼센트로 표시
statusText = _estatusupgradekind + ": " + statusValue.ToString("F1") + "%";
break;
case ESTATUSUPGRADE.CRITICALDAMAGE:
statusValue = heroStatus._critical_damage * 100; // 퍼센트로 표시
statusText = _estatusupgradekind + ": " + statusValue.ToString("F1") + "%";
break;
case ESTATUSUPGRADE.PROTECTMAXHPPER:
statusValue = heroStatus._hp;
statusText = _estatusupgradekind + ": " + statusValue.ToString("F1");
break;
case ESTATUSUPGRADE.PROTECTARMOR:
statusValue = heroStatus._armor;
statusText = _estatusupgradekind + ": " + statusValue.ToString("F1");
break;
}
_statusvalue.text = statusText;
_level.text = "Lv. " + upgradeLevel.level.ToString();
}
public void Btn_EvetnTrigger_PointDown()
{
_delaytime = POINTUPDEALYTIME;
_ispointdown = true;
ActiveUpgarde();
}
public void Btn_EventTrigger_PointUp()
{
_ispointdown = false;
}
void ActiveUpgarde()
{
_statusupgrade_event?.Invoke(_heroclass.GetID(), _estatusupgradekind);
LevelAndValueSetting();
}
}
- 받아온 영웅 정보를 이용해 현재 성장 레벨과, 스테이터스 값 등을 셋팅한다.
- 클릭 시 _statusupgrade_event에 구독된 곳으로 값을 넘겨 업그레이드를 진행 후 레벨과 값을 재셋팅하도록 한다.
- 요즘 게임엔 클릭으로 업그레이드 하는 것도 있지만 클릭 후 꾸욱 누르면 업그레이드가 되기 때문에 EventTrigger를 이용하여 꾸욱 누르면 업그레이드 되도록 만듦
StatusUpgradeManager 클래스 수정
using System;
using System.Collections.Generic;
using UnityEngine;
public class StatusUpgradeManager : MonoBehaviour
{
public static StatusUpgradeManager instance;
Dictionary<int, Dictionary<ESTATUSUPGRADE, int>> _statusupgrade = new Dictionary<int, Dictionary<ESTATUSUPGRADE, int>>();
public static event Action<int> _statusupgrade_event;
[SerializeField]
List<float> _maxlevelvalue = new List<float>()
{
0,
1000,//공격력 퍼센트
1, //크리티컬 퍼센트
1, //크리티컬 데미지 퍼센트
1000,//보호 오브젝트 최대 HP 퍼센트
100 //보호 오브젝트 최대 방어력(상수)
};
const int MAXCOINVALUE = 100000;//최대 강화 총 비용
const int MAXLEVEL = 100; //최대 레벨
void Awake()
{
instance = this;
}
void Start()
{
_statusupgrade.Clear();
}
void OnEnable()
{
UI_Btn_Status_Upgrade._statusupgrade_event += StatusLvUpgrade;
}
void OnDisable()
{
UI_Btn_Status_Upgrade._statusupgrade_event -= StatusLvUpgrade;
}
public (int level, float values) GetStatusUpgrade(int heroid, ESTATUSUPGRADE estatusupgrade)
{
if (_statusupgrade.ContainsKey(heroid) == false)
{
_statusupgrade.Add(heroid, new Dictionary<ESTATUSUPGRADE, int>());
}
if (_statusupgrade[heroid].ContainsKey(estatusupgrade) == false)
{
_statusupgrade[heroid].Add(estatusupgrade, 0);
}
var curlevel = _statusupgrade[heroid][estatusupgrade];
if (curlevel <= 0)
{
return (0, 0);
}
//최대와 현재 레벨과 최대 상승값을 이용해 일정한 값이 오르도록 수식작성
float percentPerLevel = _maxlevelvalue[(int)estatusupgrade] / (MAXLEVEL - 1);
return (curlevel, percentPerLevel * (curlevel - 1));
}
public (int level, float values) GetStatusBeforeUpgrade(int heroid, ESTATUSUPGRADE estatusupgrade)
{
if (_statusupgrade.ContainsKey(heroid) == false)
{
_statusupgrade.Add(heroid, new Dictionary<ESTATUSUPGRADE, int>());
}
if (_statusupgrade[heroid].ContainsKey(estatusupgrade) == false)
{
_statusupgrade[heroid].Add(estatusupgrade, 0);
}
var curlevel = _statusupgrade[heroid][estatusupgrade];
curlevel--;
if (curlevel <= 0)
{
return (0, 0);
}
//최대와 현재 레벨과 최대 상승값을 이용해 일정한 값이 오르도록 수식작성
float percentPerLevel = _maxlevelvalue[(int)estatusupgrade] / (MAXLEVEL - 1);
return (curlevel, percentPerLevel * (curlevel - 1));
}
void StatusLvUpgrade(int heroid, ESTATUSUPGRADE estatusupgrade)
{
if (_statusupgrade.ContainsKey(heroid) == false)
{
_statusupgrade.Add(heroid, new Dictionary<ESTATUSUPGRADE, int>());
}
if (_statusupgrade[heroid].ContainsKey(estatusupgrade) == false)
{
_statusupgrade[heroid].Add(estatusupgrade, 0);
}
var nextlevel = _statusupgrade[heroid][estatusupgrade] + 1;
if (UpgradeCointSetting(nextlevel) == false)
{
//TODO: 돈 없다는 팝업 띄우기
return;
}
_statusupgrade[heroid][estatusupgrade]++;
_statusupgrade_event?.Invoke(heroid);
}
//TODO: 추후 데이터 테이블을 이용해서 비용 할 수 있도록 개선 필요
bool UpgradeCointSetting(int nextlevel)
{
return true;
var nextlevelcoinvalue = MAXCOINVALUE / MAXLEVEL;
var currentupgradecoinvalue = nextlevelcoinvalue * nextlevel;
//TODO: 유저 돈 가져와서 처리할 것
var usercoin = 0;
if (currentupgradecoinvalue > usercoin)
{
return false;
}
usercoin -= currentupgradecoinvalue;
return true;
}
/// <summary>
/// 업그레이드 차이값을 계산하여 반환 (새로운 값 - 이전 값)
/// </summary>
public St_Status GetStatusUpgradeDifference(int heroid)
{
var heroobejct = PlayerSpawnManager.instance.GetHeroList().Find(x => x.GetID() == heroid);
var baseStatus = heroobejct._so_npc._status;
var differenceStatus = new St_Status();
// 이전과 현재 업그레이드 값 가져오기
var beforeAttack = GetStatusBeforeUpgrade(heroid, ESTATUSUPGRADE.ATTACKPER);
var currentAttack = GetStatusUpgrade(heroid, ESTATUSUPGRADE.ATTACKPER);
var beforeCritical = GetStatusBeforeUpgrade(heroid, ESTATUSUPGRADE.CRITICALPER);
var currentCritical = GetStatusUpgrade(heroid, ESTATUSUPGRADE.CRITICALPER);
var beforeCriticalDamage = GetStatusBeforeUpgrade(heroid, ESTATUSUPGRADE.CRITICALDAMAGE);
var currentCriticalDamage = GetStatusUpgrade(heroid, ESTATUSUPGRADE.CRITICALDAMAGE);
// 공격력 차이 계산
int beforeAttackValue = beforeAttack.values > 0 ?
Mathf.FloorToInt(baseStatus._damge * (beforeAttack.values / 100f)) : 0;
int currentAttackValue = currentAttack.values > 0 ?
Mathf.FloorToInt(baseStatus._damge * (currentAttack.values / 100f)) : 0;
differenceStatus._damge = currentAttackValue - beforeAttackValue;
// 크리티컬 확률 차이 계산
float beforeCriticalValue = beforeCritical.values > 0 ? (beforeCritical.values / 100f) : 0;
float currentCriticalValue = currentCritical.values > 0 ? (currentCritical.values / 100f) : 0;
differenceStatus._critical = currentCriticalValue - beforeCriticalValue;
// 크리티컬 데미지 차이 계산
float beforeCriticalDamageValue = beforeCriticalDamage.values > 0 ? (beforeCriticalDamage.values / 100f) : 0;
float currentCriticalDamageValue = currentCriticalDamage.values > 0 ? (currentCriticalDamage.values / 100f) : 0;
differenceStatus._critical_damage = currentCriticalDamageValue - beforeCriticalDamageValue;
return differenceStatus;
}
/// <summary>
/// 현재 업그레이드 값을 St_Status로 반환
/// </summary>
public St_Status GetStatusUpgradeAsStatus(int heroid)
{
var heroobejct = PlayerSpawnManager.instance.GetHeroList().Find(x => x.GetID() == heroid);
var baseStatus = heroobejct._so_npc._status;
var upgradeStatus = new St_Status();
// 각 업그레이드 타입별로 처리
var attackUpgrade = GetStatusUpgrade(heroid, ESTATUSUPGRADE.ATTACKPER);
var criticalUpgrade = GetStatusUpgrade(heroid, ESTATUSUPGRADE.CRITICALPER);
var criticalDamageUpgrade = GetStatusUpgrade(heroid, ESTATUSUPGRADE.CRITICALDAMAGE);
// 공격력: 기본값에 퍼센트 적용
if (attackUpgrade.values > 0)
{
upgradeStatus._damge = Mathf.FloorToInt(baseStatus._damge * (attackUpgrade.values / 100f));
}
// 크리티컬 확률: 퍼센트 값 그대로 적용
if (criticalUpgrade.values > 0)
{
upgradeStatus._critical = criticalUpgrade.values / 100f;
}
// 크리티컬 데미지: 퍼센트 값 그대로 적용
if (criticalDamageUpgrade.values > 0)
{
upgradeStatus._critical_damage = criticalDamageUpgrade.values / 100f;
}
return upgradeStatus;
}
/// <summary>
/// 이전 업그레이드 값을 St_Status로 반환
/// </summary>
public St_Status GetStatusBeforeUpgradeAsStatus(int heroid)
{
// PlayManager에서 기본 헤로 데이터 가져오기
var heroData = PlayManager.instance.GetHeroData(heroid);
var heroObject = heroData._playerobject;
var baseNPC = heroObject.GetComponent<BaseNPC>();
var baseStatus = baseNPC._so_npc._status;
var beforeUpgradeStatus = new St_Status();
// 각 업그레이드 타입별로 처리
var beforeAttack = GetStatusBeforeUpgrade(heroid, ESTATUSUPGRADE.ATTACKPER);
var beforeCritical = GetStatusBeforeUpgrade(heroid, ESTATUSUPGRADE.CRITICALPER);
var beforeCriticalDamage = GetStatusBeforeUpgrade(heroid, ESTATUSUPGRADE.CRITICALDAMAGE);
// 공격력: 기본값에 퍼센트 적용
if (beforeAttack.values > 0)
{
beforeUpgradeStatus._damge = Mathf.FloorToInt(baseStatus._damge * (beforeAttack.values / 100f));
}
// 크리티컬 확률: 퍼센트 값 그대로 적용
if (beforeCritical.values > 0)
{
beforeUpgradeStatus._critical = beforeCritical.values / 100f;
}
// 크리티컬 데미지: 퍼센트 값 그대로 적용
if (beforeCriticalDamage.values > 0)
{
beforeUpgradeStatus._critical_damage = beforeCriticalDamage.values / 100f;
}
return beforeUpgradeStatus;
}
}
public enum ESTATUSUPGRADE
{
NONE,
ATTACKPER,
CRITICALPER,
CRITICALDAMAGE,
PROTECTMAXHPPER,
PROTECTARMOR
}
- _statusupgrade에다가 각 영웅별 강화 수치를 저장해두어 사용할 수 있도록 만들었다.
- GetStatusUpgrade는 현재 강화 수치를 가져오도록 하였고 GetStatusBeforeUpgrade가 있어야 현재와 이전 값을 이용해 계산할 수 있어 함수를 정의 하였다.
- UpgradeCointSetting는 비용 계산을 위한 함수며 추후엔 데이터 테이블을 따로 빼둘 계획이다.
- GetStatusUpgradeDifference를 이용해 Player_Base에다가 추가할 값을 지정하는 함수다.
업그레이드 된 수치 각 영웅에게 적용
using UnityEngine;
public class Player_Base : BaseNPC
{
protected int _id;
public int GetID() => _id;
[SerializeField] AttackAreaController _attackareacontroller;
public virtual void OnSpawn(Vector2 spawnpoint)
{
transform.position = spawnpoint;
StatusUpgradeManager._statusupgrade_event += AddStatus;
UI_Hero_Btn._hero_click_event += AttackAreaView;
base.OnSpawn();
}
public virtual void IDSetting(int id)
{
_id = id;
}
void AddStatus(int id)
{
if (id != _id)
{
return;
}
// 1. 이전 업그레이드 값 제거
var beforeUpgradeStatus = StatusUpgradeManager.instance.GetStatusBeforeUpgradeAsStatus(_id);
base.RemoveStatus(beforeUpgradeStatus);
// 2. 새로운 업그레이드 값 적용
var newUpgradeStatus = StatusUpgradeManager.instance.GetStatusUpgradeAsStatus(_id);
base.AddStatus(newUpgradeStatus);
}
void AttackAreaView(int heroid)
{
if (heroid != _id)
{
return;
}
_attackareacontroller.SetAttackAreaVisibility_Active();
}
}
- StatusUpgradeManager에다가 AddStatus를 구독하여 업그레이드 할때마다 업그레이드 차이 값을 추가하도록 만듦
결과
- 강화가 잘 되는 것을 확인할 수 있습니다.
*일부로 보호 오브젝트 HP높이고 강화 비용 무료로 해둔 상태
1차 22:55 ~ 23:30 UI추가/UI_Hero/UI_Hero_Btn(추가)
2차 20:40 ~ 22:00 UI수정/UI_Hero/UI_Hero_Btn/PlayerSpawnManager/StatusUpgardeManager(수정)
3차 250628 19:00 ~ 20:50 UI_Hero/UI_Hero_Btn/PlayerSpawnManager/StatusUpgardeManager/Player_Base(수정)
'유니티 > 게임 제작' 카테고리의 다른 글
[디펜스 게임 제작] 스킬 선택지 시스템(진행중) (2) | 2025.06.29 |
---|---|
[디펜스 게임 제작] 공격 사거리 표기 (0) | 2025.06.28 |
[디펜스 게임 제작] 게임 오버 처리 (0) | 2025.06.25 |
[디펜스 게임 제작] 스테이지 표시 (0) | 2025.06.24 |
[디펜스 게임 제작] 일시정지 (0) | 2025.06.24 |