
오늘 작업
무기 가방에 처음 시작 무기로 나무검을 사용할 수 있도록 함.
무기의 이미지와 이름, 레벨, 능력치들도 UI에 표시되도록 함.
장착버튼을 누르면 장착됨.
==================
여기까지 했으면 무기만 더 추가해서 반복하면 됨.
<WeaponData.cs>
using System.Collections.Generic;
using UnityEngine;
public enum WeaponStatType
{
Damage, // 대미지
CriticalChance // 크리티컬 확률
}
[CreateAssetMenu(fileName = "New WeaponData", menuName = "WeaponStat")]
public class WeaponData : ScriptableObject // 아이템의 스탯 데이터를 저장
{
[Header("기본 정보")]
public string weaponName; // 무기 이름
public int weaponLevel; // 무기 레벨
public List<WeaponStatEntry> weaponStats; // 무기 스텟 리스트
public Sprite weaponSprite; // 무기 스프라이트
[Header("강화 정보")] public int maxLevel; // 무기 최대 레벨
public int damageIncreasePerLevel; // 레벨당 대미지 증가량
public int critChanceIncreasePerLevel; // 레벨당 크리티컬 확률 증가량
[Header("구매 비용")]
public int purchaseCost;
public int GetDamage(int level) // 무기 SO에서 현재 대미지 값 가져오기
{
int baseWeaponDamage = 0; // 무기 기본 대미지
foreach (WeaponStatEntry weaponStat in weaponStats)
{
if (weaponStat.weaponStatType == WeaponStatType.Damage)
{
// 무기 SO에 저장된 대미지 기본값을 저장
baseWeaponDamage = weaponStat.baseValue;
break;
}
}
// 무기 레벨업 시 증가하는 대미지 량
// 무기 기본 대미지 + (레벨당 증가하는 대미지 * 무기 레벨)
return baseWeaponDamage + damageIncreasePerLevel * level;
}
public int GetCritChance(int level) // 무기 SO에서 현재 크리티컬 확률 값 가져오기
{
int baseWeaponCritChance = 0; // 무기 기본 크리티컬 확률
foreach (WeaponStatEntry weaponStat in weaponStats)
{
if (weaponStat.weaponStatType == WeaponStatType.CriticalChance)
{
// 무기 SO에 저장된 크리티컬 확률 기본값을 저장
baseWeaponCritChance = weaponStat.baseValue;
break;
}
}
// 무기 레벨업 시 증가하는 크리티컬 확률 량
// 무기 기본 크리티컬 확률 + (레벨당 증가하는 크리티컬 확률 * 무기 레벨)
return baseWeaponCritChance + critChanceIncreasePerLevel * level;
}
}
[System.Serializable]
public class WeaponStatEntry // 아이템 스탯 정보 정의
{
public WeaponStatType weaponStatType; // 어떤 종류의 능력치인지
public int baseValue; // 해당 능력치의 기본 값
}
=============
<PlayerWeapon.cs>
using UnityEngine;
[System.Serializable]
public class PlayerWeapon // 플레이어가 소유한 특정 무기 데이터와 상태를 관리
{
public WeaponData weaponData; // 이 무기의 기본 정보
public int currentLevel; // 플레이어가 소유한 이 무기 인스턴스의 현재 레벨
// WeaponData와 함께 이 무기 인스턴스의 초기 레벨을 받음
public PlayerWeapon(WeaponData data, int initialLevel)
{
weaponData = data;
currentLevel = initialLevel;
}
public int GetCurrentDamage() // 이 PlayerWeapon 인스턴스의 'currentLevel'을 사용
{
// WeaponData의 GetDamage 메서드를 활용하여 현재 레벨의 데미지를 계산
return weaponData.GetDamage(currentLevel);
}
public int GetCurrentCritChance() // 이 PlayerWeapon 인스턴스의 'currentLevel'을 사용
{
// WeaponData의 GetCritChance 메서드를 활용하여 현재 레벨의 치명타 확률을 계산
return weaponData.GetCritChance(currentLevel);
}
public void WeaponLevelUp() // 무기 업그레이드시 레벨업
{
// 현재 무기 레벨 = 무기 최대 레벨이면 false반환
if (currentLevel == weaponData.maxLevel)
{
Debug.Log("만렙이라 불가");
return;
}
currentLevel++; // 현재 무기 레벨 증가
Debug.Log($"WeaponLevelUp: '{weaponData.weaponName}' 레벨 업! 현재 레벨: {currentLevel}");
}
}
========
<EquipmentManager.cs>
using System;
using UnityEngine;
public class EquipmentManager : MonoBehaviour // 무기 장착 관리
{
public static EquipmentManager Instance {get; private set;}
public PlayerWeapon equippedWeapon{ get; private set; } // 현재 장착된 무기 인스턴스
// 장비 변경 시 다른 스크립트에 알리는 이벤트
public static event Action OnEquipmentChanged;
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
public void EquipWeapon(PlayerWeapon newWeaponToEquip) // 장착할 무기
{
if (equippedWeapon != newWeaponToEquip) // 이미 장착된 무기가 아니면
{
equippedWeapon = newWeaponToEquip;
Debug.Log(equippedWeapon.weaponData.weaponName + " 장착");
// 장착이 완료된 후 OnEquipmentChanged 이벤트를 발생
OnEquipmentChanged?.Invoke();
}
}
public void UnEquipWeapon() // 해제할 무기
{
if (equippedWeapon != null)
{
string weaponName = equippedWeapon.weaponData.weaponName;
equippedWeapon = null; // 무기 해제
Debug.Log(weaponName + " 해제");
}
//장비 해제 후에도 OnEquipmentChanged 이벤트를 발생
// OnEquipmentChanged?.Invoke();
}
}
=========
<InventoryManager.cs>
using System.Collections.Generic;
using UnityEngine;
public class InventoryManager : MonoBehaviour // 플레이어가 소유한 무기를 관리하는 매니저
{
public static InventoryManager Instance { get; private set; }
// 플레이어가 소유한 무기 리스트
public List<PlayerWeapon> playerWeapons = new List<PlayerWeapon>();
// 게임 시작 시 플레이어 인벤토리에 추가될 초기 무기 데이터 목록
public List<WeaponData> initialWeaponDataList;
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
void Start()
{
// 게임 시작 시, 인스펙터에 설정된 초기 무기들을 인벤토리에 추가
foreach (WeaponData data in initialWeaponDataList)
{
// 게임 시작 시 초기 무기를 인벤토리에 추가
if (!playerWeapons.Exists(w => w.weaponData == data))
{
AddNewWeapon(data);
}
}
}
public void AddNewWeapon(WeaponData weaponData) // 인벤토리에 새로운 무기를 추가
{
// 새로운 무기를 소유한 무기 리스트에 추가
PlayerWeapon newWeapon = new PlayerWeapon(weaponData, weaponData.weaponLevel);
playerWeapons.Add(newWeapon);
Debug.Log($"InventoryManager: '{weaponData.weaponName}' (Lv.{newWeapon.currentLevel})이 인벤토리에 추가");
}
// 인벤토리에서 특정 WeaponData를 가진 PlayerWeapon을 찾는 메서드
public PlayerWeapon GetPlayerWeapon(WeaponData data)
{
return playerWeapons.Find(w => w.weaponData == data);
}
}
=======
<WeaponSlotUI.cs>
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class WeaponSlotUI : MonoBehaviour
{
[Header("UI참조")] public Image weaponImage; // 무기 스프라이트 Image 컴포넌트
public TextMeshProUGUI weaponNameText; // 무기 이름 텍스트
[SerializeField] private TextMeshProUGUI weaponDamageText; // 무기 공격력 텍스트
[SerializeField] private TextMeshProUGUI weaponCritChanceText; // 무기 치명타 확률 텍스트
[SerializeField] private TextMeshProUGUI weaponCostText; // 무기 가격
[Header("버튼 GameObject 참조")] public GameObject purchaseButtonGO;
public GameObject changeButtonGO;
public GameObject upgradeButtonGO;
[Header("무기 SO 에셋")] public WeaponData weaponDataAsset;
// 현재 이 UI 슬롯이 나타내는 무기 데이터
private WeaponData _currentWeaponData;
// 이 슬롯에 해당하는 무기의 소유 여부와 현재 레벨 (게임 시작 시 초기화)
private bool hasWeapon = false; // 무기를 소유하고 있는지
private int currentWeaponLevel = 0;
void Awake()
{
// 필수 인스턴스 존재 여부 확인
if (BtnClick.Instance == null)
{
Debug.Log("WeaponSlotUI: BtnClick.Instance를 찾을 수 없음", this);
enabled = false;
return;
}
if (EquipmentManager.Instance == null)
{
Debug.Log("WeaponSlotUI: EquipmentManager.Instance를 찾을 수 없음", this);
enabled = false;
return;
}
if (InventoryManager.Instance == null)
{
Debug.Log("WeaponSlotUI: InventoryManager.Instance를 찾을 수 없음", this);
enabled = false;
return;
}
// weaponDataAsset이 할당되었는지 확인하고 사용
if (weaponDataAsset != null)
{
_currentWeaponData = weaponDataAsset; // 직접 할당된 WeaponData를 사용합니다.
}
else
{
Debug.Log("WeaponSlotUI: 'Weapon Data Asset'이 인스펙터에 할당되지 않음. 스크립트 비활성화.", this);
enabled = false;
return;
}
// 초기 상태 설정: InventoryManager에서 해당 무기를 가지고 있는지 확인
PlayerWeapon existingWeapon = InventoryManager.Instance.GetPlayerWeapon(_currentWeaponData);
if (existingWeapon != null)
{
hasWeapon = true;
currentWeaponLevel = existingWeapon.currentLevel;
}
else
{
hasWeapon = false;
currentWeaponLevel = _currentWeaponData.weaponLevel; // 구매 전에는 WeaponData의 기본 레벨 표시
}
BtnClick.Instance.onWeaponPanelClicked.AddListener(OnWeaponPanelOpened);
BtnClick.Instance.onWeaponBuyClicked.AddListener(OnWeaponBuy);
BtnClick.Instance.onWeaponChangeClicked.AddListener(OnWeaponChange);
// EquipmentManager의 장비 변경 이벤트 구독 (장착 상태 변화 감지)
EquipmentManager.OnEquipmentChanged += OnEquipmentChanged;
}
void Start()
{
UpdateWeaponSlotUI();
}
void OnDestroy()
{
// 구독 해지 (오브젝트 수명 관리를 위해 권장)
if (BtnClick.Instance != null)
{
BtnClick.Instance.onWeaponPanelClicked.RemoveListener(OnWeaponPanelOpened);
BtnClick.Instance.onWeaponBuyClicked.RemoveListener(OnWeaponBuy);
BtnClick.Instance.onWeaponChangeClicked.RemoveListener(OnWeaponChange);
}
if (EquipmentManager.Instance != null)
{
EquipmentManager.OnEquipmentChanged -= OnEquipmentChanged;
}
}
public void OnEquipmentChanged()
{
Debug.Log("WeaponSlotUI: 장착된 무기가 변경되었습니다. UI를 갱신합니다.");
UpdateWeaponSlotUI();
}
// 무기 패널이 열렸을 때 UI 갱신
public void OnWeaponPanelOpened()
{
Debug.Log("WeaponSlotUI: 무기 패널이 열림. UI를 갱신");
if (InventoryManager.Instance == null)
{
Debug.LogError("WeaponSlotUI: InventoryManager.Instance가 null", this);
return;
}
if (_currentWeaponData == null)
{
Debug.LogError("WeaponSlotUI: _currentWeaponData가 null", this);
return;
}
// 인벤토리에서 최신 상태를 가져와 UI 업데이트
PlayerWeapon existingWeapon = InventoryManager.Instance.GetPlayerWeapon(_currentWeaponData);
if (existingWeapon != null)
{
hasWeapon = true;
currentWeaponLevel = existingWeapon.currentLevel;
}
else
{
hasWeapon = false;
currentWeaponLevel = _currentWeaponData.weaponLevel;
}
UpdateWeaponSlotUI();
}
public void OnWeaponBuy() // 무기 구매 처리
{
if (_currentWeaponData == null)
{
Debug.Log("WeaponSlotUI: _currentWeaponData가 준비되지 않음. 무기 구매를 처리할 수 없음");
return;
}
int cost = _currentWeaponData.purchaseCost;
if (BtnClick.Instance.Gold >= cost)
{
BtnClick.Instance.Gold -= cost;
InventoryManager.Instance.AddNewWeapon(_currentWeaponData);
hasWeapon = true;
currentWeaponLevel = _currentWeaponData.weaponLevel;
UpdateWeaponSlotUI();
Debug.Log($"WeaponSlotUI: 무기 '{_currentWeaponData.weaponName}' 구매 성공! 잔액: {BtnClick.Instance.Gold}");
}
}
public void OnWeaponChange() // 무기 장착 처리
{
if (_currentWeaponData == null)
{
Debug.Log("WeaponSlotUI: _currentWeaponData가 준비되지 않았습니다. 무기를 장착할 수 없음");
return;
}
PlayerWeapon weaponToEquip = InventoryManager.Instance.GetPlayerWeapon(_currentWeaponData);
if (weaponToEquip == null)
{
Debug.Log($"WeaponSlotUI: 인벤토리에서 {_currentWeaponData.weaponName}을(를) 찾을 수 없습니다. 장착할 수 없음");
return;
}
// EquipmentManager의 EquipWeapon 메서드 호출
if (EquipmentManager.Instance != null)
{
EquipmentManager.Instance.EquipWeapon(weaponToEquip);
}
else
{
Debug.Log("WeaponSlotUI: EquipmentManager.Instance가 null입니다. 무기를 장착할 수 없음");
}
Debug.Log($"WeaponSlotUI: 무기 {weaponToEquip.weaponData.weaponName} Lv.{weaponToEquip.currentLevel} 장착 완료!");
}
public void UpdateWeaponSlotUI()
{
if (_currentWeaponData == null)
{
Debug.Log("WeaponSlotUI: _currentWeaponData가 없어 UI 업데이트를 할 수 없음");
return;
}
weaponImage.sprite = _currentWeaponData.weaponSprite;
bool isWeaponEquipped = (EquipmentManager.Instance.equippedWeapon != null &&
EquipmentManager.Instance.equippedWeapon.weaponData == _currentWeaponData);
if (hasWeapon) // 무기를 소유하고 있는 경우
{
purchaseButtonGO.SetActive(false);
changeButtonGO.SetActive(!isWeaponEquipped);
upgradeButtonGO.SetActive(currentWeaponLevel < _currentWeaponData.maxLevel);
// 소유한 무기는 현재 레벨 기준으로 정보 표시
weaponNameText.text = $"{_currentWeaponData.weaponName} Lv.{currentWeaponLevel}";
weaponDamageText.text =
$"공격력: {_currentWeaponData.GetDamage(currentWeaponLevel)}"; // WeaponData의 GetDamage 사용
weaponCritChanceText.text =
$"크리티컬 확률: {_currentWeaponData.GetCritChance(currentWeaponLevel)}%"; // WeaponData의 GetCritChance 사용
}
else // 무기를 소유하고 있지 않은 경우 (구매 버튼 활성화, 장착/강화 버튼 비활성화)
{
purchaseButtonGO.SetActive(true);
changeButtonGO.SetActive(false);
upgradeButtonGO.SetActive(false);
// 구매 전에는 WeaponData의 기본 레벨 (보통 1) 기준으로 정보 표시
weaponNameText.text = _currentWeaponData.weaponName;
weaponDamageText.text = $"공격력: {_currentWeaponData.GetDamage(_currentWeaponData.weaponLevel)}";
weaponCritChanceText.text =
$"치명타 확률: {_currentWeaponData.GetCritChance(_currentWeaponData.weaponLevel)}%";
weaponCostText.text = _currentWeaponData.purchaseCost.ToString(); // 구매 가격 표시
}
}
private int GetEquippedWeaponCurrentDamage()
{
if (EquipmentManager.Instance != null && EquipmentManager.Instance.equippedWeapon != null)
{
return EquipmentManager.Instance.equippedWeapon.GetCurrentDamage();
}
Debug.LogWarning("WeaponSlotUI: 현재 장착된 무기가 없거나 EquipmentManager가 초기화되지 않음. 기본 데미지 1을 반환");
return 1;
}
private int GetEquippedWeaponCurrentCritChance()
{
if (EquipmentManager.Instance != null && EquipmentManager.Instance.equippedWeapon != null)
{
return EquipmentManager.Instance.equippedWeapon.GetCurrentCritChance();
}
Debug.LogWarning("WeaponSlotUI: 현재 장착된 무기가 없거나 EquipmentManager가 초기화되지 않음. 기본 치명타 확률 0을 반환");
return 0;
}
}
============
<BtnClick.cs>
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Events;
public class BtnClick : MonoBehaviour
{
public static BtnClick Instance { get; private set; }
public UnityEvent onWeaponPanelClicked;
public UnityEvent onWeaponBuyClicked;
public UnityEvent onWeaponChangeClicked;
public static event Action OnNewGameButtonClick;
public GameObject CloseScene, OpenScene; //화면전환
public GameObject StartScene, GameScene; //씬
public GameObject WeaponChangeUpgrade; //무기 장착 밑 강화칸
public GameObject NotEnoughGold, NotEnoughPoint; //재화 부족알림
public GameObject Weapon1BuyBtn, Weapon1UpgradeBtn, Weapon1ChangeBtn; //무기 1구매,업그레이드,교체버튼
public int Gold = 0;
int Point = 0;
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
if (onWeaponPanelClicked == null) onWeaponPanelClicked = new UnityEvent();
if (onWeaponBuyClicked == null) onWeaponBuyClicked = new UnityEvent();
if (onWeaponChangeClicked == null) onWeaponChangeClicked = new UnityEvent();
}
else
{
Destroy(gameObject);
}
}
public void WeaponChangeUpgradeBtnClick()//무기 변경/강화창 열기 버튼
{
WeaponChangeUpgrade.SetActive(true);
onWeaponPanelClicked?.Invoke();
}
public void WeaponChangeUpgradeCloseBtnClick() //무기 변경/강화창 닫기 버튼
{
WeaponChangeUpgrade.SetActive(false);
}
public void Weapon1BuyBtnClick() //무기1 구매 버튼
{
onWeaponBuyClicked?.Invoke();
// if (Gold >= 0)
// {
// Weapon1BuyBtn.SetActive(false);
// Weapon1UpgradeBtn.SetActive(true);
// Weapon1ChangeBtn.SetActive(true);
// }
// else
// {
// StartCoroutine(ShowNotEnoughGold());
// }
}
public void Weapon1ChangeBtnClick() //무기1 교체 버튼
{
onWeaponChangeClicked?.Invoke();
}
}
목표는 무기를 구매하고 인벤토리에 보관하며, 보관된 무기를 강화하고, 장착하여
플레이어의 능력치에 반영하는 것.
이 모든 과정은 버튼으로 조작된다.
================
1. WeaponData (ScriptableObject)
역할: 게임에 등장하는 모든 무기의 기본 정보를 담고 있는 데이터 템플릿
시너지:
========
2. PlayerWeapon (Serializable Class)
역할: 플레이어가 실제로 소유하고 있는 특정 무기의 '인스턴스' 데이터
시너지:
=============
3. InventoryManager (싱글톤 MonoBehaviour)
역할: 플레이어가 획득했거나 구매한 모든 무기(PlayerWeapon 인스턴스)를
관리하는 중앙 집중형 시스템
시너지:
=================
4. EquipmentManager (싱글톤 MonoBehaviour)
역할: 현재 플레이어에게 장착된 무기(equippedWeapon)를 관리하는 시스템
시너지:
=================
5. BtnClick (싱글톤 MonoBehaviour) - 팀원이 작성한 코드를 내가 일부 수정한 것
역할: 게임 내 UI 버튼의 클릭 이벤트를 중앙에서 처리하고,
해당 이벤트에 따라 관련 로직을 실행하거나 다른 스크립트의 이벤트를 발생시키는 역할
시너지:
이를 통해 사용자 입력이 UI와 로직으로 전달.
=================
6. WeaponSlotUI (MonoBehaviour)
역할: 각 개별 무기 슬롯의 UI(무기 가방의 무기들)를 관리
시너지: