├── README.md └── Scripts ├── Contents ├── CharacterCustom.cs ├── Effect.cs ├── EffectData.cs ├── EffectParticle.cs ├── ItemPickUp.cs ├── MonsterStat.cs ├── Portal.cs └── SpawningPool.cs ├── Controllers ├── BaseController.cs ├── Monster │ ├── DarkKnightController.cs │ ├── MonsterAttackCollistion.cs │ └── MonsterController.cs ├── Npc │ ├── NpcController.cs │ ├── QuestNpcController.cs │ ├── ShopNpcController.cs │ └── UpgradeNpcController.cs └── Player │ ├── CameraController.cs │ ├── CursorController.cs │ ├── MapCameraController.cs │ ├── PlayerAnimEvent.cs │ ├── PlayerAttackCollistion.cs │ └── PlayerController.cs ├── Data ├── ArmorItemData.cs ├── EquipmentData.cs ├── ItemData.cs ├── LevelData.cs ├── QuestData.cs ├── SkillData.cs ├── SkinnedData.cs ├── StartData.cs ├── TalkData.cs ├── UseItemData.cs └── WeaponItemData.cs ├── Manager ├── Contents │ └── GameManager.cs ├── Core │ ├── DataManager.cs │ ├── InputManager.cs │ ├── PoolManager.cs │ ├── Poolable.cs │ ├── ResourceManager.cs │ ├── SceneManagerEx.cs │ └── UIManager.cs └── Managers.cs ├── Scenes ├── BaseScene.cs ├── BossScene.cs ├── CustomScene.cs ├── DungeonScene.cs ├── GameScene.cs └── TitleScene.cs ├── UI ├── Popup │ ├── UI_ConfirmPopup.cs │ ├── UI_DiePopup.cs │ ├── UI_EqStatPopup.cs │ ├── UI_InputPopup.cs │ ├── UI_InvenPopup.cs │ ├── UI_LoadPopup.cs │ ├── UI_MenuPopup.cs │ ├── UI_NumberCheckPopup.cs │ ├── UI_Popup.cs │ ├── UI_QuestPopup.cs │ ├── UI_ShopPopup.cs │ ├── UI_SkillPopup.cs │ ├── UI_SlotTipPopup.cs │ ├── UI_TalkPopup.cs │ └── UI_UpgradePopup.cs ├── Scene │ ├── UI_CustomScene.cs │ ├── UI_PlayScene.cs │ ├── UI_Scene.cs │ └── UI_TitleScene.cs ├── SubItem │ ├── UI_ArmorSlot.cs │ ├── UI_CustomButton.cs │ ├── UI_DragSlot.cs │ ├── UI_Guide.cs │ ├── UI_InvenSlot.cs │ ├── UI_ItemDragSlot.cs │ ├── UI_ItemSlot.cs │ ├── UI_QuestButton.cs │ ├── UI_QuestNoticeSlot.cs │ ├── UI_ShopBuySlot.cs │ ├── UI_ShopSaleSlot.cs │ ├── UI_SkillBarSlot.cs │ ├── UI_SkillPopupSlot.cs │ ├── UI_SkillSlot.cs │ ├── UI_Slot.cs │ ├── UI_UpgradeSlot.cs │ ├── UI_UseItemSlot.cs │ └── UI_WeaponSlot.cs ├── UI_Base.cs ├── UI_EventHandler.cs └── WorldSpace │ ├── UI_HitEffect.cs │ ├── UI_HpBar.cs │ ├── UI_NameBar.cs │ ├── UI_Navigation.cs │ └── UI_QuestNotice.cs └── Utills ├── Define.cs ├── Extension.cs └── Util.cs /README.md: -------------------------------------------------------------------------------- 1 | # [Unity 3D] RPG Game Portfolio 2 | ## 1. 소개 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | < 게임 플레이 사진 > 12 | 13 |
14 | 15 | + Unity 3D RPG 게임입니다. 16 | 17 | + 게임 개발자가 되기 위해 스스로 역량을 쌓고 처음으로 제작한 RPG 포트폴리오입니다. 18 | 19 | + 현재 Repository에는 유료에셋 사용으로 인해 소스코드만 등록되어 있습니다. 20 | 21 | + 개발기간: 2023.05.10 ~ 2023.08.18 ( 약 3개월 ) 22 | 23 | + 형상관리: Git SourceTree 24 | 25 |
26 | 27 | ## 2. 개발 환경 28 | + Unity 2021.3.21f1 LTS 29 | 30 | + C# 31 | 32 | + Window 10 33 | 34 |
35 | 36 | ## 3. 사용 기술 37 | | 기술 | 설명 | 38 | |:---:|:---| 39 | | 디자인 패턴 | ● **싱글톤** 패턴을 사용하여 Manager 관리
● **State** 패턴을 사용하여 캐릭터의 기능을 직관적으로 관리 | 40 | | SkinnedMeshRender | 캐릭터의 얼굴을 꾸미고, 장비 장착 시 캐릭터의 의상이 변경되도록 구현 | 41 | | GoogleSheet | 구글 스프레드 시트를 사용해 데이터 관리 | 42 | | Save | 게임 데이터를 모두 json으로 변환하여 관리 ( Dictionary 포함 ) | 43 | | Object Pooling | 자주 사용되는 객체는 Pool 관리하여 재사용 | 44 | | UI 자동화 | 유니티 UI 상에서 컴포넌트로 Drag&Drop되는 일을 줄이기 위한 편의성 | 45 | 46 |
47 | 48 | ## 4. 구현 기능 49 | + Object 50 | - 플레이어: 51 | - 전사 52 | - 일반 몬스터: 53 | - 해골 검사 54 | - 날렵한 해골 검사 55 | - 강력한 해골 검사 56 | - 보스 몬스터: 57 | - 암흑 기사 ( 패턴 : 기본 공격 2개, 이동 공격 3개, 스킬 2개 ) 58 | - NPC: 59 | - Shop NPC (포션, 장신구, 방어구, 무기) 60 | - Upgrade NPC 61 | - Quest NPC 62 | - 아이템: 63 | - HP 회복 물약, MP 회복 물약 64 | - 무기, 방어구, 장신구 65 | + UI 66 | - Scene: 67 | - PlayScene : 게임 진행 시 사용
68 | ( 플레이어 스탯, 미니맵, 퀘스트 알림, 스킬 퀵슬롯, 소비 아이템 퀵슬롯 ) 69 | - CustomScene : 캐릭터 커스텀 시 사용
70 | ( 커스텀 부위 변경 버튼, 확인 버튼, 나가기 버튼 ) 71 | - TitleScene : 게임 접속 시 사용
72 | ( 게임 시작 버튼, 세이브 로드 버튼, 나가기 버튼 ) 73 | - Popup: 74 | - 인벤토리창, 장비창, 스킬창, 퀘스트창, 상점창, 강화창, 대화창, 메뉴창 75 | - 확인창, 개수 입력창, 메뉴창, 부활창, 슬롯Tip창, Scene Load창 76 | - WorldSpace 77 | - 피격 데미지 Effect, 체력 Bar, 이름 Bar, 네비게이션, 퀘스트 Icon 78 | 79 |
80 | 81 | ## 5. 기술 문서 82 | + https://docs.google.com/presentation/d/1jDW8nCpSjZaHVW5mElcxNQNMD1Ai76vOAgrC0b9hbXA/edit?usp=sharing 83 | 84 | ## 6. History 블로그 85 | + https://lhuhyeon.github.io/categories/3d-unity-my-rpg/ 86 | 87 | ## 7. 플레이 영상 88 | + https://www.youtube.com/watch?v=mVkvd95_ezo 89 | 90 | ## 8. 게임 다운로드 91 | + https://drive.google.com/drive/folders/1X-CLIYkAuRs7gpSAvIbH40HmISIl5l-7 92 | -------------------------------------------------------------------------------- /Scripts/Contents/CharacterCustom.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.EventSystems; 5 | 6 | /* 7 | * File : CharacterCustom.cs 8 | * Desc : 캐릭터 커스텀 제어 ( 커스텀 부위 : 머리카락, 눈썹, 수염, 얼굴 문신, 상체, 하체 ) 9 | * 10 | & Functions 11 | & [Public] 12 | & : NextPart() - 캐릭터의 부위를 다음 파츠로 변경 13 | & : SaveCustom() - 현재 부위들을 저장 14 | & 15 | & [Private] 16 | & : CharaterRotation() - 캐릭터 회전 17 | & : ChangePart() - 파츠 부위 변경 18 | & : SetSkinned() - SkinnedMeshRenderer 데이터 저장 19 | * 20 | */ 21 | 22 | public class CharacterCustom : MonoBehaviour 23 | { 24 | public bool stopRotation = false; // 회전 제어 25 | 26 | [SerializeField] 27 | private float rotationSpeed = 3.5f; // 회전 속도 28 | private float currentRotation_Y = 0.01f; // 캐릭터 Y 회전값 29 | 30 | // 부위별 파츠 리스트 31 | [SerializeField] List hairList = new List(); 32 | [SerializeField] List headList = new List(); 33 | [SerializeField] List eyebrowsList = new List(); 34 | [SerializeField] List facialHairList = new List(); 35 | [SerializeField] List torsoList = new List(); 36 | [SerializeField] List hipsList = new List(); 37 | 38 | // 부위별 현재 List index 39 | private int currentHairIndex = 0; 40 | private int currentHeadIndex = 0; 41 | private int currentEyebrowsIndex = 0; 42 | private int currentFacialHairIndex = 0; 43 | private int currentTorsoIndex = 0; 44 | private int currentHipsIndex = 0; 45 | 46 | private void Update() 47 | { 48 | CharaterRotation(); 49 | } 50 | 51 | // ~ UI_CustomButton.cs 에서 파츠 변경 버튼을 누를 때 호출 52 | public void NextPart(Define.DefaultPart partType, bool isNext) 53 | { 54 | // 부위 타입에 맞게 변경 55 | switch (partType) 56 | { 57 | case Define.DefaultPart.Hair: 58 | ChangePart(hairList, ref currentHairIndex, isNext); 59 | break; 60 | case Define.DefaultPart.Head: 61 | ChangePart(headList, ref currentHeadIndex, isNext); 62 | break; 63 | case Define.DefaultPart.Eyebrows: 64 | ChangePart(eyebrowsList, ref currentEyebrowsIndex, isNext); 65 | break; 66 | case Define.DefaultPart.FacialHair: 67 | ChangePart(facialHairList, ref currentFacialHairIndex, isNext); 68 | break; 69 | case Define.DefaultPart.Torso: 70 | ChangePart(torsoList, ref currentTorsoIndex, isNext); 71 | break; 72 | case Define.DefaultPart.Hips: 73 | ChangePart(hipsList, ref currentHipsIndex, isNext); 74 | break; 75 | } 76 | } 77 | 78 | // ~ UI_CustomButton.cs 에서 확인 버튼을 누를 때 호출 79 | public void SaveCustom() 80 | { 81 | // 딕셔너리 생성 82 | Managers.Game.DefaultPart = new Dictionary(); 83 | 84 | // GameManager의 데이터에 저장 85 | Managers.Game.DefaultPart.Add(Define.DefaultPart.Hair, SetSkinned(hairList[currentHairIndex])); 86 | Managers.Game.DefaultPart.Add(Define.DefaultPart.Head, SetSkinned(headList[currentHeadIndex])); 87 | Managers.Game.DefaultPart.Add(Define.DefaultPart.Eyebrows, SetSkinned(eyebrowsList[currentEyebrowsIndex])); 88 | Managers.Game.DefaultPart.Add(Define.DefaultPart.FacialHair, SetSkinned(facialHairList[currentFacialHairIndex])); 89 | Managers.Game.DefaultPart.Add(Define.DefaultPart.Torso, SetSkinned(torsoList[currentTorsoIndex])); 90 | Managers.Game.DefaultPart.Add(Define.DefaultPart.Hips, SetSkinned(hipsList[currentHipsIndex])); 91 | } 92 | 93 | // 캐릭터 회전 (Update) 94 | private void CharaterRotation() 95 | { 96 | // 회전 제어 97 | if (stopRotation == true) 98 | return; 99 | 100 | // UI를 클릭하면 회전 X 101 | if (Input.GetMouseButtonDown(0) == true || Input.GetMouseButtonDown(1) == true) 102 | { 103 | if (EventSystem.current.IsPointerOverGameObject()) 104 | return; 105 | } 106 | 107 | // A Key, ◀ Key : 왼쪽으로 회전 108 | // D Key, ▶ Key : 오른쪽으로 회전 109 | if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D) || 110 | Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.RightArrow)) 111 | { 112 | SetRotation(-Input.GetAxis("Horizontal")); 113 | } 114 | else if (Input.GetMouseButton(0) || Input.GetMouseButton(1)) 115 | { 116 | SetRotation(-Input.GetAxis("Mouse X")); 117 | } 118 | } 119 | 120 | // 회전 설정 121 | private void SetRotation(float horizontal) 122 | { 123 | currentRotation_Y += horizontal * rotationSpeed; 124 | 125 | transform.localRotation = Quaternion.Euler(0f, currentRotation_Y, 0f); 126 | } 127 | 128 | // 파츠 부위 변경 129 | private void ChangePart(List partList, ref int currentIndex, bool isNext) 130 | { 131 | // 현재 부위 비활성화 132 | partList[currentIndex].SetActive(false); 133 | 134 | // ( ▶ ) Button 135 | if (isNext == true) 136 | { 137 | currentIndex++; 138 | 139 | // 다음버튼 눌렀을 때 현재 인덱스가 마지막이라면 처음으로 이동 140 | if (currentIndex >= partList.Count) 141 | currentIndex = 0; 142 | } 143 | // ( ◀ ) Button 144 | else 145 | { 146 | currentIndex--; 147 | 148 | // 뒤로버튼 눌렀을 때 현재 인덱스가 처음이라면 마지막으로 이동 149 | if (currentIndex < 0) 150 | currentIndex = partList.Count - 1; 151 | } 152 | 153 | // 변경된 부위 활성화 154 | partList[currentIndex].SetActive(true); 155 | } 156 | 157 | // SkinnedMeshRenderer 필요 정보 저장 158 | private SkinnedData SetSkinned(GameObject skinnedObject) 159 | { 160 | // SkinnedMeshRenderer 컴포넌트 받기 161 | SkinnedMeshRenderer skinnedMesh = skinnedObject.GetComponent(); 162 | 163 | // 이름, localBounds, rootBone을 저장 164 | SkinnedData skinned = new SkinnedData(){ 165 | sharedMeshName = skinnedMesh.name, 166 | bounds = skinnedMesh.localBounds, 167 | rootBoneName = skinnedMesh.rootBone.name, 168 | }; 169 | 170 | // bones 저장 171 | skinned.bones = new List(); 172 | foreach(Transform child in skinnedMesh.bones) 173 | skinned.bones.Add(child.name); 174 | 175 | return skinned; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Scripts/Contents/Effect.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : Effect.cs 7 | * Desc : Effect 관련 스크립트의 부모이다. 8 | */ 9 | 10 | public class Effect : MonoBehaviour 11 | { 12 | void OnEnable() { GetComponent().Play(); } 13 | void OnDisable() { GetComponent().Stop(); } 14 | } 15 | -------------------------------------------------------------------------------- /Scripts/Contents/EffectData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : EffectData.cs 7 | * Desc : Effect의 id로 사용되며 8 | * : Effect가 부모로부터 떨어져야 되는 상황 (잔해물, 잔상 등)이라면 9 | * : EffectDisableDelayTime() 코루틴을 실행 10 | * 11 | & Functions 12 | & [Public] 13 | & : EffectDisableDelay() - 이펙트 비활성화 딜레이 14 | * 15 | */ 16 | 17 | public class EffectData : Effect 18 | { 19 | public int id; 20 | public float disableDelayTime = 0; // effect 전용 비활성화 딜레이 21 | 22 | private bool isEffect = false; // 이펙트가 실행 중인가? 23 | 24 | // ~ PlayerController.cs 에서 스킬 이펙트 비활성화를 위해 호출 25 | public void EffectDisableDelay() 26 | { 27 | // 딜레이 시간이 0이라면 바로 비활성화 28 | if (disableDelayTime == 0) 29 | { 30 | gameObject.SetActive(false); 31 | return; 32 | } 33 | 34 | // 이펙트가 실행 중이 아니면 35 | if (isEffect == false) 36 | { 37 | // disableDelayTime 동안 부모와 상속 해제 38 | StopCoroutine(EffectDisableDelayTime()); 39 | StartCoroutine(EffectDisableDelayTime()); 40 | } 41 | } 42 | 43 | // 플레이어가 움직이더라도 스킬 이펙트가 활성화되야 한다면 사용 44 | IEnumerator EffectDisableDelayTime() 45 | { 46 | isEffect = true; 47 | 48 | Transform effectParent = transform.parent; // 이펙트 부모 49 | Vector3 effectPos = transform.localPosition; // 이펙트 위치 50 | 51 | // 부모 빠져나오기 52 | transform.SetParent(null); 53 | 54 | // 이펙트 비활성화 기다리기 55 | yield return new WaitForSeconds(disableDelayTime); 56 | 57 | // 원위치 이동 후 비활성화 58 | transform.SetParent(effectParent); 59 | transform.localPosition = effectPos; 60 | transform.localRotation = Quaternion.identity; 61 | 62 | isEffect = false; 63 | 64 | gameObject.SetActive(false); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Scripts/Contents/EffectParticle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : EffectParticle.cs 8 | * Desc : ParticleSystem의 물리적 접촉이 필요할 때 사용 9 | * 10 | & Functions 11 | & [Public] 12 | & : SetInfo() - 설정 13 | & 14 | & [Private] 15 | & : ParticleCollider() - 파티클 접촉 시 호출 16 | & : OnParticleCollision() - 파티클 접촉 확인 17 | * 18 | */ 19 | 20 | public class EffectParticle : Effect 21 | { 22 | Action _onParticleCollider; // 파티클 접촉 시 실행시킬 기능 저장 23 | 24 | // 설정 25 | public void SetInfo(Action onParticleCollider) 26 | { 27 | _onParticleCollider = onParticleCollider; 28 | } 29 | 30 | // 파티클 접촉 시 호출 31 | private void ParticleCollider() 32 | { 33 | if (_onParticleCollider.IsNull() == false) 34 | { 35 | _onParticleCollider.Invoke(); 36 | _onParticleCollider = null; 37 | } 38 | } 39 | 40 | // 파티클 물리적 접촉 확인 41 | private void OnParticleCollision(GameObject other) 42 | { 43 | // 플레이어가 접촉하면 True 44 | if (other.CompareTag("Player")) 45 | ParticleCollider(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Scripts/Contents/ItemPickUp.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : ItemPickUp.cs 7 | * Desc : 땅에 떨어진 아이템의 정보를 가지고 있으며 플레이어와 가까우면 이름 생성 8 | * 9 | & Functions 10 | & : Start() - 이름바 생성 11 | & : FixedUpdate() - 플레이어 근접 시 이름바 활성화 12 | * 13 | */ 14 | 15 | public class ItemPickUp : MonoBehaviour 16 | { 17 | public ItemData item; 18 | public int itemCount = 1; // 아이템 전용 개수 19 | 20 | private float scanRange = 5f; // 플레이어 스캔 거리 21 | 22 | private UI_NameBar nameBarUI = null; 23 | 24 | void Start() 25 | { 26 | // 이름바 생성 및 자식으로 배치 27 | nameBarUI = Managers.UI.MakeWorldSpaceUI(transform); 28 | if (itemCount > 1) 29 | nameBarUI.nameText = item.itemName + $" ({itemCount})"; 30 | else 31 | nameBarUI.nameText = item.itemName; 32 | 33 | nameBarUI.nameText += " [F]"; 34 | } 35 | 36 | void FixedUpdate() 37 | { 38 | // 이름바 Null Check 39 | if (nameBarUI.IsNull() == false) 40 | { 41 | // 플레이어 Null Check 42 | if (Managers.Game.GetPlayer().IsNull() == true) 43 | return; 44 | 45 | // 플레이어와 거리 체크 46 | float distance = (Managers.Game.GetPlayer().transform.position - transform.position).magnitude; 47 | 48 | // scanRange만큼 가까우면 활성화 49 | if (distance <= scanRange) 50 | nameBarUI.gameObject.SetActive(true); 51 | else 52 | nameBarUI.gameObject.SetActive(false); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Scripts/Contents/MonsterStat.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : MonsterStat.cs 7 | * Desc : 몬스터 기본 정보, 피격, 드랍 아이템을 관리 8 | * 9 | & Functions 10 | & [Public] 11 | & : OnAttacked() - 공격 받을 때 호출 12 | & 13 | & [Protected] 14 | & : OnDead() - 사망 시 호출 15 | & 16 | & [Private] 17 | & : OnDropItem() - 드랍 아이템 관리 18 | & : HitEffect() - 피격 이펙트 생성 19 | * 20 | */ 21 | 22 | public class MonsterStat : MonoBehaviour 23 | { 24 | [SerializeField] protected int _id; 25 | [SerializeField] protected string _name = "Monster"; 26 | [SerializeField] protected int _hp; 27 | [SerializeField] protected int _maxHp; 28 | [SerializeField] protected int _attack; 29 | [SerializeField] protected int _dropExp; 30 | [SerializeField] protected int _dropGold; 31 | [SerializeField] protected int _dropItemId = 0; 32 | [SerializeField] protected float _movespeed; 33 | 34 | public int Id { get { return _id; } set { _id = value; } } 35 | public string Name { get { return _name; } set { _name = value; } } 36 | public int Hp { get { return _hp; } set { _hp = Mathf.Clamp(value, 0, MaxHp); } } 37 | public int MaxHp { get { return _maxHp; } set { _maxHp = value; Hp = MaxHp; } } 38 | public int Attack { get { return _attack; } set { _attack = value; } } 39 | public int DropExp { get { return _dropExp; } set { _dropExp = value; } } 40 | public int DropGold { get { return _dropGold; } set { _dropGold = value; } } 41 | public int DropItemId { get { return _dropItemId; } set { _dropItemId = value; } } 42 | public float MoveSpeed { get { return _movespeed; } set { _movespeed = value; } } 43 | 44 | void Start() 45 | { 46 | _monster = GetComponent(); 47 | 48 | GameObject go; 49 | if (Managers.Data.Monster.TryGetValue(Id, out go) == true) 50 | { 51 | MonsterStat stat = go.GetComponent(); 52 | _name = stat.Name; 53 | _hp = stat.Hp; 54 | _maxHp = stat.MaxHp; 55 | _attack = stat.Attack; 56 | _dropExp = stat.DropExp; 57 | _dropGold = stat.DropGold; 58 | _dropItemId = stat.DropItemId; 59 | _movespeed = stat.MoveSpeed; 60 | } 61 | } 62 | 63 | // 공격을 받았을 때 64 | MonsterController _monster; 65 | public virtual void OnAttacked(int skillAttack=0) 66 | { 67 | // 일반 몬스터만 피격 상태 진행 68 | if (_monster.monsterType == Define.MonsterType.Normal) 69 | _monster.State = Define.State.Hit; 70 | 71 | // Scene UI에 몬스터 정보 활성화 72 | Managers.Game._playScene.OnMonsterBar(this); 73 | 74 | int damage; 75 | // 스킬 데미지 확인 76 | if (skillAttack != 0) 77 | damage = Mathf.Max(0, skillAttack); 78 | else 79 | damage = Mathf.Max(0, Managers.Game.Attack); 80 | 81 | // 체력 차감 82 | Hp -= damage; 83 | 84 | // 피격 이펙트 생성 85 | HitEffect(damage); 86 | 87 | // 체력이 0보다 작으면 사망 88 | if (Hp <= 0) 89 | { 90 | Hp = 0; 91 | OnDead(); 92 | } 93 | } 94 | 95 | // 죽었을 때 96 | protected virtual void OnDead() 97 | { 98 | _monster.State = Define.State.Die; 99 | 100 | // 보상 반영 101 | Managers.Game.Exp += _dropExp; 102 | Managers.Game.Gold += _dropGold; 103 | 104 | // 퀘스트 정보 반영 105 | Managers.Game._playScene._quest.QuestTargetCount(gameObject); 106 | 107 | // Scene UI 몬스터 정보 비활성화 108 | Managers.Game._playScene.CloseMonsterBar(); 109 | 110 | // 아이템 드랍 111 | OnDropItem(); 112 | 113 | // 전투 종료 114 | _monster.BattleClose(); 115 | } 116 | 117 | // 드랍 아이템 118 | private void OnDropItem() 119 | { 120 | // DataManager에서 DropItem List가져오기 121 | List itemList = Managers.Data.DropItem[_dropItemId]; 122 | 123 | // 아이탬 개수 0~2 + Luk (최대 5개까지) 124 | int maxCount = Mathf.Clamp(2 + Managers.Game.LUK, 0, 5); 125 | 126 | for(int i=0; i(); 137 | goData.item = item; 138 | 139 | // 드랍 위치 설정 140 | float ranPos = Random.Range(-0.5f, 0.5f); 141 | go.transform.position = new Vector3(transform.position.x + ranPos, 0f, transform.position.z + ranPos); 142 | } 143 | } 144 | 145 | // 피격 데미지 출력 146 | private void HitEffect(int damage) 147 | { 148 | // UI_HitEffect 생성 후 데미지 text 넣기 149 | UI_HitEffect hitObject = Managers.UI.MakeWorldSpaceUI(gameObject.transform); 150 | hitObject.hitText.text = damage.ToString(); 151 | 152 | // 생성 위치 설정 153 | float randomX = Random.Range(-0.5f, 0.5f); 154 | float valueY = GetComponent().bounds.size.y * 0.8f; 155 | hitObject.transform.position = transform.position + new Vector3(randomX, valueY, 0); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Scripts/Contents/Portal.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : Portal.cs 7 | * Desc : 포탈 생성 및 Scene 이동 8 | * 9 | & Functions 10 | & : OnTriggerEnter() - 플레이어 Enter 확인 후 포탈 활성화 11 | & : OnTriggerStay() - 플레이어와 근접하면 Scene Load 12 | & : OnTriggerExit() - 플레이어 Exit 확인 후 포탈 비활성화 13 | * 14 | */ 15 | 16 | public class Portal : MonoBehaviour 17 | { 18 | private float scanRange = 3.2f; // 플레이어 스캔 거리 19 | private bool isPortal = false; // 포탈 접촉 여부 20 | 21 | [SerializeField] 22 | private Define.Scene sceneType; // Load할 Scene 타입 23 | 24 | [SerializeField] 25 | private GameObject portalObject; // 포탈 객체 26 | 27 | void OnTriggerEnter(Collider other) 28 | { 29 | // 플레이어 체크 30 | if (other.CompareTag("Player")) 31 | { 32 | isPortal = false; 33 | portalObject.SetActive(true); 34 | } 35 | } 36 | 37 | void OnTriggerStay(Collider other) 38 | { 39 | // 포탈 활성화 체크 40 | if (portalObject.activeSelf == true && isPortal == false) 41 | { 42 | // 플레이어와 포탈이 근접한지 체크 43 | float distance = (Managers.Game.GetPlayer().transform.position - portalObject.transform.position).magnitude; 44 | if (distance <= scanRange) 45 | { 46 | isPortal = true; 47 | 48 | // 플레이어 정지 49 | Managers.Game.StopPlayer(); 50 | 51 | // 현재 Scene이 Game Scene라면 52 | if (Managers.Scene.CurrentScene.SceneType == Define.Scene.Game) 53 | { 54 | // 확인 Popup 활성화 55 | UI_ConfirmPopup confirmPopup = Managers.UI.ShowPopupUI(); 56 | if (confirmPopup.IsNull() == true) 57 | return; 58 | 59 | // 확인 Popup 설정 60 | confirmPopup.SetInfo(()=> 61 | { 62 | // 게임 세이브 63 | Managers.Game.SaveGame(); 64 | 65 | // 씬 이동 전 위치 저장 66 | Managers.Game.CurrentPos += Vector3.forward * (-3f); 67 | 68 | // 씬 로드 69 | Managers.Scene.LoadScene(sceneType); 70 | }, Define.DungeonMessage); 71 | } 72 | else 73 | Managers.Scene.LoadScene(sceneType); 74 | } 75 | } 76 | } 77 | 78 | void OnTriggerExit(Collider other) 79 | { 80 | if (other.CompareTag("Player")) 81 | { 82 | portalObject.SetActive(false); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Scripts/Contents/SpawningPool.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.AI; 5 | 6 | /* 7 | * File : SpawningPool.cs 8 | * Desc : 몬스터 생성 9 | * 10 | & Functions 11 | & [Public] 12 | & : AddMonsterCount() - 몬스터 수 증가 13 | & : SetKeepMonsterCount() - 최대 몬스터 지정 14 | & 15 | & [Private] 16 | & : ReserveSpawn() - 몬스터 스폰 코루틴 17 | * 18 | */ 19 | 20 | public class SpawningPool : MonoBehaviour 21 | { 22 | public GameObject _spawnMonsterNumber; // 몬스터 Prefab 23 | 24 | [SerializeField] 25 | private Vector3 _spawnPos; // 스폰 위치 26 | 27 | [SerializeField] 28 | private float _spawnRedius = 5f; // 스폰 최대 거리 29 | 30 | [SerializeField] 31 | private float _spawnTime = 5f; // 스폰 최대 시간 32 | 33 | [SerializeField] 34 | private int _monsterCount = 0; // 현재 몬스터 수 35 | private int _reserveCount = 0; // 임시 변수 (에러 방지) 36 | 37 | [SerializeField] 38 | private int _keepMonsterCount = 0; // 최대 몬스터 수 39 | 40 | // 몬스터 수 증가 41 | public void AddMonsterCount(Transform parent, int value) 42 | { 43 | // 스포너 부모 체크 44 | if (transform == parent) 45 | this._monsterCount += value; 46 | } 47 | // 최대 몬스터 지정 48 | public void SetKeepMonsterCount(int count) { this._keepMonsterCount = count; } 49 | 50 | void Start() 51 | { 52 | Managers.Game.OnSpawnEvent -= AddMonsterCount; 53 | Managers.Game.OnSpawnEvent += AddMonsterCount; 54 | } 55 | 56 | void Update() 57 | { 58 | // 최대 몬스터 수 만큼 생성 59 | while((_reserveCount + _monsterCount) < _keepMonsterCount) 60 | StartCoroutine("ReserveSpawn"); 61 | } 62 | 63 | // 몬스터 스폰 설정 64 | private IEnumerator ReserveSpawn() 65 | { 66 | // 코루틴이 시작하자마자 시간을 기다리므로 _monsterCount가 증가하지 못해 Update의 while에서 Error가 발생할 수 있다. 67 | // 그러므로 _reserveCount를 사용해 while을 끝내도록 한다. 68 | _reserveCount++; 69 | 70 | yield return new WaitForSeconds(Random.Range(1, _spawnTime)); 71 | 72 | // 몬스터 생성 73 | GameObject obj = Managers.Game.Spawn(Define.WorldObject.Monster, _spawnMonsterNumber, transform); 74 | NavMeshAgent nav = obj.GetOrAddComponent(); 75 | 76 | Vector3 randPos; 77 | 78 | // 소환 가능한 위치를 찾을 때까지 루프 79 | while(true) 80 | { 81 | Vector3 randDir = Random.insideUnitSphere * _spawnRedius; // 원 형태 랜덤 벡터 지정 82 | randDir.y = 0; 83 | randPos = _spawnPos + randDir; 84 | 85 | NavMeshPath path = new NavMeshPath(); 86 | if (nav.CalculatePath(randPos, path)) // randPos 위치에 소환 가능 여부 확인 87 | { 88 | obj.transform.position = randPos; 89 | break; 90 | } 91 | } 92 | 93 | // 위치 설정 94 | nav.nextPosition = randPos; 95 | obj.GetComponent().spawnPos = randPos; 96 | 97 | // while의 에러 예방 목적인 변수이므로 코루틴이 끝날땐 --를 해준다. 98 | _reserveCount--; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Scripts/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : BaseController.cs 7 | * Desc : 상태 패턴을 사용하며 상태에 따라 Animation CrossFade를 실행한다. 8 | * 모든 Controller는 BaseController를 상속받는다. 9 | * 10 | & Functions 11 | & [Public] 12 | & : Init() - 초기 설정 (abstract) 13 | & 14 | & [protected] 15 | & : AnimAttack() - 공격 Animation 설정 16 | & : UpdateMoving() - 움직일 시 Update 17 | & : UpdateDiveRoll() - 구르기 시 Update 18 | & : UpdateIdle() - 멈춤일 시 Update 19 | & : UpdateAttack() - 공격할 시 Update 20 | & : UpdateSkill() - 스킬사용 시 Update 21 | & : UpdateHit() - 공격 받을 시 Update 22 | & : UpdateDie() - 죽을 시 Update 23 | * 24 | */ 25 | 26 | public abstract class BaseController : MonoBehaviour 27 | { 28 | [SerializeField] 29 | public Define.WorldObject WorldObjectType { get; protected set; } = Define.WorldObject.Unknown; 30 | 31 | [SerializeField] 32 | protected GameObject _lockTarget; // 마우스로 타겟한 오브젝트 담는 변수 33 | 34 | [SerializeField] 35 | protected Vector3 _destPos; // 도착 좌표 36 | 37 | [SerializeField] 38 | protected Define.State _state = Define.State.Idle; // 상태 변수 39 | 40 | protected int attackNumber = 1; // 일반 공격 콤보 체크 41 | 42 | protected Animator anim; 43 | protected RaycastHit hit; 44 | 45 | // 캐릭터 상태에 따라 애니메이션 작동 46 | public virtual Define.State State 47 | { 48 | get { return _state; } 49 | set { 50 | _state = value; 51 | 52 | switch (_state) 53 | { 54 | case Define.State.Moving: 55 | anim.CrossFade("RUN", 0.1f); 56 | break; 57 | case Define.State.Idle: 58 | anim.CrossFade("WAIT", 0.4f); 59 | break; 60 | case Define.State.DiveRoll: 61 | anim.CrossFade("DIVEROLL", 0.1f, -1, 0); 62 | break; 63 | case Define.State.Attack: 64 | AnimAttack(); 65 | break; 66 | case Define.State.Hit: 67 | anim.CrossFade("HIT", 0.1f, -1, 0); 68 | break; 69 | case Define.State.Down: 70 | anim.CrossFade("DOWN", 0.1f, -1, 0); 71 | break; 72 | case Define.State.Die: 73 | anim.CrossFade("DIE", 0.1f, -1, 0); 74 | break; 75 | } 76 | } 77 | } 78 | 79 | void Start() 80 | { 81 | Init(); 82 | _lockTarget = null; 83 | } 84 | 85 | // Playe, NPC 전용 ( 키 입력이 필요한 경우 ) 86 | void Update() 87 | { 88 | if (WorldObjectType == Define.WorldObject.Monster) 89 | return; 90 | 91 | switch (State) 92 | { 93 | case Define.State.Moving: // 움직임 94 | UpdateMoving(); 95 | break; 96 | case Define.State.DiveRoll: // 구르기 97 | UpdateDiveRoll(); 98 | break; 99 | case Define.State.Idle: // 가만히 있기 100 | UpdateIdle(); 101 | break; 102 | case Define.State.Attack: // 일반 공격 103 | UpdateAttack(); 104 | break; 105 | case Define.State.Skill: // 스킬 106 | UpdateSkill(); 107 | break; 108 | case Define.State.Hit: // 피격 109 | UpdateHit(); 110 | break; 111 | case Define.State.Die: // 죽음 112 | UpdateDie(); 113 | break; 114 | } 115 | } 116 | 117 | // Monster 전용 118 | void FixedUpdate() 119 | { 120 | if (WorldObjectType != Define.WorldObject.Monster) 121 | return; 122 | 123 | switch (State) 124 | { 125 | case Define.State.Moving: // 움직임 126 | UpdateMoving(); 127 | break; 128 | case Define.State.Idle: // 가만히 있기 129 | UpdateIdle(); 130 | break; 131 | case Define.State.Attack: // 일반 공격 132 | UpdateAttack(); 133 | break; 134 | case Define.State.Skill: // 스킬 135 | UpdateSkill(); 136 | break; 137 | case Define.State.Hit: // 피격 138 | UpdateHit(); 139 | break; 140 | case Define.State.Die: // 죽음 141 | UpdateDie(); 142 | break; 143 | } 144 | } 145 | 146 | public abstract void Init(); 147 | 148 | // 기본 공격 애니메이션 149 | protected virtual void AnimAttack() 150 | { 151 | anim.CrossFade("ATTACK"+attackNumber, 0.1f, -1, 0); 152 | 153 | if (attackNumber == 1) 154 | attackNumber = 2; 155 | else if (attackNumber == 2) 156 | attackNumber = 1; 157 | } 158 | 159 | protected virtual void UpdateMoving() {} 160 | protected virtual void UpdateDiveRoll() {} 161 | protected virtual void UpdateIdle() {} 162 | protected virtual void UpdateAttack() {} 163 | protected virtual void UpdateSkill() {} 164 | protected virtual void UpdateHit() {} 165 | protected virtual void UpdateDie() {} 166 | } 167 | -------------------------------------------------------------------------------- /Scripts/Controllers/Monster/MonsterAttackCollistion.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : MonsterAttackCollistion.cs 7 | * Desc : 몬스터 콜라이더 공격 8 | * 9 | & Functions 10 | & [Public] 11 | & : IsCollider() - 콜라이더 여부 설정 12 | & 13 | & [Private] 14 | & : OnTriggerEnter() - 플레이어와 접촉 시 데미지 반영 15 | * 16 | */ 17 | 18 | public class MonsterAttackCollistion : MonoBehaviour 19 | { 20 | public int damage; 21 | 22 | [SerializeField] 23 | private BoxCollider boxCollider; 24 | 25 | public void IsCollider(bool isActive) { boxCollider.enabled = isActive; } 26 | 27 | void OnTriggerEnter(Collider other) 28 | { 29 | if (other.CompareTag("Player") == true) 30 | Managers.Game.OnAttacked(damage); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Scripts/Controllers/Npc/NpcController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : NpcController.cs 7 | * Desc : NPC 기본 기능 및 모든 NPC의 부모 8 | * 모든 NPC는 플레이어와 상호작용하기 때문에 OnInteract(), OpenPopup(), ExitPopup()를 상속 받아 구현 9 | * 10 | & Functions 11 | & [Public] 12 | & : Init() - 초기 설정 13 | & : GetInteract() - 상호작용 외부 접근 14 | & 15 | & [Protected] 16 | & : UpdateIdle() - 멈춤일 때 Update (플레이어 감지) 17 | & : OnInteract() - 상호작용 기능 18 | & : OpenPopup() - Popup 활성화 19 | & : ExitPopup() - Popup 비활성화 20 | & 21 | & [Private] 22 | & : InteractCheck() - 상호작용 확인 23 | * 24 | */ 25 | 26 | public abstract class NpcController : BaseController 27 | { 28 | [SerializeField] 29 | protected string npcName; // npc 이름 30 | [SerializeField] 31 | protected float scanRange; // 플레이어 스캔 사거리 32 | 33 | protected UI_NameBar nameBarUI; // 이름바 UI 34 | 35 | public override void Init() 36 | { 37 | // 이름바 생성 및 이름 설정 38 | nameBarUI = Managers.UI.MakeWorldSpaceUI(transform); 39 | nameBarUI.nameText = npcName + " [G]"; 40 | } 41 | 42 | // 상호작용 외부 접근 43 | public void GetInteract() { OnInteract(); } 44 | 45 | protected override void UpdateIdle() 46 | { 47 | // 플레이어 Null Check 48 | if (Managers.Game.GetPlayer().IsNull() == true) 49 | return; 50 | 51 | // 플레이어와의 거리 좌표 52 | Vector3 direction = Managers.Game.GetPlayer().transform.position - transform.position; 53 | 54 | // 거리 체크 55 | if (direction.magnitude <= scanRange) 56 | { 57 | // 상호작용 체크 58 | InteractCheck(); 59 | 60 | _lockTarget = Managers.Game.GetPlayer(); // 타겟 설정 61 | nameBarUI.gameObject.SetActive(true); // 이름바 활성화 62 | 63 | // 방향 설정 64 | direction.y = 0; 65 | transform.rotation = Quaternion.LookRotation(direction); 66 | } 67 | else 68 | { 69 | _lockTarget = null; 70 | nameBarUI.gameObject.SetActive(false); 71 | } 72 | } 73 | 74 | // 상호작용 기능 75 | protected virtual void OnInteract() 76 | { 77 | Managers.Game.IsInteract = !Managers.Game.IsInteract; 78 | 79 | // 상호작용 시작 80 | if (Managers.Game.IsInteract) 81 | { 82 | // 모든 팝업 비활성화 및 플레이어 정지 83 | Managers.UI.CloseAllPopupUI(); 84 | Managers.Game.StopPlayer(); 85 | 86 | OpenPopup(); // Popup Open 87 | } 88 | else 89 | ExitPopup(); // Popup Exit 90 | } 91 | 92 | // Popup Open/Exit 93 | protected virtual void OpenPopup() {} 94 | protected virtual void ExitPopup() {} 95 | 96 | // 플레이어가 가까이 있다면 상호작용 가능 97 | private void InteractCheck() 98 | { 99 | if (Input.GetKeyDown(KeyCode.G)) 100 | OnInteract(); 101 | 102 | // 상호작용 중이라면 103 | if (Managers.Game.IsInteract == true) 104 | { 105 | // Esc Key 상호작용 종료 106 | if (Input.GetKeyDown(KeyCode.Escape)) 107 | OnInteract(); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Scripts/Controllers/Npc/QuestNpcController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : QuestNpcController.cs 7 | * Desc : 퀘스트 NPC 기능 구현 8 | * 대화의 종류는 일반, 퀘스트 시작, 퀘스트 거절, 퀘스트 수락, 퀘스트 진행, 퀘스트 성공으로 총 6가지 존재. 9 | * 퀘스트를 가져오는 방법은 questId, talkId를 미리 유니티상의 컴포넌트에서 id를 입력시켜준다. 10 | * 입력된 Id를 가지고 DataManager에 보내어 퀘스트를 받아온다. 11 | * 12 | & Functions 13 | & [Public] 14 | & : Init() - 초기 설정 15 | & 16 | & [Protected] 17 | & : OnInteract() - 상호작용 기능 구현 18 | & : OpenPopup() - Popup 활성화 (TalkCheck() 호출) 19 | & 20 | & [Private] 21 | & : TalkCheck() - 대화 확인 22 | & : OnTalk() - 대화 시작 23 | & : NextQuestCheck() - 다음 퀘스트 확인 24 | & : NoticeUpdate() - 알람 업데이트 25 | * 26 | */ 27 | 28 | public class QuestNpcController : NpcController 29 | { 30 | public Define.QuestType questType; // 퀘스트 타입 31 | 32 | [SerializeField] 33 | private int[] questId; // 퀘스트 id Array 34 | [SerializeField] 35 | private int[] talkId; // 대화 id Array 36 | 37 | private int nextQuestIndex; // 다음 퀘스트 Index 38 | 39 | private bool isQuest; // 퀘스트가 존재하는가? 40 | 41 | private QuestData currentQuest; // 현재 퀘스트 42 | private TalkData currentTalk; // 현재 대화 43 | 44 | private List questDataList; // 퀘스트 List 45 | private List talkDataList; // 대화 List 46 | 47 | private UI_QuestNotice noticeObject; // 알람 Prefab 48 | 49 | public override void Init() 50 | { 51 | base.Init(); 52 | 53 | questDataList = new List(); 54 | talkDataList = new List(); 55 | 56 | // id에 맞게 퀘스트, 대화 데이터 가져오기 57 | for(int i=0; i= currentQuest.targetCount) 112 | { 113 | // 인벤토리가 꽉찼는지 확인 114 | if (Managers.Game._playScene._inventory.IsInvenMaxSize() == true) 115 | { 116 | // 경고 안내문 생성 117 | Managers.UI.MakeSubItem().SetInfo("인벤토리가 가득 찼습니다.", Color.red); 118 | return; 119 | } 120 | 121 | // Scene UI 알람이 등록된 퀘스트라면 삭제 122 | Managers.Game._playScene._quest.CloseQuestNotice(currentQuest); 123 | 124 | OnTalk(currentTalk.clearTalk); // 클리어 대화 시작 125 | currentQuest.QuestClear(); // 퀘스트 클리어 (보상 지급) 126 | 127 | Managers.Game.RefreshQuest(); // 클리어 퀘스트에 등록 128 | NextQuestCheck(); // 다음 퀘스트 확인 129 | } 130 | else 131 | OnTalk(currentTalk.procTalk); // 퀘스트 진행 대화 시작 132 | 133 | return; 134 | } 135 | 136 | // 레벨이 되면 퀘스트 대화 시작 137 | if (currentQuest.minLevel <= Managers.Game.Level) 138 | { 139 | OnTalk(currentTalk); 140 | return; 141 | } 142 | 143 | // 여기까지 오면 받을 퀘스트가 없기에 기본 대화 진행 144 | OnTalk(currentTalk.basicsTalk); 145 | } 146 | 147 | // 기본 하나의 대화 시작 148 | private void OnTalk(string text) 149 | { 150 | UI_TalkPopup talkPopup = Managers.Game._playScene._talk; 151 | 152 | Managers.UI.OnPopupUI(talkPopup); // Popup 활성화 153 | talkPopup.SetInfo(text, npcName); // 대화 세팅 154 | } 155 | 156 | // TalkData를 그대로 보낼 시 퀘스트 시작으로 판정 157 | private void OnTalk(TalkData texts) 158 | { 159 | UI_TalkPopup talkPopup = Managers.Game._playScene._talk; 160 | 161 | Managers.UI.OnPopupUI(talkPopup); // Popup 활성화 162 | talkPopup.SetInfo(texts, currentQuest, npcName); // 대화 세팅 163 | } 164 | 165 | // 다음 퀘스트 166 | private void NextQuestCheck() 167 | { 168 | nextQuestIndex++; 169 | 170 | // 퀘스트 개수 확인 171 | if (nextQuestIndex == questDataList.Count) 172 | { 173 | isQuest = false; 174 | return; 175 | } 176 | 177 | // 퀘스트, 대화 적용 178 | currentQuest = questDataList[nextQuestIndex]; 179 | currentTalk = talkDataList[nextQuestIndex]; 180 | 181 | isQuest = true; 182 | } 183 | 184 | // 알람 업데이트 ( 실시간 npc 위의 퀘스트 상태 표시 ) 185 | private void NoticeUpdate() 186 | { 187 | // Null Check 188 | if (noticeObject.IsNull() == true) 189 | return; 190 | 191 | // 레벨 확인 192 | if (currentQuest.minLevel > Managers.Game.Level) 193 | return; 194 | 195 | // 이미 퀘스트를 클리어 했는가? or 퀘스트가 없거나 196 | if (currentQuest.isClear == true || isQuest == false) 197 | { 198 | noticeObject.SetInfo("", transform.position); 199 | return; 200 | } 201 | 202 | // 퀘스트 목표 달성 확인 203 | if (currentQuest.currnetTargetCount >= currentQuest.targetCount) 204 | { 205 | noticeObject.SetInfo("?"); 206 | return; 207 | } 208 | 209 | // 퀘스트 수락 확인 210 | if (currentQuest.isAccept == true) 211 | noticeObject.SetInfo("", transform.position); 212 | else 213 | noticeObject.SetInfo("!", transform.position); 214 | } 215 | 216 | private void DelayInit() 217 | { 218 | // 현재 클리어한 퀘스트가 있는지 219 | for(int i=0; i(); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /Scripts/Controllers/Npc/ShopNpcController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : ShopNpcController.cs 7 | * Desc : 상점 Npc 기능 구현 8 | * 9 | & Functions 10 | & [Protected] 11 | & : OpenPopup() - Popup 활성화 (OpenShop() 호출) 12 | & : ExitPopup() - Popup 비활성화 (ExitShop() 호출) 13 | & 14 | & [Private] 15 | & : OpenShop() - 상점 Popup 열기 16 | & : ExitShop() - 상점 Popup 나가기 17 | * 18 | */ 19 | 20 | public class ShopNpcController : NpcController 21 | { 22 | public Define.ShopType shopType = Define.ShopType.Unknown; 23 | 24 | [SerializeField] 25 | private int shopBuyId; // Shop Npc 구매 목록 Id 26 | 27 | protected override void OpenPopup() { OpenShop(); } 28 | protected override void ExitPopup() { ExitShop(); } 29 | 30 | private void OpenShop() 31 | { 32 | // 상점 Popup 활성화 33 | Managers.UI.OnPopupUI(Managers.Game._playScene._shop); 34 | Managers.Game._playScene._shop.RefreshUI(this, shopBuyId); 35 | 36 | // 인벤토리 Popup 활성화 37 | Managers.UI.OnPopupUI(Managers.Game._playScene._inventory); 38 | Managers.Game._playScene._inventory.ResetPos(); 39 | } 40 | 41 | private void ExitShop() 42 | { 43 | Managers.Game._playScene._shop.ExitShop(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Scripts/Controllers/Npc/UpgradeNpcController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : UpgradeNpcController.cs 7 | * Desc : 장비 강화 NPC 기능 구현 8 | * 9 | & Functions 10 | & [Protected] 11 | & : OpenPopup() - Popup 활성화 (OpenUpgrade() 호출) 12 | & : ExitPopup() - Popup 비활성화 (ExitUpgrade() 호출) 13 | & 14 | & [Private] 15 | & : OpenUpgrade() - 업그레이드 Popup 열기 16 | & : ExitUpgrade() - 업그레이드 Popup 나가기 17 | * 18 | */ 19 | 20 | public class UpgradeNpcController : NpcController 21 | { 22 | protected override void OpenPopup() { OpenUpgrade(); } 23 | protected override void ExitPopup() { ExitUpgrade(); } 24 | 25 | private void OpenUpgrade() 26 | { 27 | // 업그레이드 Popup 활성화 28 | Managers.UI.OnPopupUI(Managers.Game._playScene._upgrade); 29 | 30 | // 인벤토리 Popup 활성화 31 | Managers.UI.OnPopupUI(Managers.Game._playScene._inventory); 32 | Managers.Game._playScene._inventory.ResetPos(); 33 | } 34 | 35 | private void ExitUpgrade() 36 | { 37 | Managers.Game._playScene._upgrade.ExitUpgrade(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Scripts/Controllers/Player/CameraController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : CameraController.cs 7 | * Desc : 플레이어를 쿼터뷰 모드로 따라다니는 카메라 기능 8 | * 9 | & Functions 10 | & [Public] 11 | & : SetPlayer() - 플레이어 Prefab 받기 12 | & 13 | & [Private] 14 | & : QuarterViewUpdate() - 쿼터뷰 모드로 플레이어 따라가기 15 | * 16 | */ 17 | 18 | public class CameraController : MonoBehaviour 19 | { 20 | [SerializeField] 21 | private Define.CameraMode _mode = Define.CameraMode.QuarterView; 22 | [SerializeField] 23 | private Vector3 _delta; 24 | [SerializeField] 25 | private GameObject _player = null; 26 | 27 | private RaycastHit hit; 28 | 29 | public void SetPlayer(GameObject go) { _player = go; } 30 | 31 | void LateUpdate() 32 | { 33 | QuarterViewUpdate(); 34 | } 35 | 36 | // 카메라 위치 이동을 마지막 업데이트에 실행함으로 써 떨림현상 완화 37 | private void QuarterViewUpdate() 38 | { 39 | if (_mode == Define.CameraMode.QuarterView) 40 | { 41 | if (_player.isValid() == false) 42 | return; 43 | 44 | // 플레이어가 오브젝트에 가려져있다면 가깝게 이동 45 | if (Physics.Raycast(_player.transform.position, _delta, out hit, _delta.magnitude, 1 << 10)) // 10 : Block 46 | { 47 | float dist = (hit.point - _player.transform.position).magnitude * 0.8f; 48 | transform.position = (_player.transform.position + Vector3.up) + _delta.normalized * dist; 49 | } 50 | else 51 | { 52 | transform.position = _player.transform.position + _delta; 53 | transform.LookAt(_player.transform); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Scripts/Controllers/Player/CursorController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : CursorController.cs 7 | * Desc : 마우스 커서 icon을 상황마다 바꿔주는 기능 8 | * 9 | & Functions 10 | & [Private] 11 | & : CursorUpdate() - 상황마다 마우스 커서 Update 12 | * 13 | */ 14 | 15 | public class CursorController : MonoBehaviour 16 | { 17 | // 마우스 커서 상태 18 | public enum CursorType 19 | { 20 | None, 21 | Attack, 22 | Hand, 23 | Loot, 24 | } 25 | 26 | private CursorType _cursorType = CursorType.None; 27 | 28 | private RaycastHit hit; 29 | private Texture2D _attackIcon; // 공격 icon 30 | private Texture2D _handIcon; // 기본 icon 31 | private Texture2D _lootIcon; // npc icon 32 | 33 | private int _mask = (1 << (int)Define.Layer.Ground) | (1 << (int)Define.Layer.Monster) | (1 << (int)Define.Layer.Npc); 34 | 35 | void Start() 36 | { 37 | // 커서 텍스쳐 가져오기 38 | _attackIcon = Managers.Resource.Load("Textures/Cursor/Attack"); 39 | _handIcon = Managers.Resource.Load("Textures/Cursor/Hand"); 40 | _lootIcon = Managers.Resource.Load("Textures/Cursor/Loot"); 41 | 42 | // Hand icon 커서에 적용 43 | Cursor.SetCursor(_handIcon, new Vector2(_handIcon.width / 3.1f, 0), CursorMode.Auto); 44 | _cursorType = CursorType.Hand; 45 | } 46 | 47 | void Update() 48 | { 49 | CursorUpdate(); 50 | } 51 | 52 | private void CursorUpdate() 53 | { 54 | // 꾹 누르면 아이콘 유지 55 | if (Input.GetMouseButton(0)) 56 | return; 57 | 58 | // 마우스 포인트 가져오기 59 | Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 60 | 61 | if (Physics.Raycast(ray, out hit, 100f, _mask)) 62 | { 63 | // NPC 64 | if (hit.collider.gameObject.layer == (int)Define.Layer.Npc) 65 | { 66 | if (_cursorType != CursorType.Loot) 67 | { 68 | Cursor.SetCursor(_lootIcon, new Vector2(_lootIcon.width / 4.5f, _lootIcon.height / 2), CursorMode.Auto); 69 | _cursorType = CursorType.Loot; 70 | } 71 | return; 72 | } 73 | // Monster 74 | else if (hit.collider.gameObject.layer == (int)Define.Layer.Monster) 75 | { 76 | if (_cursorType != CursorType.Attack) 77 | { 78 | Cursor.SetCursor(_attackIcon, new Vector2(_attackIcon.width / 3.9f, 0), CursorMode.Auto); 79 | _cursorType = CursorType.Attack; 80 | } 81 | return; 82 | } 83 | // Default 84 | else if (_cursorType != CursorType.Hand) 85 | { 86 | Cursor.SetCursor(_handIcon, new Vector2(_handIcon.width / 3.1f, 0), CursorMode.Auto); 87 | _cursorType = CursorType.Hand; 88 | } 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /Scripts/Controllers/Player/MapCameraController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : MapCameraController.cs 7 | * Desc : 미니맵 전용 카메라 8 | * 9 | & Functions 10 | & : FixedUpdate() - 하늘에서 플레이어 추격 11 | * 12 | */ 13 | 14 | public class MapCameraController : MonoBehaviour 15 | { 16 | [SerializeField] 17 | private float height; 18 | 19 | void FixedUpdate() 20 | { 21 | if (Managers.Game.GetPlayer().isValid() == false) 22 | return; 23 | 24 | // 플레이어 따라다니기 25 | transform.position = Managers.Game.GetPlayer().transform.position + (Vector3.up * height); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Scripts/Controllers/Player/PlayerAnimEvent.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : PlayerAnimEvent.cs 7 | * Desc : 플레이어의 공격, 스킬 등의 애니메이션 Event 관리 (공격마다 각 사거리가 다르므로 하드코딩) 8 | * 9 | ^ Tip : OnParticleCollision() 통해 파티클로도 접촉을 확인할 수 있기 때문에 다음 부터는 이걸 사용해야겠다. 10 | * 11 | */ 12 | 13 | public class PlayerAnimEvent : MonoBehaviour 14 | { 15 | [SerializeField] 16 | private CapsuleCollider capsuleCollider; 17 | 18 | private int nextSkillIndex = 0; // 스킬 콤보 체크용 19 | 20 | private const int X_Axis = 0, Y_Axis = 1, Z_Axis = 2; 21 | 22 | #region 공격 사거리 23 | 24 | // 공격 사이즈 클래스 25 | private class AttackSize 26 | { 27 | public float x; 28 | public float y; 29 | public float z; 30 | public float redius; 31 | public float height; 32 | public int direction; // x: 0, y: 1, z: 2 33 | } 34 | 35 | // Id 101 공격 범위 (트리플 슬래쉬) 36 | private AttackSize skill101 = new AttackSize() 37 | { 38 | x = 0, y = 0, z = -0.35f, redius = 2.35f, height = 4.5f, direction = Y_Axis, 39 | }; 40 | 41 | // Id 102 공격 범위 (라이징 슬래쉬) 42 | private AttackSize[] skill102 = new AttackSize[] 43 | { 44 | new AttackSize() 45 | { 46 | x = 0.3f, y = 0, z = 1.23f, redius = 0.5f, height = 3.5f, direction = Z_Axis, 47 | }, 48 | new AttackSize() 49 | { 50 | x = 0.43f, y = 0.56f, z = 1f, redius = 0.93f, height = 3.6f, direction = Y_Axis, 51 | }, 52 | new AttackSize() 53 | { 54 | x = 0, y = 0, z = 0f, redius = 2.35f, height = 4.5f, direction = Y_Axis, 55 | }, 56 | }; 57 | 58 | // Id 103 공격 범위 (회전의 칼날) 59 | private AttackSize skill103 = new AttackSize() 60 | { 61 | x = -0.4f, y = 0, z = -0.6f, redius = 4.4f, height = 8.7f, direction = Y_Axis, 62 | }; 63 | 64 | // Id 104 공격 범위 (어둠의 칼날) 65 | private AttackSize skill104 = new AttackSize() 66 | { 67 | x = 0, y = 0, z = -0.4f, redius = 2.8f, height = 5.5f, direction = Y_Axis, 68 | }; 69 | 70 | // Id 105 공격 범위 (궁극의 일격) 71 | private AttackSize skill105 = new AttackSize() 72 | { 73 | x = 0, y = 0, z = 6.1f, redius = 1.2f, height = 11.7f, direction = Z_Axis, 74 | }; 75 | 76 | // Id 106 공격 범위 (칼날 섬멸) 77 | private AttackSize[] skill106 = new AttackSize[] 78 | { 79 | new AttackSize() 80 | { 81 | x = 0f, y = 0, z = 0.13f, redius = 1.23f, height = 3.4f, direction = X_Axis, 82 | }, 83 | new AttackSize() 84 | { 85 | x = 0f, y = 0f, z = 3.5f, redius = 3f, height = 9.4f, direction = X_Axis, 86 | }, 87 | new AttackSize() 88 | { 89 | x = 0f, y = 0f, z = 3.5f, redius = 3f, height = 9.4f, direction = X_Axis, 90 | }, 91 | new AttackSize() 92 | { 93 | x = 0f, y = 0f, z = 3.5f, redius = 3f, height = 9.4f, direction = X_Axis, 94 | }, 95 | }; 96 | 97 | // Id 107 공격 범위 (궁극의 칼날) 98 | private AttackSize skill107 = new AttackSize() 99 | { 100 | x = 0f, y = 0f, z = -0.4f, redius = 7f, height = 0f, direction = Y_Axis, 101 | }; 102 | 103 | #endregion 104 | 105 | // 기본 검 공격 106 | private void OnBasicAttack() 107 | { 108 | capsuleCollider.gameObject.SetActive(true); 109 | } 110 | 111 | // skill 101 : 트리플 슬래쉬 112 | private void OnTripleSlash() 113 | { 114 | OnSize(skill101); 115 | } 116 | 117 | // skill 102 : 라이징 슬래쉬 118 | private void OnRisingSlash() 119 | { 120 | OnSize(skill102[nextSkillIndex]); 121 | 122 | ++nextSkillIndex; 123 | if (nextSkillIndex == skill102.Length) 124 | nextSkillIndex = 0; 125 | } 126 | 127 | // skill 103 : 회전의 칼날 128 | private void OnRotationBlade() 129 | { 130 | OnSize(skill103); 131 | } 132 | 133 | // skill 104 : 어둠의 칼날 134 | private void OnDarkBlade() 135 | { 136 | OnSize(skill104); 137 | } 138 | 139 | // skill 105 : 궁극의 일격 140 | private void OnBigSwordSlash() 141 | { 142 | OnSize(skill105); 143 | } 144 | 145 | // skill 106 : 칼날 섬멸 146 | private void OnBladeAnnihilation() 147 | { 148 | OnSize(skill106[nextSkillIndex]); 149 | 150 | ++nextSkillIndex; 151 | if (nextSkillIndex == skill106.Length) 152 | nextSkillIndex = 0; 153 | } 154 | 155 | // skill 107 : 궁극의 칼날 156 | private void OnEventualityBlade() 157 | { 158 | OnSize(skill107); 159 | } 160 | 161 | private void OnSize(AttackSize size) 162 | { 163 | capsuleCollider.gameObject.SetActive(true); 164 | SetSize(size); 165 | } 166 | 167 | private void SetSize(AttackSize size) 168 | { 169 | capsuleCollider.direction = size.direction; 170 | // capsuleCollider.center.Set(size.x, size.y, size.z); 171 | capsuleCollider.center = new Vector3(size.x, size.y, size.z); 172 | capsuleCollider.radius = size.redius; 173 | capsuleCollider.height = size.height; 174 | } 175 | } -------------------------------------------------------------------------------- /Scripts/Controllers/Player/PlayerAttackCollistion.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : PlayerAttackCollistion.cs 7 | * Desc : 플레이어 전방에 Collider를 활성화하여 접촉된 몬스터에게 데미지 반영 8 | * 9 | & Functions 10 | & : OnEnable() - 활성화 시 0.1f 후 비활성화 (DelayActiveFalse() Invoke 호출) 11 | & : OnDisable() - 비활성화 시 스킬 콤보 체크 및 콜라이더 사이즈 초기화 12 | & : OnTriggerEnter() - 몬스터 접촉 시 데미지 반영 13 | & 14 | & [Private] 15 | & : DelayActiveFalse() - 비활성화 딜레이 16 | & : BasicColliderSize() - 콜라이더 기본 사이즈 초기화 17 | * 18 | */ 19 | 20 | public class PlayerAttackCollistion : MonoBehaviour 21 | { 22 | private int skillIndex = 0; // 스킬 콤보 공격력 List index 23 | 24 | private CapsuleCollider capsuleCollider; 25 | 26 | [SerializeField] 27 | private PlayerController player; 28 | 29 | void Start() 30 | { 31 | capsuleCollider = GetComponent(); 32 | 33 | skillIndex = 0; 34 | 35 | gameObject.SetActive(false); 36 | } 37 | 38 | void OnEnable() 39 | { 40 | Invoke("DelayActiveFalse", 0.1f); 41 | } 42 | 43 | void OnDisable() 44 | { 45 | BasicColliderSize(); 46 | 47 | // 마지막 스킬 공격이라면 index 초기화 48 | if (player.currentSkill.IsNull() == false) 49 | { 50 | if (skillIndex == player.currentSkill.powerList.Count - 1) 51 | skillIndex = 0; 52 | else 53 | skillIndex++; 54 | } 55 | } 56 | 57 | void OnTriggerEnter(Collider other) 58 | { 59 | if (other.CompareTag("Monster")) 60 | { 61 | if (player.State == Define.State.Skill) 62 | { 63 | if (player.currentSkill.powerList.Contains(skillIndex) == false) 64 | skillIndex = 0; 65 | 66 | // 스킬 공격 67 | int skillDamage = player.currentSkill.powerList[skillIndex] * (Managers.Game.Attack / 2); 68 | other.GetComponent().OnAttacked(skillDamage); 69 | } 70 | else 71 | other.GetComponent().OnAttacked(); // 기본 공격 72 | } 73 | } 74 | 75 | // Invoke 호출 76 | private void DelayActiveFalse() 77 | { 78 | gameObject.SetActive(false); 79 | } 80 | 81 | // 기본 콜라이더 사이즈 82 | private void BasicColliderSize() 83 | { 84 | capsuleCollider.center = new Vector3(0, 0, 0.4f); 85 | capsuleCollider.radius = 1.2f; 86 | capsuleCollider.height = 2.4f; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Scripts/Data/ArmorItemData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : ArmorItemData.cs 8 | * Desc : 방어 아이템 스탯 데이터 (방어도, 체력, 마나, 속도) 9 | * 10 | & Functions 11 | & : ArmorClone() - 방어구 깊은 복사 12 | * 13 | */ 14 | 15 | [Serializable] 16 | public class ArmorItemData : EquipmentData 17 | { 18 | public Define.ArmorType armorType = Define.ArmorType.Unknown; 19 | 20 | // 기본 스탯 21 | public int defnece=0; 22 | public int hp=0; 23 | public int mp=0; 24 | public int moveSpeed=0; 25 | 26 | // 강화 시 추가 스탯 27 | public int addDefnece=0; 28 | public int addHp=0; 29 | public int addMp=0; 30 | public int addMoveSpeed=0; 31 | 32 | [NonSerialized] 33 | public List charEquipment; // 캐릭터 파츠 활성화 34 | 35 | // 깊은 복사용 36 | public ArmorItemData ArmorClone() 37 | { 38 | ArmorItemData armor = new ArmorItemData(); 39 | 40 | (this as EquipmentData).EquipmentClone(armor); 41 | 42 | armor.armorType = this.armorType; 43 | armor.defnece = this.defnece; 44 | armor.hp = this.hp; 45 | armor.mp = this.mp; 46 | armor.moveSpeed = this.moveSpeed; 47 | 48 | armor.charEquipment = this.charEquipment; 49 | 50 | return armor; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Scripts/Data/EquipmentData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : EquipmentData.cs 7 | * Desc : 모든 장비 아이템의 부모 8 | * 9 | & Functions 10 | & : EquipmentClone() - 장비 깊은 복사 11 | * 12 | */ 13 | 14 | public class EquipmentData : ItemData 15 | { 16 | public int minLevel; // 최소 장착 레벨 17 | public int upgradeValue = 0; // +1 강화마다 올라가는 수치 18 | public int upgradeCount = 0; // 업그레이드 횟수 19 | 20 | public void EquipmentClone(EquipmentData equip) 21 | { 22 | equip.minLevel = this.minLevel; 23 | equip.upgradeValue = this.upgradeValue; 24 | equip.upgradeCount = this.upgradeCount; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Scripts/Data/ItemData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : ItemData.cs 8 | * Desc : 모든 아이템의 부모 9 | * 10 | & Functions 11 | & : ItemClone() - 아이템 깊은 복사 12 | * 13 | */ 14 | 15 | [Serializable] 16 | public abstract class ItemData 17 | { 18 | public int id; 19 | public string itemName; 20 | public Define.ItemType itemType = Define.ItemType.Unknown; 21 | public Define.itemGrade itemGrade = Define.itemGrade.Common; 22 | public int itemPrice; 23 | public int itemMaxCount = 99; 24 | 25 | public GameObject itemObject; 26 | 27 | [TextArea] 28 | public string itemDesc; 29 | 30 | public Sprite itemIcon; 31 | 32 | // Deep Copy (깊은 복사) 33 | public ItemData ItemClone() 34 | { 35 | if (this is EquipmentData) 36 | { 37 | if (this is ArmorItemData) 38 | { 39 | return AddItemValue((this as ArmorItemData).ArmorClone()); 40 | } 41 | else if (this is WeaponItemData) 42 | { 43 | return AddItemValue((this as WeaponItemData).WeaponClone()); 44 | } 45 | } 46 | else if (this is UseItemData) 47 | { 48 | return AddItemValue((this as UseItemData).UseClone()); 49 | } 50 | 51 | return null; 52 | } 53 | 54 | T AddItemValue(T item) where T : ItemData 55 | { 56 | item.id = this.id; 57 | item.itemName = this.itemName; 58 | item.itemType = this.itemType; 59 | item.itemGrade = this.itemGrade; 60 | item.itemPrice = this.itemPrice; 61 | item.itemMaxCount = this.itemMaxCount; 62 | item.itemObject = this.itemObject; 63 | item.itemDesc = this.itemDesc; 64 | item.itemIcon = this.itemIcon; 65 | 66 | return item; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Scripts/Data/LevelData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : LevelData.cs 8 | * Desc : 레벨 데이터 9 | */ 10 | 11 | public class LevelData 12 | { 13 | public int level; 14 | public int totalExp; 15 | public int statPoint; 16 | public int maxHp; 17 | public int maxMp; 18 | } 19 | -------------------------------------------------------------------------------- /Scripts/Data/QuestData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : QuestData.cs 8 | * Desc : 퀘스트 데이터 9 | * 10 | & Functions 11 | & : QuestClear() - 퀘스트 성공 보상 지급 12 | * 13 | */ 14 | 15 | [Serializable] 16 | public class RewardItem 17 | { 18 | public int ItemId; 19 | public int itemCount; 20 | } 21 | 22 | [Serializable] 23 | public class QuestData 24 | { 25 | public int id; 26 | public string titleName; 27 | public Define.QuestType questType; 28 | public int minLevel; 29 | public int targetId; 30 | public int targetCount; 31 | public int currnetTargetCount; 32 | public int rewardGold; 33 | public int rewardExp; 34 | public List rewardItems; 35 | public string description; 36 | public string targetDescription; 37 | public Vector3 targetPos; 38 | 39 | public bool isAccept = false; // 수락 상태 40 | public bool isClear = false; // 클리어 상태 41 | 42 | // 퀘스트 성공 43 | public void QuestClear() 44 | { 45 | isClear = true; 46 | 47 | // 보상 지급 48 | foreach(RewardItem rewardItem in rewardItems) 49 | Managers.Game._playScene._inventory.AcquireItem(Managers.Data.CallItem(rewardItem.ItemId), rewardItem.itemCount); 50 | 51 | Managers.Game.Gold += rewardGold; 52 | Managers.Game.Exp += rewardExp; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Scripts/Data/SkillData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : SkillData.cs 8 | * Desc : 스킬 데이터 9 | */ 10 | 11 | [Serializable] 12 | public class SkillData 13 | { 14 | public int skillId; 15 | public string skillName; 16 | public int minLevel; 17 | public int skillCoolDown; 18 | public int skillConsumMp; 19 | public bool isCoolDown = false; 20 | public bool isLock = true; 21 | public string discription; 22 | public Sprite skillSprite; 23 | public List powerList; 24 | } -------------------------------------------------------------------------------- /Scripts/Data/SkinnedData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : SkinnedData.cs 8 | * Desc : 커스텀이나 세이브를 불러올 때 사용될 데이터 9 | * [SkinnedMeshRenderer 교체 방법]: https://lhuhyeon.github.io/posts/Unity-SkinnedMeshRenderer-Change/ 10 | */ 11 | 12 | [Serializable] 13 | public class SkinnedData 14 | { 15 | public string sharedMeshName; 16 | public Bounds bounds; 17 | public List bones; 18 | public string rootBoneName; 19 | } 20 | -------------------------------------------------------------------------------- /Scripts/Data/StartData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Xml.Serialization; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : StartData.cs 8 | * Desc : 시작 기본 데이터 9 | */ 10 | 11 | public class StartData 12 | { 13 | public int Id; 14 | public int totalExp; 15 | public int exp; 16 | public int level; 17 | public int maxHp; 18 | public int maxMp; 19 | public int STR; 20 | public int MoveSpeed; 21 | public int LUK; 22 | public int gold; 23 | } -------------------------------------------------------------------------------- /Scripts/Data/TalkData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : TalkData.cs 7 | * Desc : 대화 데이터 8 | */ 9 | 10 | public class TalkData 11 | { 12 | public int id; 13 | public string basicsTalk; 14 | public List questStartTalk; 15 | public string acceptTalk; 16 | public string refusalTalk; 17 | public string procTalk; 18 | public string clearTalk; 19 | } 20 | -------------------------------------------------------------------------------- /Scripts/Data/UseItemData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : UseItemData.cs 8 | * Desc : 소비 아이템 데이터 9 | * 10 | & Functions 11 | & : UseItem() - 아이템 사용 (체력 or 마나) 12 | & : UseClone() - 소비 아이템 깊은 복사 13 | * 14 | */ 15 | 16 | [Serializable] 17 | public class UseItemData : ItemData 18 | { 19 | public Define.UseType useType = Define.UseType.Unknown; 20 | public int useValue = 0; 21 | public int itemCount = 0; 22 | 23 | public bool UseItem(ItemData item) 24 | { 25 | if ((item is UseItemData) == false) 26 | return false; 27 | 28 | UseItemData useItem = item as UseItemData; 29 | 30 | if (useItem.useType == Define.UseType.Hp) 31 | Managers.Game.Hp += useItem.useValue; 32 | else if (useItem.useType == Define.UseType.Mp) 33 | Managers.Game.Mp += useItem.useValue; 34 | 35 | return true; 36 | } 37 | 38 | public UseItemData UseClone() 39 | { 40 | UseItemData useItem = new UseItemData(); 41 | useItem.useType = this.useType; 42 | useItem.useValue = this.useValue; 43 | useItem.itemCount = this.itemCount; 44 | 45 | return useItem; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Scripts/Data/WeaponItemData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : WeaponItemData.cs 8 | * Desc : 무기 아이템 데이터 (공격력) 9 | * 10 | & Functions 11 | & : WeaponClone() - 무기 아이템 깊은 복사 12 | * 13 | */ 14 | 15 | [Serializable] 16 | public class WeaponItemData : EquipmentData 17 | { 18 | public Define.WeaponType weaponType = Define.WeaponType.Unknown; 19 | 20 | public int attack=0; 21 | public int addAttack=0; 22 | 23 | [NonSerialized] 24 | public GameObject charEquipment; 25 | 26 | public WeaponItemData WeaponClone() 27 | { 28 | WeaponItemData weapon = new WeaponItemData(); 29 | 30 | (this as EquipmentData).EquipmentClone(weapon); 31 | 32 | weapon.weaponType = this.weaponType; 33 | weapon.attack = this.attack; 34 | weapon.charEquipment = this.charEquipment; 35 | 36 | return weapon; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Scripts/Manager/Core/InputManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.EventSystems; 5 | using System; 6 | 7 | /* 8 | * File : InputManager.cs 9 | * Desc : 플레이어의 모든 입력(마우스, 키보드)을 확인하고 반환 10 | * [ Rookiss의 MMORPG Game Part 3 참고. ] 11 | */ 12 | 13 | public class InputManager 14 | { 15 | // 키 입력 메소드들을 한번에 실행하기 위한 변수 16 | public Action KeyAction = null; 17 | public Action MouseAction = null; 18 | 19 | bool _leftPressed = false; 20 | bool _rightPressed = false; 21 | 22 | float _leftPressedTime; 23 | float _rightPressedTime; 24 | 25 | public void OnUpdate() 26 | { 27 | // 상호작용 확인 28 | if (Managers.Game.IsInteract == true || Managers.Game.isPopups[Define.Popup.Talk] == true) 29 | return; 30 | 31 | // 키입력 메소드가 KeyAction안에 존재하는가? 32 | if (Input.anyKey && KeyAction.IsNull() == false) 33 | KeyAction.Invoke(); 34 | 35 | // UI를 클릭했을 때 36 | if (EventSystem.current.IsPointerOverGameObject()) 37 | return; 38 | 39 | if (MouseAction.IsNull() == false) 40 | { 41 | if (Input.GetMouseButton(1)) 42 | { 43 | if (!_rightPressed) 44 | { 45 | MouseAction.Invoke(Define.MouseEvent.RightDown); 46 | _rightPressedTime = Time.time; 47 | } 48 | MouseAction.Invoke(Define.MouseEvent.RightPress); 49 | _rightPressed = true; 50 | } 51 | else{ 52 | if (_rightPressed) 53 | { 54 | if (Time.time < _rightPressedTime * 0.2f) 55 | MouseAction.Invoke(Define.MouseEvent.RightClick); 56 | 57 | MouseAction.Invoke(Define.MouseEvent.RightUp); 58 | } 59 | _rightPressed = false; 60 | _rightPressedTime = 0f; 61 | } 62 | 63 | if (Input.GetMouseButtonDown(0)) 64 | { 65 | if (!_leftPressed) 66 | MouseAction.Invoke(Define.MouseEvent.LeftDown); 67 | } 68 | 69 | if (Input.GetMouseButton(0)) 70 | { 71 | _leftPressedTime = Time.time; 72 | MouseAction.Invoke(Define.MouseEvent.LeftPress); 73 | _leftPressed = true; 74 | } 75 | else{ 76 | if (_leftPressed) 77 | { 78 | if (Time.time < _leftPressedTime * 0.2f) 79 | MouseAction.Invoke(Define.MouseEvent.LeftClick); 80 | 81 | MouseAction.Invoke(Define.MouseEvent.LeftUp); 82 | } 83 | _leftPressed = false; 84 | _leftPressedTime = 0f; 85 | } 86 | } 87 | } 88 | 89 | public void Clear() 90 | { 91 | KeyAction = null; 92 | MouseAction = null; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Scripts/Manager/Core/PoolManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : PoolManager.cs 7 | * Desc : Poolable 컴포넌트가 있는 오브젝트를 Pool 관리해준다 ( ResourceManager의 보조역할 ) 8 | * [ Rookiss의 MMORPG Game Part 3 참고. ] 9 | */ 10 | 11 | public class PoolManager 12 | { 13 | class Pool 14 | { 15 | public GameObject Original { get; private set; } // Pool을 진행할 대표 변수 16 | public Transform Root { get; set; } 17 | 18 | Stack _poolStack = new Stack(); 19 | 20 | // Pool 초기 설정 21 | public void Init(GameObject original, int count = 5) 22 | { 23 | Original = original; 24 | Root = new GameObject().transform; // Pool을 담을 Root Object 25 | Root.name = $"{original.name}_Root"; 26 | 27 | for (int i = 0; i < count; i++) // count 만큼 pool Object 생성 후 Stack에 push 28 | Push(Create()); 29 | } 30 | 31 | Poolable Create() 32 | { 33 | GameObject go = Object.Instantiate(Original); // Original 복제 34 | go.name = Original.name; 35 | return go.GetOrAddComponent(); // Poolable 컴포넌트 생성 36 | } 37 | 38 | public void PushCreate(int count = 5) 39 | { 40 | for (int i = 0; i < count; i++) 41 | Push(Create()); 42 | } 43 | 44 | // 객체 생성 메소드 45 | public void Push(Poolable poolable) 46 | { 47 | if (poolable.IsNull() == true) 48 | return; 49 | 50 | poolable.transform.SetParent(Root); 51 | poolable.gameObject.SetActive(false); 52 | poolable.IsUsing = false; 53 | 54 | _poolStack.Push(poolable); 55 | } 56 | 57 | // 객체 반환 메소드 58 | public Poolable Pop(Transform parent = null) 59 | { 60 | Poolable poolable; 61 | 62 | if (_poolStack.Count > 0) 63 | poolable = _poolStack.Pop(); 64 | else 65 | poolable = Create(); 66 | 67 | poolable.gameObject.SetActive(true); 68 | 69 | // DontDestroyOnLoad 해제 용도 (SceneManager 객체를 이용) 70 | if (parent.IsNull() == true) 71 | poolable.transform.SetParent(Managers.Scene.CurrentScene.transform); 72 | 73 | poolable.transform.SetParent(parent); 74 | poolable.IsUsing = true; 75 | 76 | return poolable; 77 | } 78 | } 79 | 80 | Dictionary _pool = new Dictionary(); // pool 객체 저장 81 | Transform _root; // 오브젝트 생성 경로 82 | 83 | public void Init() 84 | { 85 | // Pool Object를 담을 부모 객체(_root) 경로 설정 86 | if (_root.IsNull() == true) 87 | { 88 | _root = new GameObject { name = "@Pool_Root" }.transform; 89 | Object.DontDestroyOnLoad(_root); 90 | } 91 | } 92 | 93 | // 새로운 pool 생성 후 저장 94 | public void CreatePool(GameObject original, int count = 5) 95 | { 96 | // 이미 풀에 존재하면 생성 취소 97 | if (_pool.ContainsKey(original.name) == true) 98 | return; 99 | 100 | AddCreatePool(original, count); 101 | } 102 | 103 | public void AddCreatePool(GameObject original, int count = 5) 104 | { 105 | if (_pool.ContainsKey(original.name) == true) 106 | { 107 | _pool[original.name].PushCreate(count); 108 | return; 109 | } 110 | 111 | Pool pool = new Pool(); 112 | pool.Init(original, count); // Pool 생성 113 | pool.Root.SetParent(_root); // _root(@Pool_Root)를 부모 객체로 설정 114 | 115 | _pool.Add(original.name, pool); 116 | } 117 | 118 | // 기존 pool 저장 메소드 119 | public void Push(Poolable poolable) 120 | { 121 | string name = poolable.gameObject.name; 122 | 123 | if (_pool.ContainsKey(name) == false) 124 | { 125 | GameObject.Destroy(poolable.gameObject); 126 | return; 127 | } 128 | 129 | _pool[name].Push(poolable); 130 | } 131 | 132 | // pool 반환 메소드 133 | public Poolable Pop(GameObject original, Transform parent = null) 134 | { 135 | // Poolable 컴포넌트가 붙은 객체인데 저장된 Key가 없을 경우 생성 136 | if (_pool.ContainsKey(original.name) == false) 137 | CreatePool(original); 138 | 139 | return _pool[original.name].Pop(parent); // Pop 진행 140 | } 141 | 142 | // pool 객체 읽기 메소드 143 | public GameObject GetOriginal(string name) 144 | { 145 | if (_pool.ContainsKey(name) == false) 146 | return null; 147 | 148 | return _pool[name].Original; 149 | } 150 | 151 | // mmorpg에서 지역마다 쓰는 객체다 다를 수도 있기 때문에 일단 구현! 152 | public void Clear() 153 | { 154 | // @Pool_Root 안에 있는 객체 모두 제거 155 | foreach(Transform child in _root) 156 | GameObject.Destroy(child.gameObject); 157 | 158 | _pool.Clear(); // Pool 초기화 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /Scripts/Manager/Core/Poolable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : Poolable.cs 7 | * Desc : Poolable 스크립트가 객체에 컴포넌트로 추가되어 있는지 확인하여 Pool 여부를 체크한다. 8 | * [ Rookiss의 MMORPG Game Part 3 참고. ] 9 | */ 10 | 11 | public class Poolable : MonoBehaviour 12 | { 13 | public bool IsUsing; // 아무것도 없어 허전하니 풀 중인지 알려주는 변수를 넣어주었다. 14 | } 15 | -------------------------------------------------------------------------------- /Scripts/Manager/Core/ResourceManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : ResourceManager.cs 7 | * Desc : Resource 폴더 사용 및 유니티에서 제공하는 명령어를 더 쉽게 사용하기 위한 매니저 8 | * [ Rookiss의 MMORPG Game Part 3 참고. ] 9 | */ 10 | 11 | public class ResourceManager 12 | { 13 | // 객체 읽기 14 | public T Load(string path) where T : Object 15 | { 16 | // 찾으려는 타입이 GameObject일 경우 Pool에서 찾음. 17 | if (typeof(T) == typeof(GameObject)) 18 | { 19 | string name = path; 20 | int index = name.LastIndexOf('/'); // '/' 문자까지의 문자열 개수 반환 21 | if (index >= 0) 22 | name.Substring(index + 1); // name을 index+1 문자열 위치에서 반환 23 | 24 | GameObject go = Managers.Pool.GetOriginal(name); 25 | if (go.IsNull() == false) 26 | return go as T; 27 | // return go.GetComponent(); 28 | } 29 | 30 | // Pool에서 찾지 못하면 Resources 경로에서 가져옴. 31 | // [유니티에서 제공하는 Resources 폴더 안에 해당 경로의 프리팹을 읽어오는 클래스] 32 | return Resources.Load(path); 33 | } 34 | 35 | public GameObject Instantiate(GameObject obj, Transform parent = null) 36 | { 37 | if (obj.IsNull() == true) 38 | { 39 | Debug.Log("객체가 존재하지 않습니다."); 40 | return null; 41 | } 42 | 43 | // 풀링이 적용된 객체인지 확인 44 | if (obj.GetComponent().IsNull() == false) 45 | return Managers.Pool.Pop(obj, parent).gameObject; 46 | 47 | // 해당 original 프리팹을 parent의 자식 객체로 생성하기 48 | GameObject go = Object.Instantiate(obj, parent); 49 | go.name = obj.name; // (Clone) 이름을 없애기 위한 코드 50 | 51 | return go; 52 | } 53 | 54 | // 프리팹 생성 55 | public GameObject Instantiate(string path, Transform parent = null) 56 | { 57 | // original 프리팹 객체 읽어오기. 58 | GameObject original = Load($"Prefabs/{path}"); 59 | 60 | if (original.IsNull() == true) 61 | { 62 | Debug.Log($"Failed to load prefab : {path}"); 63 | return null; 64 | } 65 | 66 | // 풀링이 적용된 객체인지 확인 67 | if (original.GetComponent().IsNull() == false) 68 | { 69 | // UI 팝업이면 하나만 생성 70 | if (original.GetComponent().IsNull() == false) 71 | Managers.Pool.CreatePool(original, 1); 72 | 73 | return Managers.Pool.Pop(original, parent).gameObject; 74 | } 75 | 76 | // 해당 original 프리팹을 parent의 자식 객체로 생성하기 77 | GameObject go = Object.Instantiate(original, parent); 78 | go.name = original.name; // (Clone) 이름을 없애기 위한 코드 79 | 80 | return go; 81 | } 82 | 83 | // 오브젝트 삭제 84 | public void Destroy(GameObject go) 85 | { 86 | if (go.IsNull() == true) 87 | return; 88 | 89 | // 만약에 풀링이 필요한 아이라면 PoolManager한테 위탁 90 | Poolable poolable = go.GetComponent(); 91 | if (poolable.IsNull() == false) 92 | { 93 | Managers.Pool.Push(poolable); 94 | return; 95 | } 96 | 97 | Object.Destroy(go); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Scripts/Manager/Core/SceneManagerEx.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | using UnityEngine.SceneManagement; 6 | 7 | /* 8 | * File : SceneManagerEx.cs 9 | * Desc : 씬 로드 매니저 10 | * [ Rookiss의 MMORPG Game Part 3 참고. ] 11 | */ 12 | 13 | public class SceneManagerEx 14 | { 15 | public BaseScene CurrentScene { get { return GameObject.FindObjectOfType(); } } 16 | 17 | public void LoadScene(Define.Scene type) 18 | { 19 | Managers.Clear(); 20 | SceneManager.LoadScene(GetSceneName(type)); 21 | } 22 | 23 | public AsyncOperation LoadAsynScene(Define.Scene type) 24 | { 25 | AsyncOperation operation = SceneManager.LoadSceneAsync(Managers.Scene.GetSceneName(type)); 26 | operation.allowSceneActivation = false; 27 | 28 | return operation; 29 | } 30 | 31 | string GetSceneName(Define.Scene type) 32 | { 33 | string name = System.Enum.GetName(typeof(Define.Scene), type); 34 | return name; 35 | } 36 | 37 | public void Clear() 38 | { 39 | CurrentScene.Clear(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Scripts/Manager/Core/UIManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : UIManager.cs 7 | * Desc : UI의 Scene, Popup, WorldSpace 생성/제거를 도와주는 매니저 8 | * [ Rookiss의 MMORPG Game Part 3 참고. ] 9 | */ 10 | 11 | public class UIManager 12 | { 13 | int _order = 10; 14 | 15 | List _popupList = new List(); 16 | UI_Scene _sceneUI = null; 17 | 18 | // 프리팹 오브젝트 부모 (하이라이커 깔끔하게 정리하려고 사용) 19 | public GameObject Root 20 | { 21 | get{ 22 | GameObject root = GameObject.Find("@UI_Root");// 오브젝트 찾기 23 | 24 | if (root.IsNull() == true) 25 | root = new GameObject{name = "@UI_Root"}; // 오브젝트 이름 설정 26 | 27 | return root; 28 | } 29 | } 30 | 31 | // 오브젝트에 Canvas를 추가하고 order을 설정 32 | public void SetCanvas(GameObject go, bool sort = true) 33 | { 34 | Canvas canvas = Util.GetOrAddComponent(go); 35 | canvas.renderMode = RenderMode.ScreenSpaceOverlay; 36 | canvas.overrideSorting = true; 37 | 38 | if (sort) 39 | SetOrder(canvas); 40 | else 41 | canvas.sortingOrder = 0; 42 | } 43 | 44 | public void SetOrder(Canvas canvas) 45 | { 46 | canvas.sortingOrder = _order; 47 | _order++; 48 | } 49 | 50 | // 3D 안에 있는 WorldSpace에서 UI 생성 (캐릭터 체력 UI ...) 51 | public T MakeWorldSpaceUI(Transform parent = null, string name = null) where T : UI_Base 52 | { 53 | if (string.IsNullOrEmpty(name)) 54 | name = typeof(T).Name; 55 | 56 | GameObject go = Managers.Resource.Instantiate($"UI/WorldSpace/{name}"); 57 | 58 | if (parent.IsNull() == false) 59 | go.transform.SetParent(parent); 60 | 61 | Canvas canvas = go.GetOrAddComponent(); 62 | canvas.renderMode = RenderMode.WorldSpace; 63 | canvas.worldCamera = Camera.main; 64 | 65 | return go.GetOrAddComponent(); 66 | } 67 | 68 | public T MakeSubItem(Transform parent = null, string name = null) where T : UI_Base 69 | { 70 | if (string.IsNullOrEmpty(name)) 71 | name = typeof(T).Name; 72 | 73 | GameObject go = Managers.Resource.Instantiate($"UI/SubItem/{name}"); 74 | 75 | if (parent.IsNull() == false) 76 | go.transform.SetParent(parent); 77 | 78 | return go.GetOrAddComponent(); 79 | } 80 | 81 | public T ShowSceneUI(string name = null) where T : UI_Scene 82 | { 83 | // name = null 경우 84 | if (string.IsNullOrEmpty(name)) 85 | name = typeof(T).Name; 86 | 87 | GameObject go = Managers.Resource.Instantiate($"UI/Scene/{name}"); 88 | T sceneUI = Util.GetOrAddComponent(go); 89 | _sceneUI = sceneUI; 90 | 91 | return sceneUI; 92 | } 93 | 94 | // UI에 만들어질 프리팹을 stack에 넣어 order을 관리 95 | public T ShowPopupUI(string name = null) where T : UI_Popup 96 | { 97 | if (string.IsNullOrEmpty(name)) 98 | name = typeof(T).Name; 99 | 100 | // 이미 생성된 Popup이면 종료 101 | if (_popupList.Contains(Managers.Resource.Load($"UI/Popup/{name}")) == true) 102 | return null; 103 | 104 | GameObject go = Managers.Resource.Instantiate($"UI/Popup/{name}"); 105 | T popup = Util.GetOrAddComponent(go); 106 | _popupList.Add(popup); 107 | 108 | go.transform.SetParent(Root.transform); 109 | 110 | return popup; 111 | } 112 | 113 | // 팝업창 켜기 114 | public void OnPopupUI(UI_Popup popup) 115 | { 116 | // 이미 켜져있으면 진행 X 117 | if (popup.gameObject.activeSelf == true) 118 | return; 119 | 120 | _popupList.Add(popup); 121 | Managers.Pool.Pop(popup.gameObject); 122 | SetOrder(popup.GetComponent()); 123 | 124 | Managers.Game.isPopups[popup.popupType] = true; 125 | 126 | popup.transform.SetParent(Root.transform); 127 | } 128 | 129 | // 리스트에 popup이 있나 확인 후 삭제 130 | public void ClosePopupUI(UI_Popup popup) 131 | { 132 | if (_popupList.Count == 0) 133 | return; 134 | 135 | if (_popupList.Contains(popup) == false) 136 | { 137 | Debug.Log("Close Popup Failed!"); 138 | return; 139 | } 140 | 141 | _order--; 142 | 143 | Managers.Game.isPopups[popup.popupType] = false; 144 | _popupList.Remove(popup); 145 | Managers.Resource.Destroy(popup.gameObject); 146 | } 147 | 148 | public bool ClosePopupUI() 149 | { 150 | if (_popupList.Count == 0) 151 | return false; 152 | 153 | foreach(UI_Popup popup in _popupList) 154 | { 155 | if (popup.IsNull() == false) 156 | { 157 | OnClosePopup(popup); 158 | return true; 159 | } 160 | } 161 | 162 | return false; 163 | } 164 | 165 | void OnClosePopup(UI_Popup popup) 166 | { 167 | Managers.Game.isPopups[popup.popupType] = false; 168 | _popupList.Remove(popup); 169 | 170 | // fake null 체크 171 | if (popup.IsFakeNull() == true) 172 | popup = null; 173 | else 174 | Managers.Resource.Destroy(popup.gameObject); 175 | 176 | _order--; 177 | } 178 | 179 | // List 전체 Close 180 | public void CloseAllPopupUI() 181 | { 182 | while (true) 183 | { 184 | if (ClosePopupUI() == false) 185 | break; 186 | } 187 | 188 | _popupList.Clear(); 189 | } 190 | 191 | public void Clear() 192 | { 193 | CloseAllPopupUI(); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /Scripts/Manager/Managers.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : Managers.cs 7 | * Desc : 싱글톤 패턴을 사용하여 모든 매니저에 접근 가능 8 | * [ Rookiss의 MMORPG Game Part 3 참고. ] 9 | */ 10 | 11 | public class Managers : MonoBehaviour 12 | { 13 | private static Managers s_instance; 14 | private static Managers Instance { get { Init(); return s_instance; } } 15 | 16 | #region Contents 17 | 18 | private GameManager _game = new GameManager(); 19 | 20 | public static GameManager Game { get { return Instance._game; } } 21 | 22 | #endregion 23 | 24 | #region Core 25 | 26 | private DataManager _data = new DataManager(); 27 | private InputManager _input = new InputManager(); 28 | private PoolManager _pool = new PoolManager(); 29 | private ResourceManager _resource = new ResourceManager(); 30 | private SceneManagerEx _scene = new SceneManagerEx(); 31 | private UIManager _ui = new UIManager(); 32 | 33 | public static DataManager Data { get { return Instance._data; } } 34 | public static InputManager Input { get { return Instance._input; } } 35 | public static PoolManager Pool { get { return Instance._pool; } } 36 | public static ResourceManager Resource { get { return Instance._resource; } } 37 | public static SceneManagerEx Scene { get { return Instance._scene; } } 38 | public static UIManager UI { get { return Instance._ui; } } 39 | 40 | #endregion 41 | 42 | void Start() 43 | { 44 | Application.targetFrameRate = 50; 45 | 46 | Init(); 47 | } 48 | 49 | void Update() 50 | { 51 | Input.OnUpdate(); 52 | Game.OnUpdate(); 53 | } 54 | 55 | // 싱글톤 메소드 56 | static void Init() 57 | { 58 | if (s_instance.IsNull() == true) 59 | { 60 | GameObject go = GameObject.Find("@Manager"); 61 | 62 | if (go.IsNull() == true) 63 | { 64 | go = new GameObject{name = "@Manager"}; 65 | go.AddComponent(); 66 | Debug.Log("@Manager 생성."); 67 | } 68 | 69 | DontDestroyOnLoad(go); 70 | s_instance = go.GetComponent(); 71 | 72 | s_instance._data.Init(); 73 | s_instance._game.Init(); 74 | s_instance._pool.Init(); 75 | // s_instance._data.Init(); 76 | } 77 | } 78 | 79 | public static void Clear() 80 | { 81 | UI.Clear(); 82 | Scene.Clear(); 83 | Game.Clear(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Scripts/Scenes/BaseScene.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.EventSystems; 5 | 6 | /* 7 | * File : BaseScene.cs 8 | * Desc : 모든 Scene은 BaseScene을 상속받는다. 9 | * BaseScene은 Scene이 로드될 때 기본적인 기능을 수행 10 | */ 11 | 12 | public abstract class BaseScene : MonoBehaviour 13 | { 14 | public Define.Scene SceneType { get; protected set; } = Define.Scene.Unknown; 15 | 16 | void Awake() 17 | { 18 | Init(); 19 | } 20 | 21 | protected virtual void Init() 22 | { 23 | Object obj = GameObject.FindObjectOfType(typeof(EventSystem)); 24 | 25 | if (obj.IsNull() == true) 26 | Managers.Resource.Instantiate("UI/EventSystem").name = "@EventSystem"; 27 | 28 | Camera.main.gameObject.GetOrAddComponent(); 29 | } 30 | 31 | public abstract void Clear(); 32 | } 33 | -------------------------------------------------------------------------------- /Scripts/Scenes/BossScene.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : BossScene.cs 7 | * Desc : BossScene이 Load되면 호출된다. 8 | */ 9 | 10 | public class BossScene : BaseScene 11 | { 12 | [SerializeField] 13 | Transform playerSpawn; 14 | 15 | protected override void Init() 16 | { 17 | base.Init(); 18 | SceneType = Define.Scene.Boss; // 타입 설정 19 | 20 | Camera.main.gameObject.GetOrAddComponent().SetPlayer(Managers.Game.GetPlayer()); 21 | 22 | Managers.Game.GetPlayer().transform.position = playerSpawn.position; 23 | 24 | if (Managers.Game.GetPlayer().IsNull() == false) 25 | { 26 | GameObject clickMoveEffect = Managers.Resource.Instantiate("Effect/ClickMoveEffect"); 27 | clickMoveEffect.SetActive(false); 28 | 29 | Managers.Game.GetPlayer().GetComponent().clickMoveEffect = clickMoveEffect; 30 | } 31 | 32 | Managers.Game._playScene.IsMiniMap(false); 33 | 34 | Managers.Game.StopPlayer(); 35 | } 36 | 37 | public override void Clear() 38 | { 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Scripts/Scenes/CustomScene.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : CustomScene.cs 7 | * Desc : CustomScene이 Load되면 호출된다. (커스텀 캐릭터 생성) 8 | */ 9 | 10 | public class CustomScene : BaseScene 11 | { 12 | [SerializeField] 13 | Transform characterPos; 14 | 15 | protected override void Init() 16 | { 17 | base.Init(); 18 | 19 | SceneType = Define.Scene.PlayerCustom; 20 | 21 | GameObject charCustom = Managers.Resource.Instantiate("CharacterCustom", characterPos); 22 | Managers.UI.ShowSceneUI().custom = charCustom.GetComponent(); 23 | } 24 | 25 | public override void Clear() {} 26 | } 27 | -------------------------------------------------------------------------------- /Scripts/Scenes/DungeonScene.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.AI; 5 | 6 | /* 7 | * File : DungeonScene.cs 8 | * Desc : DungeonScene이 Load되면 호출된다. 9 | */ 10 | 11 | public class DungeonScene : BaseScene 12 | { 13 | [SerializeField] 14 | Transform playerSpawn; 15 | 16 | protected override void Init() 17 | { 18 | base.Init(); 19 | SceneType = Define.Scene.Dungeon; // 타입 설정 20 | 21 | Camera.main.gameObject.GetOrAddComponent().SetPlayer(Managers.Game.GetPlayer()); 22 | 23 | Managers.Game.GetPlayer().transform.position = playerSpawn.position; 24 | 25 | if (Managers.Game.GetPlayer().IsNull() == false) 26 | { 27 | GameObject clickMoveEffect = Managers.Resource.Instantiate("Effect/ClickMoveEffect"); 28 | clickMoveEffect.SetActive(false); 29 | 30 | Managers.Game.GetPlayer().GetComponent().clickMoveEffect = clickMoveEffect; 31 | } 32 | 33 | Managers.Game._playScene.IsMiniMap(false); 34 | 35 | Managers.Game.StopPlayer(); 36 | } 37 | 38 | public override void Clear() 39 | { 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Scripts/Scenes/GameScene.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : GameScene.cs 7 | * Desc : GameScene이 Load되면 호출된다. ( 플레이어, PlayScene 등 생성 ) 8 | */ 9 | 10 | public class GameScene : BaseScene 11 | { 12 | [SerializeField] 13 | Transform playerSpawn; 14 | 15 | protected override void Init() 16 | { 17 | base.Init(); 18 | SceneType = Define.Scene.Game; // 타입 설정 19 | 20 | Managers.Game.defualtSpawn = playerSpawn.position; 21 | 22 | // 플레이어 캐릭터 생성 23 | if (Managers.Game.GetPlayer().IsFakeNull() == true) 24 | { 25 | GameObject _player = Managers.Game.Spawn(Define.WorldObject.Player, "Player"); 26 | _player.transform.position = playerSpawn.position; 27 | DontDestroyOnLoad(_player); 28 | } 29 | 30 | // UI 생성 31 | if (Managers.Game._playScene.IsFakeNull() == true) 32 | { 33 | Managers.Game.Init(); 34 | Managers.Game._playScene = Managers.UI.ShowSceneUI(); 35 | DontDestroyOnLoad(Managers.Game._playScene.gameObject); 36 | } 37 | else 38 | Managers.Game._playScene.IsMiniMap(true); 39 | 40 | // 플레이어 세이브 위치 이동 41 | if (Managers.Game.CurrentPos != Vector3.zero) 42 | Managers.Game.GetPlayer().transform.position = Managers.Game.CurrentPos; 43 | 44 | // 클릭 Effect 생성 45 | if (Managers.Game.GetPlayer().IsFakeNull() == false) 46 | { 47 | GameObject clickMoveEffect = Managers.Resource.Instantiate("Effect/ClickMoveEffect"); 48 | clickMoveEffect.SetActive(false); 49 | 50 | Managers.Game.GetPlayer().GetComponent().clickMoveEffect = clickMoveEffect; 51 | } 52 | 53 | // 카메라 조정 54 | Camera.main.gameObject.GetOrAddComponent().SetPlayer(Managers.Game.GetPlayer()); 55 | } 56 | 57 | public override void Clear() 58 | { 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Scripts/Scenes/TitleScene.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : TitleScene.cs 7 | * Desc : TitleScene이 Load되면 호출된다. 8 | */ 9 | 10 | public class TitleScene : BaseScene 11 | { 12 | protected override void Init() 13 | { 14 | base.Init(); 15 | 16 | SceneType = Define.Scene.Title; 17 | 18 | Managers.UI.ShowSceneUI(); 19 | } 20 | 21 | public override void Clear() {} 22 | } 23 | -------------------------------------------------------------------------------- /Scripts/UI/Popup/UI_ConfirmPopup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using TMPro; 5 | using UnityEngine; 6 | using UnityEngine.EventSystems; 7 | 8 | /* 9 | * File : UI_ConfirmPopup.cs 10 | * Desc : 확인/취소 Popup UI 11 | * 12 | & Functions 13 | & [Public] 14 | & : Init() - 초기 설정 15 | & : SetInfo() - 새 정보 설정 ( 확인 클릭 시 Invoke 호출할 Action 받기 ) 16 | & 17 | & [Private] 18 | & : OnClickYesButton() - 확인 클릭 시 호출 19 | & : OnClickNoButton() - 취소 클릭 시 호출 20 | * 21 | */ 22 | 23 | public class UI_ConfirmPopup : UI_Popup 24 | { 25 | enum Gameobjects 26 | { 27 | Background, 28 | } 29 | 30 | enum Buttons 31 | { 32 | YesButton, 33 | NoButton, 34 | } 35 | 36 | [SerializeField] 37 | TextMeshProUGUI _Messagetext; 38 | 39 | public override bool Init() 40 | { 41 | if (base.Init() == false) 42 | return false; 43 | 44 | // 자식 객체 불러오기 45 | BindObject(typeof(Gameobjects)); 46 | BindButton(typeof(Buttons)); 47 | 48 | // Order 설정 49 | GetObject((int)Gameobjects.Background).BindEvent((PointerEventData eventData)=> 50 | { 51 | Managers.UI.SetOrder(GetComponent()); 52 | }, Define.UIEvent.Click); 53 | 54 | // 버튼 Event 설정 55 | GetButton((int)Buttons.YesButton).onClick.AddListener(OnClickYesButton); 56 | GetButton((int)Buttons.NoButton).onClick.AddListener(OnClickNoButton); 57 | 58 | return true; 59 | } 60 | 61 | // 새 정보 설정 ( Action 받기 ) 62 | Action _onClickYesButton; 63 | public void SetInfo(Action onClickYesButton, string text) 64 | { 65 | // Order + 1 66 | Managers.UI.SetOrder(GetComponent()); 67 | 68 | _onClickYesButton = onClickYesButton; 69 | _Messagetext.text = text; 70 | } 71 | 72 | // 확인 버튼 73 | private void OnClickYesButton() 74 | { 75 | // Action Invoke 실행 76 | Managers.UI.ClosePopupUI(this); 77 | if (_onClickYesButton.IsNull() == false) 78 | _onClickYesButton.Invoke(); 79 | } 80 | 81 | // 취소 버튼 82 | private void OnClickNoButton() 83 | { 84 | // Popup 비활성화 85 | Managers.UI.ClosePopupUI(this); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Scripts/UI/Popup/UI_DiePopup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : UI_DiePopup.cs 7 | * Desc : 플레이어 사망 시 부활 Popup UI 8 | * 9 | & Functions 10 | & [Public] 11 | & : Init() - 초기 설정 ( 버튼 Event 설정 ) 12 | * 13 | */ 14 | 15 | public class UI_DiePopup : UI_Popup 16 | { 17 | enum Buttons 18 | { 19 | ResurrectionButton, 20 | ReSpawnButton, 21 | } 22 | 23 | enum Images { Background } 24 | 25 | public override bool Init() 26 | { 27 | if (base.Init() == false) 28 | return false; 29 | 30 | // 자식 객체 불러오기 31 | BindButton(typeof(Buttons)); 32 | BindImage(typeof(Images)); 33 | 34 | // 즉시 부활 버튼 35 | GetButton((int)Buttons.ResurrectionButton).onClick.AddListener(()=> 36 | { 37 | // 제자리 부활 + 체력/마나 50% 회복 + 100골드 차감 38 | if (Managers.Game.Gold < 100) 39 | { 40 | Managers.UI.MakeSubItem().SetInfo("골드가 부족합니다!", Color.yellow); 41 | return; 42 | } 43 | 44 | Managers.Game.Gold -= 100; 45 | 46 | Managers.Game.OnResurrection(0.5f); 47 | 48 | Managers.UI.ClosePopupUI(this); 49 | }); 50 | 51 | // 마을 부활 버튼 52 | GetButton((int)Buttons.ReSpawnButton).onClick.AddListener(()=> 53 | { 54 | // 마을 부활 + 체력/마나 20% 회복 55 | Managers.Game.OnResurrection(0.2f); 56 | 57 | if (Managers.Scene.CurrentScene.SceneType != Define.Scene.Game) 58 | Managers.Scene.LoadScene(Define.Scene.Game); 59 | 60 | Managers.Game.GetPlayer().transform.position = Managers.Game.defualtSpawn; 61 | 62 | Managers.UI.ClosePopupUI(this); 63 | }); 64 | 65 | return true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Scripts/UI/Popup/UI_InputPopup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using TMPro; 6 | using UnityEngine.UI; 7 | using System.Text.RegularExpressions; 8 | 9 | /* 10 | * File : UI_InputPopup.cs 11 | * Desc : 입력이 필요할 때 Popup UI (닉네임 입력 등..) 12 | * 13 | & Functions 14 | & [Public] 15 | & : Init() - 초기 설정 16 | & : SetInfo() - 새 정보 설정 ( 확인 클릭 시 Invoke 호출할 Action 받기 ) 17 | & 18 | & [Private] 19 | & : OnClickYesButton() - 확인 버튼 클릭 시 호출 20 | & : OnClickNoButton() - 취소 버튼 클릭 시 호출 21 | * 22 | */ 23 | 24 | public class UI_InputPopup : UI_Popup 25 | { 26 | enum Buttons 27 | { 28 | NoButton, 29 | YesButton, 30 | } 31 | 32 | [SerializeField] 33 | private TMP_InputField _inputField; 34 | 35 | [SerializeField] 36 | private TextMeshProUGUI _messageText; 37 | 38 | private string _regex; // 정규식 39 | 40 | public override bool Init() 41 | { 42 | if (base.Init() == false) 43 | return false; 44 | 45 | BindButton(typeof(Buttons)); 46 | 47 | // 버튼 기능 등록 48 | GetButton((int)Buttons.YesButton).onClick.AddListener(OnClickYesButton); 49 | GetButton((int)Buttons.NoButton).onClick.AddListener(OnClickNoButton); 50 | 51 | return true; 52 | } 53 | 54 | // 기능 설정 55 | Action _onClickYesButton; 56 | Action _onClickNoButton; 57 | public void SetInfo(Action onClickYesButton, string messageText, string placeholderText, string regex, Action onClickNoButton=null) 58 | { 59 | _onClickYesButton = onClickYesButton; 60 | _onClickNoButton = onClickNoButton; 61 | _messageText.text = messageText; 62 | _regex = regex; 63 | 64 | _inputField.placeholder.GetComponent().text = placeholderText; 65 | _inputField.Select(); 66 | } 67 | 68 | private void OnClickYesButton() 69 | { 70 | Regex regex = new Regex(_regex); 71 | if (regex.IsMatch(_inputField.text)) 72 | { 73 | Managers.UI.ClosePopupUI(this); 74 | 75 | // 확인 기능 실행 76 | if (_onClickYesButton.IsNull() == false) 77 | _onClickYesButton.Invoke(_inputField.text); 78 | } 79 | else 80 | { 81 | // 경고문 생성 82 | Managers.UI.MakeSubItem().SetInfo("한글|영어|숫자 2글자 이상 8글자 이하", Color.red); 83 | } 84 | } 85 | 86 | private void OnClickNoButton() 87 | { 88 | Managers.UI.ClosePopupUI(this); 89 | 90 | // 취소 기능 실행 91 | if (_onClickNoButton.IsNull() == false) 92 | _onClickNoButton.Invoke(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Scripts/UI/Popup/UI_InvenPopup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.EventSystems; 5 | using UnityEngine.UI; 6 | 7 | /* 8 | * File : UI_InvenPopup.cs 9 | * Desc : 인벤토리 관리 Popup UI 10 | * 11 | & Functions 12 | & [Public] 13 | & : Init() - 초기 설정 14 | & : IsInvenMaxSize() - 인벤토리 여유 공간 확인 15 | & : AcquireItem() - 인벤토리 슬롯에 아이템 저장 16 | & : ResetPos() - Popup 위치 초기화 17 | & 18 | & [Private] 19 | & : OnInventoryUI() - 인벤토리 활성화or비활성화 20 | & : SetInfo() - 기능 설정 21 | & : ResetSlot() - 슬롯 초기화 22 | & : SetEventHandler() - EventHandler 설정 23 | & : RefreshUI() - 새로고침 UI 24 | & : Exit() - 나가기 25 | * 26 | */ 27 | 28 | public class UI_InvenPopup : UI_Popup 29 | { 30 | enum Gameobjects 31 | { 32 | Background, 33 | Content, 34 | Title, 35 | ExitButton, 36 | } 37 | 38 | enum Texts 39 | { 40 | GoldText, 41 | } 42 | 43 | private List invenSlots; // 슬롯 List 44 | 45 | [SerializeField] 46 | private int invenCount = 42; // 인벤 슬롯 개수 47 | 48 | public override bool Init() 49 | { 50 | if (base.Init() == false) 51 | return false; 52 | 53 | invenSlots = new List(); 54 | popupType = Define.Popup.Inventory; 55 | 56 | // 자식 객체 불러오기 57 | BindObject(typeof(Gameobjects)); 58 | BindText(typeof(Texts)); 59 | 60 | // InputManager에 입력 등록 61 | Managers.Input.KeyAction -= OnInventoryUI; 62 | Managers.Input.KeyAction += OnInventoryUI; 63 | 64 | SetInfo(); 65 | 66 | Invoke("DelayInit", 0.0001f); 67 | 68 | return true; 69 | } 70 | void DelayInit() { RefreshUI(); Managers.UI.ClosePopupUI(this); } 71 | 72 | void Update() 73 | { 74 | // 인벤토리 활성화되면 실시간 새로고침 75 | if (Managers.Game.isPopups[Define.Popup.Inventory] == true) 76 | RefreshUI(); 77 | } 78 | 79 | // 인벤토리 자리 확인 80 | public bool IsInvenMaxSize() 81 | { 82 | foreach(UI_InvenSlot slot in invenSlots) 83 | { 84 | if (slot.item.IsNull() == true) 85 | return false; 86 | } 87 | 88 | return true; 89 | } 90 | 91 | // 인벤토리 슬롯 아이템 저장 92 | public bool AcquireItem(ItemData item, int count = 1) 93 | { 94 | // 모든 슬롯 확인 95 | foreach(UI_InvenSlot slot in invenSlots) 96 | { 97 | // 슬롯에 아이템이 없으면 98 | if (slot.item.IsNull() == true) 99 | { 100 | // 아이템 저장 101 | slot.AddItem(item, count); 102 | return true; 103 | } 104 | 105 | // 소비 아이템이라면 106 | if (item is UseItemData) 107 | { 108 | // 아이템의 id가 같다면 똑같은 아이템이므로 109 | if (item.id == slot.item.id) 110 | { 111 | // 개수 추가 112 | slot.SetCount(count); 113 | return true; 114 | } 115 | } 116 | } 117 | 118 | // 경고문 생성 119 | Managers.UI.MakeSubItem().SetInfo("인벤토리가 가득 찼습니다.", Color.red); 120 | 121 | return false; 122 | } 123 | 124 | // 인벤토리 Popup 초기화 125 | public void ResetPos() 126 | { 127 | RectTransform invenPos = GetObject((int)Gameobjects.Background).GetComponent(); 128 | invenPos.anchoredPosition = new Vector2(330, 0); 129 | } 130 | 131 | // 인벤토리 활성화 132 | private void OnInventoryUI() 133 | { 134 | if (Input.GetKeyDown(KeyCode.I)) 135 | { 136 | Managers.Game.isPopups[Define.Popup.Inventory] = !Managers.Game.isPopups[Define.Popup.Inventory]; 137 | 138 | // 인벤토리 Popup On/Off 139 | if (Managers.Game.isPopups[Define.Popup.Inventory]) 140 | Managers.UI.OnPopupUI(this); 141 | else 142 | Exit(); 143 | } 144 | } 145 | 146 | // 기능 설정 147 | private void SetInfo() 148 | { 149 | ResetSlot(); // 슬롯 초기화 150 | SetEventHandler(); // EventHandler 설정 151 | } 152 | 153 | private void ResetSlot() 154 | { 155 | // 슬롯들을 담고 있는 부모 가져오기 156 | GameObject grid = GetObject((int)Gameobjects.Content); 157 | 158 | // 슬롯 모두 삭제 159 | foreach(Transform child in grid.transform) 160 | Managers.Resource.Destroy(child.gameObject); 161 | 162 | // invenCount만큼 슬롯 생성 163 | for(int i=0; i(parent: grid.transform); 167 | 168 | // 슬롯 위치 번호 169 | invenItem.invenNumber = i; 170 | 171 | // 위치 번호가 세이브에 있다면 item 가져오기 172 | if (Managers.Game.InvenItem.TryGetValue(i, out ItemData item) == true) 173 | { 174 | // 기능 설정 175 | invenItem.SetInfo(); 176 | 177 | // 소비 아이템이라면 178 | if (item is UseItemData) 179 | { 180 | // 아이템 개수와 함께 저장 181 | UseItemData useItem = item as UseItemData; 182 | invenItem.AddItem(useItem, useItem.itemCount); 183 | } 184 | else 185 | invenItem.AddItem(item); // 그냥 item 저장 186 | } 187 | 188 | // 생성된 슬롯 List에 저장 189 | invenSlots.Add(invenItem); 190 | } 191 | } 192 | 193 | private void SetEventHandler() 194 | { 195 | // Title 잡고 인벤토리 이동 196 | RectTransform invenPos = GetObject((int)Gameobjects.Background).GetComponent(); 197 | GetObject((int)Gameobjects.Title).BindEvent((PointerEventData eventData)=> 198 | { 199 | // 상호작용 중이면 이동X 200 | if (Managers.Game.IsInteract == true) 201 | return; 202 | 203 | invenPos.anchoredPosition = new Vector2 204 | ( 205 | Mathf.Clamp(invenPos.anchoredPosition.x + eventData.delta.x, -655, 655), 206 | Mathf.Clamp(invenPos.anchoredPosition.y + eventData.delta.y, -253, 217) 207 | ); 208 | }, Define.UIEvent.Drag); 209 | 210 | // Order 설정 211 | GetObject((int)Gameobjects.Background).BindEvent((PointerEventData eventData)=> 212 | { 213 | Managers.UI.SetOrder(GetComponent()); 214 | }, Define.UIEvent.Click); 215 | 216 | // Exit 버튼 217 | GetObject((int)Gameobjects.ExitButton).BindEvent((PointerEventData eventData)=> 218 | { 219 | if (Managers.Game.IsInteract == true) 220 | return; 221 | 222 | Exit(); 223 | }, Define.UIEvent.Click); 224 | } 225 | 226 | private void RefreshUI() 227 | { 228 | // 골드 개수 불러오기 229 | GetText((int)Texts.GoldText).text = Managers.Game.Gold.ToString(); 230 | } 231 | 232 | private void Exit() 233 | { 234 | Managers.Game._playScene._slotTip.OnSlotTip(false); 235 | Managers.UI.ClosePopupUI(this); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /Scripts/UI/Popup/UI_LoadPopup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | using TMPro; 6 | 7 | /* 8 | * File : UI_LoadPopup.cs 9 | * Desc : Scene을 Load할 때 호출되는 Popup UI 10 | * 11 | & Functions 12 | & [Public] 13 | & : SetInfo() - 기본 설정 14 | & 15 | & [Private] 16 | & : LoadAsynSceneCoroutine() - 비동기식 로드 17 | & : OnDataRequest() - 구글스프레드시트 데이터 불러오기 18 | * 19 | */ 20 | 21 | public class UI_LoadPopup : UI_Popup 22 | { 23 | // 메시지 string Array 24 | private string[] loadMessges = new string[]{Define.LoadMessage1, Define.LoadMessage2, Define.LoadMessage3}; 25 | 26 | // 현재 메시지 Index 27 | private int currentMessageIndex = 0; 28 | 29 | [SerializeField] 30 | private Slider loadSlider; 31 | 32 | [SerializeField] 33 | private TextMeshProUGUI tipText; 34 | 35 | // 기본 설정 36 | public void SetInfo(Define.Scene type, int plusTime = 0) 37 | { 38 | // 구글 시트 데이터 가져오기 39 | OnDataRequest(); 40 | 41 | // slider 초기화 42 | loadSlider.value = 0; 43 | loadSlider.minValue = 0; 44 | loadSlider.maxValue = plusTime; 45 | 46 | // 출력할 메시지 선정 47 | currentMessageIndex = Random.Range(0,3); 48 | tipText.text = $"Tip : {loadMessges[currentMessageIndex]}"; 49 | 50 | // 플레이어 정지 51 | Managers.Game.StopPlayer(); 52 | 53 | // 비동기 로드 시작 54 | StartCoroutine(LoadAsynSceneCoroutine(type, plusTime)); 55 | } 56 | 57 | void Update() 58 | { 59 | if (Input.GetMouseButtonDown(0)) 60 | { 61 | currentMessageIndex++; 62 | if (currentMessageIndex >= loadMessges.Length) 63 | currentMessageIndex = 0; 64 | 65 | tipText.text = $"Tip : {loadMessges[currentMessageIndex]}"; 66 | } 67 | } 68 | 69 | // 비동기 로드 70 | private float loadTime = 0; 71 | private IEnumerator LoadAsynSceneCoroutine(Define.Scene type, int plusTime = 0) 72 | { 73 | yield return null; 74 | 75 | // Scene Load 76 | AsyncOperation operation = Managers.Scene.LoadAsynScene(type); 77 | 78 | // Load 시간 확인 79 | while (operation.isDone == false) 80 | { 81 | loadTime += Time.deltaTime; 82 | 83 | loadSlider.value = loadTime; 84 | 85 | // 시간이 다 되면 탈출 86 | if (loadTime > plusTime) 87 | { 88 | operation.allowSceneActivation = true; 89 | } 90 | 91 | yield return null; 92 | } 93 | } 94 | 95 | // 구글 스프레드시트 데이터 가져오기 96 | private void OnDataRequest() 97 | { 98 | // 이미 데이터를 받았다면 종료 99 | if (Managers.Data.isData == true) 100 | return; 101 | 102 | StartCoroutine(Managers.Data.DataRequest(Define.StartNumber)); 103 | StartCoroutine(Managers.Data.DataRequest(Define.LevelNumber)); 104 | StartCoroutine(Managers.Data.DataRequest(Define.SkillNumber)); 105 | StartCoroutine(Managers.Data.DataRequest(Define.UseItemNumber)); 106 | StartCoroutine(Managers.Data.DataRequest(Define.WeaponItemNumber)); 107 | StartCoroutine(Managers.Data.DataRequest(Define.ArmorItemNumber)); 108 | StartCoroutine(Managers.Data.DataRequest(Define.DropItemNumber)); 109 | StartCoroutine(Managers.Data.DataRequest(Define.MonsterNumber)); 110 | StartCoroutine(Managers.Data.DataRequest(Define.ShopNumber)); 111 | StartCoroutine(Managers.Data.DataRequest(Define.TalkNumber)); 112 | StartCoroutine(Managers.Data.DataRequest(Define.QuestNumber)); 113 | 114 | Managers.Data.isData = true; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Scripts/UI/Popup/UI_MenuPopup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : UI_MenuPopup.cs 7 | * Desc : Esc Menu Popup UI 8 | * 현재 띄어진 Popup이 없다면 Menu Popup을 활성화한다. 9 | * 10 | & Functions 11 | & [Public] 12 | & : Init() - 초기 설정 13 | & 14 | & [Private] 15 | & : OnMenuPopup() - 메뉴 활성화or비활성화 16 | & : OnMenu() - 메뉴 활성화 진행 17 | & : OnClickContinueButton() - 진행 버튼 18 | & : OnClickSaveButton() - 세이브 버튼 19 | & : OnClickAppExitButton() - 나가기 버튼 20 | & : Exit() - 초기화 21 | * 22 | */ 23 | 24 | public class UI_MenuPopup : UI_Popup 25 | { 26 | enum Buttons 27 | { 28 | ContinueButton, 29 | SaveButton, 30 | AppExitButton, 31 | } 32 | 33 | public override bool Init() 34 | { 35 | if (base.Init() == false) 36 | return false; 37 | 38 | popupType = Define.Popup.Menu; 39 | 40 | // 자식 객체 불러오기 41 | BindButton(typeof(Buttons)); 42 | 43 | // 버튼 기능 등록 44 | GetButton((int)Buttons.ContinueButton).onClick.AddListener(OnClickContinueButton); 45 | GetButton((int)Buttons.SaveButton).onClick.AddListener(OnClickSaveButton); 46 | GetButton((int)Buttons.AppExitButton).onClick.AddListener(OnClickAppExitButton); 47 | 48 | // InputManager에 입력 등록 49 | Managers.Input.KeyAction -= OnMenuPopup; 50 | Managers.Input.KeyAction += OnMenuPopup; 51 | 52 | Managers.UI.ClosePopupUI(this); 53 | 54 | return true; 55 | } 56 | 57 | // 메뉴 활성화 58 | private void OnMenuPopup() 59 | { 60 | if (Input.GetKeyDown(KeyCode.Escape)) 61 | { 62 | Managers.Game.isPopups[Define.Popup.Menu] = !Managers.Game.isPopups[Define.Popup.Menu]; 63 | 64 | // 메뉴 Popup On/Off 65 | if (Managers.Game.isPopups[Define.Popup.Menu]) 66 | OnMenu(); 67 | else 68 | Exit(); 69 | } 70 | } 71 | 72 | private void OnMenu() 73 | { 74 | // 현재 활성화 중인 Popup이 없다면 75 | if (Managers.UI.ClosePopupUI() == false) 76 | { 77 | // 메뉴 활성화 78 | Managers.UI.OnPopupUI(this); 79 | Time.timeScale = 0; 80 | } 81 | else 82 | { 83 | // 메뉴 끄기 84 | Managers.Game.isPopups[Define.Popup.Menu] = false; 85 | Managers.Game._playScene._slotTip.OnSlotTip(false); 86 | } 87 | } 88 | 89 | // 게임 진행 버튼 90 | private void OnClickContinueButton() 91 | { 92 | Exit(); 93 | } 94 | 95 | // 게임 세이브 버튼 96 | private void OnClickSaveButton() 97 | { 98 | Managers.Game.SaveGame(); 99 | Exit(); 100 | } 101 | 102 | // 게임 나가기 버튼 103 | private void OnClickAppExitButton() 104 | { 105 | Exit(); 106 | Application.Quit(); 107 | } 108 | 109 | // 초기화 110 | private void Exit() 111 | { 112 | Time.timeScale = 1; 113 | Managers.UI.ClosePopupUI(this); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Scripts/UI/Popup/UI_NumberCheckPopup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.EventSystems; 6 | using TMPro; 7 | using UnityEngine.UI; 8 | 9 | /* 10 | * File : UI_NumberCheckPopup.cs 11 | * Desc : 개수 확인 Popup UI ( 현재 상점에서 사용 중 ) 12 | * 13 | & Functions 14 | & [Public] 15 | & : Init() - 초기 설정 16 | & : SetInfo() - 기능 설정 17 | & 18 | & [Private] 19 | & : OnClickMinusButton() - 마이너스 버튼 20 | & : OnClickPlusButton() - 플러스 버튼 21 | & : OnClickYesButton() - 확인 버튼 22 | & : OnClickNoButton() - 취소 버튼 23 | & : RefreshUI() - 새로고침 UI 24 | * 25 | */ 26 | 27 | public class UI_NumberCheckPopup : UI_Popup 28 | { 29 | enum Gameobjects 30 | { 31 | Background, 32 | } 33 | 34 | enum Buttons 35 | { 36 | MinusButton, 37 | PlusButton, 38 | NoButton, 39 | YesButton, 40 | } 41 | 42 | private int itemCount = 0; // 현재 개수 43 | private int itemMaxCount = 0; // 최대 개수 44 | 45 | private Action _onClickYesButton; // 확인 버튼 누를 시 호출 46 | private UI_InvenSlot _invenItem; // 인벤토리 슬롯 47 | 48 | [SerializeField] 49 | private Slider numberSlider; 50 | 51 | [SerializeField] 52 | private TextMeshProUGUI _itemCountText; 53 | 54 | public override bool Init() 55 | { 56 | if (base.Init() == false) 57 | return false; 58 | 59 | // 자식 객체 불러오기 60 | BindObject(typeof(Gameobjects)); 61 | BindButton(typeof(Buttons)); 62 | 63 | // 버튼 기능 등록 64 | GetButton((int)Buttons.MinusButton).onClick.AddListener(OnClickMinusButton); 65 | GetButton((int)Buttons.PlusButton).onClick.AddListener(OnClickPlusButton); 66 | GetButton((int)Buttons.NoButton).onClick.AddListener(OnClickNoButton); 67 | GetButton((int)Buttons.YesButton).onClick.AddListener(OnClickYesButton); 68 | 69 | // Order 설정 70 | GetObject((int)Gameobjects.Background).BindEvent((PointerEventData eventData)=> 71 | { 72 | Managers.UI.SetOrder(GetComponent()); 73 | }, Define.UIEvent.Click); 74 | 75 | // 슬라이더 사용 시 기능 등록 76 | numberSlider.onValueChanged.AddListener((float value)=> 77 | { 78 | itemCount = (int)value; 79 | _itemCountText.text = itemCount.ToString(); 80 | }); 81 | 82 | return true; 83 | } 84 | 85 | // 인벤토리 받으며 세팅 (판매할 때 사용 중) 86 | public void SetInfo(UI_InvenSlot invenItem, Action onClickYesButton) 87 | { 88 | _onClickYesButton = onClickYesButton; 89 | _invenItem = invenItem; 90 | 91 | itemMaxCount = invenItem.itemCount; 92 | 93 | RefreshUI(); 94 | } 95 | 96 | // 아이템 받으며 세팅 (구매할 때 사용 중) 97 | public void SetInfo(ItemData item, Action onClickYesButton) 98 | { 99 | _onClickYesButton = onClickYesButton; 100 | 101 | itemMaxCount = (int)(Managers.Game.Gold / item.itemPrice); 102 | 103 | RefreshUI(); 104 | } 105 | 106 | // 마이너스 버튼 107 | private void OnClickMinusButton() 108 | { 109 | itemCount = Mathf.Clamp(--itemCount, 1, itemMaxCount); 110 | numberSlider.value = itemCount; 111 | _itemCountText.text = itemCount.ToString(); 112 | } 113 | 114 | // 플러스 버튼 115 | private void OnClickPlusButton() 116 | { 117 | itemCount = Mathf.Clamp(++itemCount, 1, itemMaxCount); 118 | numberSlider.value = itemCount; 119 | _itemCountText.text = itemCount.ToString(); 120 | } 121 | 122 | // 확인 버튼 123 | private void OnClickYesButton() 124 | { 125 | Managers.UI.ClosePopupUI(this); 126 | 127 | if (_onClickYesButton.IsNull() == false) 128 | _onClickYesButton.Invoke(itemCount); 129 | } 130 | 131 | // 취소 버튼 132 | private void OnClickNoButton() 133 | { 134 | Managers.UI.ClosePopupUI(this); 135 | } 136 | 137 | private void RefreshUI() 138 | { 139 | Managers.UI.SetOrder(GetComponent()); 140 | 141 | itemCount = 1; 142 | 143 | numberSlider.minValue = itemCount; 144 | numberSlider.maxValue = itemMaxCount; 145 | numberSlider.value = itemCount; 146 | 147 | _itemCountText.text = itemCount.ToString(); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /Scripts/UI/Popup/UI_Popup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : UI_Popup.cs 7 | * Desc : 모든 Popup의 부모 8 | * 9 | & Functions 10 | & [Public] 11 | & : Init() - 초기 설정 12 | * 13 | */ 14 | 15 | public class UI_Popup : UI_Base 16 | { 17 | public Define.Popup popupType = Define.Popup.Unknown; 18 | 19 | public override bool Init() 20 | { 21 | if (base.Init() == false) 22 | return false; 23 | 24 | // Canvas 생성 25 | Managers.UI.SetCanvas(gameObject, true); 26 | return true; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Scripts/UI/Popup/UI_SkillPopup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.EventSystems; 5 | 6 | /* 7 | * File : UI_SkillPopup.cs 8 | * Desc : 스킬 슬롯을 관리하는 Popup UI 9 | * 10 | & Functions 11 | & [Public] 12 | & : Init() - 초기 설정 13 | & 14 | & [Private] 15 | & : OnSkillPopup() - 스킬창 활성화or비활성화 16 | & : SetInfo() - 기본 설정 17 | & : Exit() - 나가기 (초기화) 18 | * 19 | */ 20 | 21 | public class UI_SkillPopup : UI_Popup 22 | { 23 | enum Gameobjects 24 | { 25 | Title, 26 | Background, 27 | ExitButton, 28 | } 29 | 30 | public override bool Init() 31 | { 32 | if (base.Init() == false) 33 | return false; 34 | 35 | // 자식 객체 불러오기 36 | BindObject(typeof(Gameobjects)); 37 | 38 | popupType = Define.Popup.SkillUI; 39 | 40 | Managers.Input.KeyAction -= OnSkillPopup; 41 | Managers.Input.KeyAction += OnSkillPopup; 42 | 43 | SetInfo(); 44 | 45 | Managers.UI.ClosePopupUI(this); 46 | 47 | return true; 48 | } 49 | 50 | // 스킬창 활성화 51 | private void OnSkillPopup() 52 | { 53 | if (Input.GetKeyDown(KeyCode.K)) 54 | { 55 | Managers.Game.isPopups[Define.Popup.SkillUI] = !Managers.Game.isPopups[Define.Popup.SkillUI]; 56 | 57 | // 스킬창 Popup On/Off 58 | if (Managers.Game.isPopups[Define.Popup.SkillUI]) 59 | Managers.UI.OnPopupUI(this); 60 | else 61 | Exit(); 62 | } 63 | } 64 | 65 | private void SetInfo() 66 | { 67 | // Title 잡고 인벤토리 이동 68 | RectTransform skillPopupPos = GetObject((int)Gameobjects.Background).GetComponent(); 69 | GetObject((int)Gameobjects.Title).BindEvent((PointerEventData eventData) => 70 | { 71 | skillPopupPos.anchoredPosition = new Vector2 72 | ( 73 | Mathf.Clamp(skillPopupPos.anchoredPosition.x + eventData.delta.x, -655, 655), 74 | Mathf.Clamp(skillPopupPos.anchoredPosition.y + eventData.delta.y, -253, 217) 75 | ); 76 | }, Define.UIEvent.Drag); 77 | 78 | // Order 설정 79 | GetObject((int)Gameobjects.Background).BindEvent((PointerEventData eventData) => 80 | { 81 | Managers.UI.SetOrder(GetComponent()); 82 | }, Define.UIEvent.Click); 83 | 84 | // Exit 버튼 85 | GetObject((int)Gameobjects.ExitButton).BindEvent((PointerEventData eventData) => 86 | { 87 | Managers.UI.ClosePopupUI(this); 88 | }, Define.UIEvent.Click); 89 | } 90 | 91 | private void Exit() 92 | { 93 | Managers.Game._playScene._slotTip.OnSlotTip(false); 94 | Managers.UI.ClosePopupUI(this); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Scripts/UI/Popup/UI_SlotTipPopup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : UI_SlotTipPopup.cs 7 | * Desc : 슬롯의 아이템 정보를 확인하는 Popup UI 8 | * 9 | & Functions 10 | & [Public] 11 | & : Init() - 초기 설정 12 | & 13 | & [Private] 14 | & : OnSlotTip() - 슬롯 정보 활성화 15 | & : RefreshUI() - 새로고침 UI (슬롯 정보 새로고침) 16 | & : SetColor() - 색 설정 17 | * 18 | */ 19 | 20 | public class UI_SlotTipPopup : UI_Popup 21 | { 22 | enum Gameobjects 23 | { 24 | Background, 25 | } 26 | 27 | enum Images 28 | { 29 | ItemImage, 30 | } 31 | 32 | enum Texts 33 | { 34 | ItemNameText, 35 | ItemTypeText, 36 | ItemGradeText, 37 | ItemLevelText, 38 | ItemStatText, 39 | } 40 | 41 | public RectTransform background; 42 | 43 | public override bool Init() 44 | { 45 | if (base.Init() == false) 46 | return false; 47 | 48 | BindObject(typeof(Gameobjects)); 49 | BindImage(typeof(Images)); 50 | BindText(typeof(Texts)); 51 | 52 | background = GetObject((int)Gameobjects.Background).GetComponent(); 53 | 54 | Managers.UI.ClosePopupUI(this); 55 | 56 | return true; 57 | } 58 | 59 | // 슬롯 팁 활성화 60 | public void OnSlotTip(bool isActive) 61 | { 62 | if (isActive) 63 | Managers.UI.OnPopupUI(this); 64 | else 65 | { 66 | if (this.gameObject.activeSelf == true) 67 | Managers.UI.ClosePopupUI(this); 68 | } 69 | } 70 | 71 | // 아이템 정보 확인시 새로고침 72 | public void RefreshUI(ItemData item) 73 | { 74 | if (item.IsNull() == true) 75 | { 76 | Debug.Log("아이템 정보가 없습니다."); 77 | OnSlotTip(false); 78 | return; 79 | } 80 | 81 | // 위치 설정 82 | RectTransform tipRect = background; 83 | Vector3 slotTipPos = background.anchoredPosition; 84 | slotTipPos.x = slotTipPos.x + (tipRect.rect.width * 0.65f); 85 | slotTipPos.y = slotTipPos.y - (tipRect.rect.height * 0.65f); 86 | background.anchoredPosition = slotTipPos; 87 | 88 | GetImage((int)Images.ItemImage).sprite = item.itemIcon; 89 | 90 | GetText((int)Texts.ItemNameText).text = item.itemName; 91 | GetText((int)Texts.ItemTypeText).text = item.itemType.ToString(); 92 | GetText((int)Texts.ItemGradeText).text = item.itemGrade.ToString(); 93 | 94 | // 아이템 등급에 따른 색깔 95 | switch(item.itemGrade) 96 | { 97 | case Define.itemGrade.Common: 98 | SetColor(Color.white); 99 | break; 100 | case Define.itemGrade.Rare: 101 | SetColor(Color.green); 102 | break; 103 | case Define.itemGrade.Epic: 104 | SetColor(Color.blue); 105 | break; 106 | case Define.itemGrade.Legendary: 107 | SetColor(Color.yellow); 108 | break; 109 | } 110 | 111 | // 장비라면 112 | if (item is EquipmentData) 113 | { 114 | // 강화가 됐다면 115 | if ((item as EquipmentData).upgradeCount > 0) 116 | GetText((int)Texts.ItemNameText).text = item.itemName + $" [+{(item as EquipmentData).upgradeCount}]"; 117 | } 118 | 119 | // 아이템 종류 별로 세팅 120 | if (item.itemType == Define.ItemType.Use) 121 | { 122 | GetText((int)Texts.ItemLevelText).text = ""; 123 | GetText((int)Texts.ItemStatText).text = item.itemDesc; 124 | } 125 | else if (item.itemType == Define.ItemType.Armor) 126 | { 127 | ArmorItemData armor = item as ArmorItemData; 128 | GetText((int)Texts.ItemLevelText).text = "최소레벨 " + armor.minLevel; 129 | 130 | string statStr = ""; 131 | // 강화 확인 132 | if (armor.upgradeCount > 0) 133 | { 134 | statStr += armor.defnece > 0 ? $"방어력 {armor.defnece} (+{armor.addDefnece})\n" : ""; 135 | statStr += armor.hp > 0 ? $"체력 {armor.hp} (+{armor.addHp})\n" : ""; 136 | statStr += armor.mp > 0 ? $"마나 {armor.mp} (+{armor.addMp})\n" : ""; 137 | } 138 | else 139 | { 140 | statStr += armor.defnece > 0 ? $"방어력 {armor.defnece}\n" : ""; 141 | statStr += armor.hp > 0 ? $"체력 {armor.hp}\n" : ""; 142 | statStr += armor.mp > 0 ? $"마나 {armor.mp}\n" : ""; 143 | } 144 | 145 | statStr += armor.moveSpeed > 0 ? $"이동속도 {armor.moveSpeed}\n" : ""; 146 | 147 | GetText((int)Texts.ItemStatText).text = statStr; 148 | } 149 | else if (item.itemType == Define.ItemType.Weapon) 150 | { 151 | WeaponItemData weapon = item as WeaponItemData; 152 | GetText((int)Texts.ItemLevelText).text = "최소레벨 " + weapon.minLevel; 153 | 154 | // 강화 확인 155 | if (weapon.upgradeCount > 0) 156 | GetText((int)Texts.ItemStatText).text = $"공격력 {weapon.attack} (+{weapon.addAttack})"; 157 | else 158 | GetText((int)Texts.ItemStatText).text = $"공격력 {weapon.attack}"; 159 | } 160 | } 161 | 162 | private void SetColor(Color color) 163 | { 164 | GetText((int)Texts.ItemNameText).color = color; 165 | GetText((int)Texts.ItemGradeText).color = color; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Scripts/UI/Popup/UI_UpgradePopup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.EventSystems; 5 | 6 | /* 7 | * File : UI_UpgradePopup.cs 8 | * Desc : 장비를 강화할 수 있는 Popup UI 9 | * 10 | & Functions 11 | & [Public] 12 | & : Init() - 초기 설정 13 | & : RefreshUI() - 장비 강화수치 새로고침 14 | & : ExitUpgrade() - 강화창 나가기 15 | & : Clear() - 초기화 16 | & 17 | & [Private] 18 | & : OnClickUpgradeButton() - 강화 진행 버튼 19 | & : EquipmentUpgradeGold() - 장비 강화 골드 지불 20 | & : EquipmentUpgrade() - 장비 강화 적용 21 | & : SetInfo() - 기능 설정 22 | * 23 | */ 24 | 25 | public class UI_UpgradePopup : UI_Popup 26 | { 27 | enum Gameobjects 28 | { 29 | ItemSlot, 30 | } 31 | 32 | enum Buttons 33 | { 34 | UpgradeButton, 35 | ExitButton, 36 | } 37 | 38 | enum Texts 39 | { 40 | ItemNameText, 41 | UpgradeResultText, 42 | UpgradeGoldText, 43 | } 44 | 45 | public EquipmentData _equipment; 46 | 47 | private int maxUpgradeCount = 10; // 최대 강화 수치 48 | 49 | public override bool Init() 50 | { 51 | if (base.Init() == false) 52 | return false; 53 | 54 | // 자식 객체 불러오기 55 | BindObject(typeof(Gameobjects)); 56 | BindButton(typeof(Buttons)); 57 | BindText(typeof(Texts)); 58 | 59 | SetInfo(); 60 | 61 | Managers.UI.ClosePopupUI(this); 62 | 63 | return true; 64 | } 65 | 66 | public void RefreshUI(EquipmentData equipment) 67 | { 68 | _equipment = equipment; 69 | 70 | // 풀강 확인 71 | if (equipment.upgradeCount >= maxUpgradeCount) 72 | { 73 | GetText((int)Texts.ItemNameText).text = _equipment.itemName; 74 | GetText((int)Texts.UpgradeResultText).text = $"Max"; 75 | GetText((int)Texts.UpgradeGoldText).text = ""; 76 | } 77 | else 78 | { 79 | GetText((int)Texts.ItemNameText).text = _equipment.itemName; 80 | GetText((int)Texts.UpgradeResultText).text = $"{_equipment.upgradeCount} → {_equipment.upgradeCount+1}"; 81 | GetText((int)Texts.UpgradeGoldText).text = EquipmentUpgradeGold(_equipment).ToString(); 82 | } 83 | } 84 | 85 | // 강화 진행 버튼 86 | private void OnClickUpgradeButton() 87 | { 88 | if (_equipment.IsNull() == true) 89 | return; 90 | 91 | // 강화 수치 Max 확인 92 | if (_equipment.upgradeCount >= maxUpgradeCount) 93 | return; 94 | 95 | // 금액 확인 96 | int upgradeGold = EquipmentUpgradeGold(_equipment); 97 | if (Managers.Game.Gold < upgradeGold) 98 | { 99 | GetText((int)Texts.ItemNameText).text = "금액이 부족합니다!"; 100 | return; 101 | } 102 | 103 | Managers.Game.Gold -= upgradeGold; 104 | 105 | // 강화 적용 106 | EquipmentUpgrade(_equipment); 107 | RefreshUI(_equipment); 108 | } 109 | 110 | // 강화 비용 계산 111 | private int EquipmentUpgradeGold(EquipmentData equipment) 112 | { 113 | // 강화 금액 : 아이템 판매 가격 + ((판매 가격 / 2) * 강화 횟수) 114 | int gold = equipment.itemPrice + (int)((equipment.itemPrice / 4) * (equipment.upgradeCount)); 115 | return gold; 116 | } 117 | 118 | // 강화 적용 119 | private void EquipmentUpgrade(EquipmentData equipment) 120 | { 121 | equipment.upgradeCount += 1; 122 | 123 | // 장비 타입 확인 후 강화 적용 124 | if (equipment is WeaponItemData) 125 | { 126 | WeaponItemData weapon = equipment as WeaponItemData; 127 | 128 | weapon.addAttack = weapon.upgradeValue * weapon.upgradeCount; 129 | } 130 | else if (equipment is ArmorItemData) 131 | { 132 | ArmorItemData armor = equipment as ArmorItemData; 133 | 134 | armor.addDefnece = armor.upgradeValue * armor.upgradeCount; 135 | armor.addHp = (armor.upgradeValue * 5) * armor.upgradeCount; 136 | armor.addMp = (armor.upgradeValue * 5) * armor.upgradeCount; 137 | } 138 | } 139 | 140 | public void ExitUpgrade() 141 | { 142 | if (_equipment.IsNull() == false) 143 | Managers.Game._playScene._inventory.AcquireItem(_equipment); 144 | 145 | Clear(); 146 | 147 | // 강화 슬롯 초기화 148 | GetObject((int)Gameobjects.ItemSlot).GetComponent().ClearSlot(); 149 | 150 | Managers.Game.IsInteract = false; 151 | 152 | Managers.UI.CloseAllPopupUI(); 153 | } 154 | 155 | public void Clear() 156 | { 157 | _equipment = null; 158 | 159 | Managers.Game._playScene._slotTip.OnSlotTip(false); 160 | 161 | GetText((int)Texts.ItemNameText).text = "강화할 장비를 선택하세요"; 162 | GetText((int)Texts.UpgradeResultText).text = ""; 163 | GetText((int)Texts.UpgradeGoldText).text = "0"; 164 | } 165 | 166 | private void SetInfo() 167 | { 168 | GetText((int)Texts.ItemNameText).text = "강화할 장비를 선택하세요"; 169 | GetText((int)Texts.UpgradeResultText).text = ""; 170 | GetText((int)Texts.UpgradeGoldText).text = "0"; 171 | 172 | GetButton((int)Buttons.UpgradeButton).onClick.AddListener(OnClickUpgradeButton); 173 | GetButton((int)Buttons.ExitButton).onClick.AddListener(ExitUpgrade); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /Scripts/UI/Scene/UI_CustomScene.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : UI_CustomScene.cs 8 | * Desc : 캐릭터 커스텀 Scene UI 9 | * 10 | & Functions 11 | & [Public] 12 | & : Init() - 초기 설정 13 | & 14 | & [Private] 15 | & : OnClickCheckButton() - 커스텀이 끝날 때 확인 버튼 16 | & : LoadPopup() - Scene을 로드할 Popup 생성 17 | & : OnClickExitButton() - 커스텀 나가기 버튼 18 | * 19 | */ 20 | 21 | public class UI_CustomScene : UI_Scene 22 | { 23 | enum GameObjects 24 | { 25 | Grid, 26 | } 27 | 28 | enum Buttons 29 | { 30 | CheckButton, 31 | ExitButton, 32 | } 33 | 34 | public CharacterCustom custom; 35 | 36 | public override bool Init() 37 | { 38 | if (base.Init() == false) 39 | return false; 40 | 41 | // 자식 객체 불러오기 42 | BindObject(typeof(GameObjects)); 43 | BindButton(typeof(Buttons)); 44 | 45 | // 커스텀할 캐릭터 찾기 46 | if (custom.IsNull() == true) 47 | custom = GameObject.FindObjectOfType(); 48 | 49 | // 커스텀 버튼에 캐릭터 객체 보내주기 50 | foreach(Transform child in GetObject((int)GameObjects.Grid).transform) 51 | child.GetComponent().SetInfo(custom); 52 | 53 | // 버튼 기능 등록 54 | GetButton((int)Buttons.CheckButton).onClick.AddListener(OnClickCheckButton); 55 | GetButton((int)Buttons.ExitButton).onClick.AddListener(OnClickExitButton); 56 | 57 | return true; 58 | } 59 | 60 | // 커스텀이 끝날 때 확인 버튼 61 | private void OnClickCheckButton() 62 | { 63 | // 캐릭터 회전 중지 64 | custom.stopRotation = true; 65 | custom.SaveCustom(); 66 | 67 | // 입력 Popup 생성 후 이름 받기 68 | Managers.UI.ShowPopupUI().SetInfo((string inputText)=> 69 | { 70 | Managers.Game.Name = inputText; 71 | LoadPopup(); 72 | } 73 | , "이름을 입력해 주세요", "이름 입력란", Define.NameRegex 74 | , ()=>{ 75 | custom.stopRotation = false; 76 | }); 77 | } 78 | 79 | // Scene을 로드할 Popup 생성 80 | private void LoadPopup() 81 | { 82 | if(Application.internetReachability == NetworkReachability.NotReachable) 83 | { 84 | // 인터넷 연결이 안되었을 때 행동 85 | Managers.UI.MakeSubItem().SetInfo("네트워크 연결이 필요합니다.", Color.red); 86 | } 87 | else if(Application.internetReachability == NetworkReachability.ReachableViaCarrierDataNetwork) 88 | { 89 | // 데이터로 연결이 되었을 때 행동 90 | Managers.UI.ShowPopupUI().SetInfo(Define.Scene.Game, 6); 91 | } 92 | else 93 | { 94 | // 와이파이로 연결이 되었을 때 행동 95 | Managers.UI.ShowPopupUI().SetInfo(Define.Scene.Game, 7); 96 | } 97 | } 98 | 99 | // 커스텀 나가기 버튼 100 | private void OnClickExitButton() 101 | { 102 | Managers.Scene.LoadScene(Define.Scene.Title); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Scripts/UI/Scene/UI_Scene.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | /* 6 | * File : UI_Scene.cs 7 | * Desc : 모든 Scene의 부모 8 | * 9 | & Functions 10 | & [Public] 11 | & : Init() - 초기 설정 12 | * 13 | */ 14 | 15 | public class UI_Scene : UI_Base 16 | { 17 | public override bool Init() 18 | { 19 | if (base.Init() == false) 20 | return false; 21 | 22 | Managers.UI.SetCanvas(gameObject, false); 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Scripts/UI/Scene/UI_TitleScene.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | /* 7 | * File : UI_TitleScene.cs 8 | * Desc : 게임 플레이 Scene UI 9 | * 10 | & Functions 11 | & [Public] 12 | & : Init() - 초기 설정 13 | & 14 | & [Private] 15 | & : OnClickStartButton() - 게임 시작 버튼 16 | & : OnClickLoadButton() - 세이브 로드 버튼 17 | & : OnClickExitButton() - 게임 나가기 버튼 18 | * 19 | */ 20 | 21 | public class UI_TitleScene : UI_Scene 22 | { 23 | enum Buttons 24 | { 25 | StartButton, 26 | LoadButton, 27 | ExitButton, 28 | } 29 | 30 | enum Texts 31 | { 32 | LoadButtonText, 33 | } 34 | 35 | public override bool Init() 36 | { 37 | if (base.Init() == false) 38 | return false; 39 | 40 | // 자식 객체 불러오기 41 | BindButton(typeof(Buttons)); 42 | BindText(typeof(Texts)); 43 | 44 | // 버튼 기능 등록 45 | GetButton((int)Buttons.StartButton).onClick.AddListener(OnClickStartButton); 46 | GetButton((int)Buttons.LoadButton).onClick.AddListener(OnClickLoadButton); 47 | GetButton((int)Buttons.ExitButton).onClick.AddListener(OnClickExitButton); 48 | 49 | // 세이브 로드 여부 확인 50 | if (Managers.Game.IsSaveLoad() == false) 51 | { 52 | Color _color = GetText((int)Texts.LoadButtonText).color; 53 | _color.a = 0.5f; 54 | GetText((int)Texts.LoadButtonText).color = _color; 55 | 56 | string path = "Art/UI/Classic_RPG_GUI/Parts/mid_button_off"; 57 | GetButton((int)Buttons.LoadButton).GetComponent().sprite = Managers.Resource.Load(path); 58 | } 59 | 60 | return true; 61 | } 62 | 63 | // 시작 버튼 64 | private void OnClickStartButton() 65 | { 66 | Managers.Scene.LoadScene(Define.Scene.PlayerCustom); 67 | } 68 | 69 | // 세이브 로드 버튼 70 | private void OnClickLoadButton() 71 | { 72 | if (Managers.Game.LoadGame() == false) 73 | return; 74 | 75 | if(Application.internetReachability == NetworkReachability.NotReachable) 76 | { 77 | // 인터넷 연결이 안되었을 때 행동 78 | Managers.UI.MakeSubItem().SetInfo("네트워크 연결이 필요합니다.", Color.red); 79 | } 80 | else if(Application.internetReachability == NetworkReachability.ReachableViaCarrierDataNetwork) 81 | { 82 | // 데이터로 연결이 되었을 때 행동 83 | Managers.UI.ShowPopupUI().SetInfo(Define.Scene.Game, 6); 84 | } 85 | else 86 | { 87 | // 와이파이로 연결이 되었을 때 행동 88 | Managers.UI.ShowPopupUI().SetInfo(Define.Scene.Game, 7); 89 | } 90 | } 91 | 92 | // 나가기 버튼 93 | private void OnClickExitButton() 94 | { 95 | Application.Quit(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_ArmorSlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.EventSystems; 5 | using UnityEngine.UI; 6 | 7 | /* 8 | * File : UI_ArmorSlot.cs 9 | * Desc : UI_EqStatPopup.cs의 하위객체에서 사용되며 방어구 아이템을 장착/해제할 수 있다. 10 | * 11 | & Functions 12 | & [Public] 13 | & : SetInfo() - 기능 설정 14 | & : ChangeArmor() - 방어구 장착 및 교체 15 | & : AddItem() - 아이템 추가 16 | & : ClearSlot() - 초기화 17 | & 18 | & [Protected] 19 | & : OnClickSlot() - 슬롯 우클릭 시 "장비 해제" 20 | & : OnEndDragSlot() - 마우스 클릭을 해제한 위치가 UI라면 "인벤으로 보내기" 21 | & : OnDropSlot() - 현재 슬롯에 마우스 클릭을 때면 "장비 장착" 22 | & : ChangeSlot() - 슬롯 교체 23 | & 24 | & [Private] 25 | & : AddArmor() - 장비 장착 진행 26 | & : EquipmentActive() - 장비 파츠 확인 27 | * 28 | */ 29 | 30 | public class UI_ArmorSlot : UI_ItemDragSlot 31 | { 32 | public Define.ArmorType armorType = Define.ArmorType.Unknown; 33 | public ArmorItemData armorItem; 34 | 35 | public override void SetInfo() 36 | { 37 | Managers.Game._playScene._equipment.armorSlots.Add(this); 38 | 39 | // 해당 부위 장비가 이미 장착되어 있다면 장착 (Save Load 했을때) 40 | if (Managers.Game.CurrentArmor.TryGetValue(armorType, out armorItem) == true) 41 | { 42 | base.AddItem(armorItem); 43 | AddArmor(armorItem); 44 | } 45 | 46 | base.SetInfo(); 47 | } 48 | 49 | // 방어구 교체 50 | public void ChangeArmor(UI_ItemSlot itemSlot) 51 | { 52 | ChangeSlot(itemSlot); 53 | } 54 | 55 | public override void AddItem(ItemData _item, int count = 1) 56 | { 57 | base.AddItem(_item, count); 58 | 59 | armorItem = _item as ArmorItemData; 60 | 61 | // 장착 중인 장비가 있다면 비활성화 62 | if (Managers.Game.CurrentArmor.ContainsKey(armorType) == true) 63 | { 64 | // 현재 장착한 장비 가져오기 65 | ArmorItemData currentArmor = Managers.Game.CurrentArmor[armorType]; 66 | 67 | // 플레이어가 현재 입고 있는 장비 오브젝트 비활성화 68 | EquipmentActive(currentArmor, false); 69 | 70 | // 스탯 해제 71 | Managers.Game.RefreshArmor(currentArmor, false); 72 | } 73 | 74 | // 방어구 장착 75 | AddArmor(armorItem); 76 | } 77 | 78 | protected override void OnClickSlot(PointerEventData eventData) 79 | { 80 | if (item.IsNull() == true || UI_DragSlot.instance.dragSlotItem.IsNull() == false) 81 | return; 82 | 83 | // 우클릭하여 장비 벗기 84 | if (Input.GetMouseButtonUp(1)) 85 | { 86 | // 인벤으로 보내고 초기화 87 | if (Managers.Game._playScene._inventory.AcquireItem(armorItem) == true) 88 | ClearSlot(); 89 | } 90 | } 91 | 92 | protected override void OnEndDragSlot(PointerEventData eventData) 93 | { 94 | // 아이템을 버린 위치가 UI가 아니라면 95 | if (item.IsNull() == false && !EventSystem.current.IsPointerOverGameObject()) 96 | { 97 | // 인벤으로 보내고 초기화 98 | if (Managers.Game._playScene._inventory.AcquireItem(armorItem) == true) 99 | ClearSlot(); 100 | } 101 | 102 | base.OnEndDragSlot(eventData); 103 | } 104 | 105 | protected override void OnDropSlot(PointerEventData eventData) 106 | { 107 | UI_Slot dragSlot = UI_DragSlot.instance.dragSlotItem; 108 | 109 | if (dragSlot.IsNull() == false) 110 | { 111 | // 자기 자신이라면 취소 112 | if (dragSlot == this) 113 | return; 114 | 115 | // 장비 장착 (or 교체) 116 | ChangeSlot(dragSlot as UI_ItemSlot); 117 | } 118 | } 119 | 120 | protected override void ChangeSlot(UI_ItemSlot itemSlot) 121 | { 122 | // 장비 확인 123 | if ((itemSlot.item is ArmorItemData) == false) 124 | return; 125 | 126 | // 같은 부위 확인 127 | ArmorItemData armor = itemSlot.item as ArmorItemData; 128 | if (armorType != armor.armorType) 129 | return; 130 | 131 | // 레벨 확인 132 | if (Managers.Game.Level < armor.minLevel) 133 | { 134 | Managers.UI.MakeSubItem().SetInfo("레벨이 부족합니다.", new Color(1f, 0.5f, 0f)); 135 | return; 136 | } 137 | 138 | ItemData _tempItem = item; 139 | 140 | // 장비 장착 141 | AddItem(itemSlot.item); 142 | 143 | // 기존 장비 인벤 이동 144 | UI_InvenSlot inven = itemSlot as UI_InvenSlot; 145 | if (_tempItem.IsNull() == false) 146 | inven.AddItem(_tempItem); 147 | else 148 | inven.ClearSlot(); 149 | } 150 | 151 | // 장비 장착 152 | private void AddArmor(ArmorItemData armorItem) 153 | { 154 | // 장비 장착 진행 155 | if (Managers.Game.CurrentArmor.ContainsKey(armorType) == false) 156 | Managers.Game.CurrentArmor.Add(armorType, armorItem); 157 | else 158 | Managers.Game.CurrentArmor[armorType] = armorItem; 159 | 160 | // 장비 오브젝트 활성화 161 | EquipmentActive(armorItem, true); 162 | 163 | // 스탯 적용 164 | Managers.Game.RefreshArmor(armorItem, true); 165 | } 166 | 167 | // 캐릭터 장비 파츠 활성화 여부 168 | private void EquipmentActive(ArmorItemData armor, bool isActive) 169 | { 170 | // 아이템이 현재 입고 있는 장비를 알고 있다면 171 | if (armor.charEquipment.IsNull() == false) 172 | { 173 | foreach(GameObject obj in armor.charEquipment) 174 | obj.SetActive(isActive); 175 | 176 | return; 177 | } 178 | 179 | // 모른다면 id로 찾기 180 | PlayerController player = Managers.Game.GetPlayer().GetComponent(); 181 | 182 | List objList = new List(); 183 | if (player.charEquipment.TryGetValue(armor.id, out objList) == false) 184 | { 185 | Debug.Log($"{armor.id} : 활성화 실패"); 186 | return; 187 | } 188 | 189 | // 아이템 안에 넣어주기 190 | armor.charEquipment = objList; 191 | 192 | foreach(GameObject obj in objList) 193 | obj.SetActive(isActive); 194 | } 195 | 196 | public override void ClearSlot() 197 | { 198 | base.ClearSlot(); 199 | 200 | EquipmentActive(armorItem, false); // 장비 비활성화 201 | Managers.Game.RefreshArmor(armorItem, false); // 장비 스탯 해제 202 | armorItem = null; 203 | Managers.Game.CurrentArmor.Remove(armorType); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_CustomButton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : UI_CustomButton.cs 8 | * Desc : UI_CustomScene.cs에서 생성되며 파츠 교체하는 버튼을 담당 9 | * 10 | & Functions 11 | & [Public] 12 | & : Init() - 초기 설정 13 | & : SetInfo() - 기능 설정 14 | * 15 | */ 16 | 17 | public class UI_CustomButton : UI_Base 18 | { 19 | enum Buttons 20 | { 21 | NextButton, 22 | BackButton, 23 | } 24 | 25 | public Define.DefaultPart partType; // 기본 파츠 타입 26 | 27 | private CharacterCustom _custom; // 커스텀 캐릭터 Object 28 | 29 | public override bool Init() 30 | { 31 | if (base.Init() == false) 32 | return false; 33 | 34 | BindButton(typeof(Buttons)); 35 | 36 | // ▶ 클릭 버튼 37 | GetButton((int)Buttons.NextButton).onClick.AddListener(()=>{ _custom.NextPart(partType, true); }); 38 | 39 | // ◀ 클릭 버튼 40 | GetButton((int)Buttons.BackButton).onClick.AddListener(()=>{ _custom.NextPart(partType, false); }); 41 | 42 | return true; 43 | } 44 | 45 | public void SetInfo(CharacterCustom custom) { _custom = custom; } 46 | } 47 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_DragSlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | /* 7 | * File : UI_DragSlot.cs 8 | * Desc : 마우스로 슬롯이 옮겨지는 과정을 보여주기 위한 슬롯 9 | * 10 | & Functions 11 | & [Public] 12 | & : DragSetImage() - 드래그할 경우 이미지 활성화 13 | & : SetColor() - 색깔 설정 14 | * 15 | */ 16 | 17 | public class UI_DragSlot : MonoBehaviour 18 | { 19 | public static UI_DragSlot instance; 20 | 21 | public UI_Slot dragSlotItem; // 슬롯 담는 변수 22 | public Image icon; // 아이템 이미지 23 | 24 | void Start() 25 | { 26 | instance = this; 27 | } 28 | 29 | // 드래그 할 경우 활성화 30 | public void DragSetImage(Image _icon) 31 | { 32 | Managers.UI.SetOrder(GetComponent()); 33 | icon.sprite = _icon.sprite; 34 | SetColor(1); 35 | } 36 | 37 | public void SetColor(float _alpha) 38 | { 39 | Color color = icon.color; 40 | color.a = _alpha; 41 | icon.color = color; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_Guide.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using TMPro; 5 | 6 | /* 7 | * File : UI_Guide.cs 8 | * Desc : 안내문, 경고문 등 상황에 띄울 수 있는 가이드 UI 9 | * 10 | & Functions 11 | & [Public] 12 | & : SetInfo() - 기능 설정 (안내 메시지 설정) 13 | & 14 | & [Private] 15 | & : MessageCoroutine() - 메시지가 붕뜨며 사라지는 코루틴 16 | * 17 | */ 18 | 19 | public class UI_Guide : UI_Base 20 | { 21 | [SerializeField] 22 | private TextMeshProUGUI _messageText; 23 | private Color _color; 24 | private Coroutine co; 25 | 26 | public void SetInfo(string messageText, Color color) 27 | { 28 | // 초기화 29 | _messageText.text = messageText; 30 | _messageText.transform.localPosition = Vector3.zero; 31 | _color = color; 32 | _messageText.color = _color; 33 | 34 | if (co.IsNull() == false) StopCoroutine(co); 35 | co = StartCoroutine(MessageCoroutine()); 36 | } 37 | 38 | private IEnumerator MessageCoroutine() 39 | { 40 | yield return new WaitForSeconds(1f); 41 | 42 | // 점점 사라지며 올라가기 43 | for(float i=1.0f; i>=0.0f; i-=0.01f) 44 | { 45 | _color.a = i; 46 | _messageText.color = _color; 47 | 48 | _messageText.transform.localPosition += Vector3.up * 0.7f; 49 | 50 | yield return null; 51 | } 52 | 53 | Managers.Resource.Destroy(gameObject); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_ItemDragSlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.EventSystems; 5 | 6 | /* 7 | * File : UI_ItemDragSlot.cs 8 | * Desc : Drag, Drop PointerEvent가 필요한 Slot이 상속 받는다. 9 | * 10 | & Functions 11 | & [Protected] 12 | & : OnBeginDragSlot() - 드래그 시작 "DragSlot 생성" 13 | & : OnDragSlot() - 드래그 진행 "드래그 방향으로 DragSlot 이동" 14 | & : OnEndDragSlot() - 드래그 종료 "DragSlot 초기화" 15 | & : ChangeSlot() - 슬롯 교체 16 | * 17 | */ 18 | 19 | public class UI_ItemDragSlot : UI_ItemSlot 20 | { 21 | // 드래그를 시작할 때 22 | protected override void OnBeginDragSlot(PointerEventData eventData) 23 | { 24 | // 아이템이 존재할 시 마우스로 들기 가능. 25 | if (item.IsNull() == true) 26 | return; 27 | 28 | // dragSlot 활성화 29 | UI_DragSlot.instance.dragSlotItem = this; 30 | UI_DragSlot.instance.DragSetImage(icon); 31 | 32 | UI_DragSlot.instance.icon.transform.position = eventData.position; 33 | } 34 | 35 | // 드래그 중일 때 36 | protected override void OnDragSlot(PointerEventData eventData) 37 | { 38 | // 마우스 드래그 방향으로 아이템 이동 39 | if (item.IsNull() == false && UI_DragSlot.instance.dragSlotItem.IsNull() == false) 40 | UI_DragSlot.instance.icon.transform.position = eventData.position; 41 | } 42 | 43 | // 드래그가 끝나면 44 | protected override void OnEndDragSlot(PointerEventData eventData) 45 | { 46 | // dragSlot 초기화 47 | UI_DragSlot.instance.SetColor(0); 48 | UI_DragSlot.instance.dragSlotItem = null; 49 | } 50 | 51 | // 슬롯 바꾸기 52 | protected virtual void ChangeSlot(UI_ItemSlot itemSlot) {} 53 | } 54 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_ItemSlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | using UnityEngine.EventSystems; 4 | using UnityEngine.UI; 5 | 6 | /* 7 | * File : UI_ItemSlot.cs 8 | * Desc : 모든 Item관련 Slot은 해당 클래스를 상속받는다. 9 | * 10 | & Functions 11 | & [Public] 12 | & : SetInfo() - 기능 설정 13 | & : AddItem() - 아이템 추가 14 | & : SetCount() - 개수 설정 15 | & : ClearSlot() - 초기화 16 | & 17 | & [Protected] 18 | & : OnEnterSlot() - 마우스가 Slot과 접촉하면 SlotTip 활성화 19 | & : OnExitSlot() - 마우스가 Slot과 접촉이 해제되면 SlotTip 비활성화 20 | & : SetColor() - 색깔 설정 21 | * 22 | */ 23 | 24 | public class UI_ItemSlot : UI_Slot 25 | { 26 | enum Texts { ItemCountText, } 27 | 28 | public ItemData item; 29 | public int itemCount; 30 | 31 | public override void SetInfo() 32 | { 33 | base.SetInfo(); 34 | 35 | BindText(typeof(Texts)); 36 | } 37 | 38 | // 아이템 등록 39 | public virtual void AddItem(ItemData _item, int count = 1) 40 | { 41 | item = _item; 42 | 43 | // 장비가 아니라면 개수 설정 44 | if ((item is UseItemData) == true) 45 | { 46 | (item as UseItemData).itemCount = count; 47 | itemCount = count; 48 | GetText((int)Texts.ItemCountText).text = itemCount.ToString(); 49 | } 50 | else 51 | { 52 | itemCount = 1; 53 | 54 | if (GetText((int)Texts.ItemCountText).IsNull() == false) 55 | GetText((int)Texts.ItemCountText).text = ""; 56 | } 57 | 58 | if (item.itemIcon.IsFakeNull() == true) 59 | item.itemIcon = Managers.Data.Item[item.id].itemIcon; 60 | 61 | // Spirte 넣기 62 | // try는 null체크 시 없는 객체면 item Data에서 빼옴. 63 | try 64 | { 65 | icon.sprite = item.itemIcon; 66 | } 67 | catch 68 | { 69 | icon.sprite = item.itemIcon = Managers.Data.Item[item.id].itemIcon; 70 | } 71 | 72 | // 색 활성화 73 | SetColor(255); 74 | } 75 | 76 | // 아이템 개수 업데이트 77 | public virtual void SetCount(int count = 1) 78 | { 79 | itemCount += count; 80 | GetText((int)Texts.ItemCountText).text = itemCount.ToString(); 81 | 82 | if (item is UseItemData) 83 | (item as UseItemData).itemCount += count; 84 | 85 | // 개수가 없다면 86 | if (itemCount <= 0) 87 | ClearSlot(); 88 | } 89 | 90 | // 마우스가 슬롯에 닿았다면 정보 활성화 91 | protected override void OnEnterSlot(PointerEventData eventData) 92 | { 93 | if (item.IsNull() == false) 94 | { 95 | Managers.Game._playScene._slotTip.OnSlotTip(true); 96 | Managers.Game._playScene._slotTip.background.position = icon.transform.position; 97 | Managers.Game._playScene._slotTip.RefreshUI(item); 98 | } 99 | } 100 | 101 | // 마우스가 슬롯에서 빠져나오면 정보 비활성화 102 | protected override void OnExitSlot(PointerEventData eventData) 103 | { 104 | if (item.IsNull() == false) 105 | Managers.Game._playScene._slotTip.OnSlotTip(false); 106 | } 107 | 108 | // 투명도 설정 (0 ~ 255) 109 | protected override void SetColor(float _alpha) 110 | { 111 | base.SetColor(_alpha); 112 | 113 | if (GetText((int)Texts.ItemCountText).IsNull() == false) 114 | GetText((int)Texts.ItemCountText).color = icon.color; 115 | } 116 | 117 | public override void ClearSlot() 118 | { 119 | base.ClearSlot(); 120 | item = null; 121 | itemCount = 0; 122 | 123 | if (GetText((int)Texts.ItemCountText).IsNull() == false) 124 | GetText((int)Texts.ItemCountText).text = ""; 125 | 126 | Managers.Game._playScene._slotTip.OnSlotTip(false); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_QuestButton.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using TMPro; 4 | using UnityEngine.EventSystems; 5 | using UnityEngine.UI; 6 | using UnityEngine; 7 | 8 | /* 9 | * File : UI_QuestButton.cs 10 | * Desc : UI_QuestPopup.cs에서 생성되며 퀘스트 정보를 활성화하는 버튼으로 사용 11 | * 12 | & Functions 13 | & [Public] 14 | & : Init() - 아이템 추가 15 | & : SetInfo() - 기능 설정 16 | & 17 | & [Private] 18 | & : OnClickSceneNoticeButton() - 클릭 시 Scene UI에 퀘스트 알람 추가 19 | * 20 | */ 21 | 22 | public class UI_QuestButton : UI_Base 23 | { 24 | enum Buttons { QuestSceneButton } 25 | enum Images { QuestSceneOkIcon } 26 | enum Texts { QuestSlotText } 27 | 28 | private QuestData _quest; 29 | 30 | private string slotText; // 퀘스트 제목 31 | private bool isNotice = true; // 퀘스트 알람 32 | 33 | public override bool Init() 34 | { 35 | if (base.Init() == false) 36 | return false; 37 | 38 | BindButton(typeof(Buttons)); 39 | BindImage(typeof(Images)); 40 | BindText(typeof(Texts)); 41 | 42 | gameObject.BindEvent((PointerEventData eventData)=> 43 | { 44 | Managers.Game._playScene._quest.OnQuest(_quest); 45 | }); 46 | 47 | GetButton((int)Buttons.QuestSceneButton).onClick.AddListener(OnClickSceneNoticeButton); 48 | 49 | GetImage((int)Images.QuestSceneOkIcon).gameObject.SetActive(!isNotice); 50 | 51 | GetText((int)Texts.QuestSlotText).text = slotText; 52 | 53 | return true; 54 | } 55 | 56 | public void SetInfo(QuestData quest) 57 | { 58 | _quest = quest; 59 | slotText = _quest.titleName; 60 | } 61 | 62 | // 씬에 퀘스트 알림 추가 63 | private void OnClickSceneNoticeButton() 64 | { 65 | isNotice = !isNotice; 66 | 67 | // 알람 활성화/비활성화 68 | if (isNotice == true) 69 | isNotice = Managers.Game._playScene._quest.SetQuestNotice(_quest); 70 | else 71 | Managers.Game._playScene._quest.CloseQuestNotice(_quest); 72 | 73 | GetImage((int)Images.QuestSceneOkIcon).gameObject.SetActive(!isNotice); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_QuestNoticeSlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using TMPro; 4 | using UnityEngine; 5 | 6 | /* 7 | * File : UI_QuestNoticeSlot.cs 8 | * Desc : Scene UI에 생성된 퀘스트 알람 기능 9 | * 10 | & Functions 11 | & : FixedUpdate() - 실시간 퀘스트 내용 업데이트 12 | & : SetInfo() - 기능 설정 (퀘스트 정보 받기) 13 | * 14 | */ 15 | 16 | public class UI_QuestNoticeSlot : UI_Base 17 | { 18 | enum Texts 19 | { 20 | QuestNameText, 21 | QuestDescText 22 | } 23 | 24 | public QuestData _quest; 25 | 26 | private string _targetName; // 목표 이름 27 | private string _questNameText; // 퀘스트 제목 28 | private string _qeustDescText; // 퀘스트 내용 29 | 30 | private bool isSuccess = false; 31 | 32 | public override bool Init() 33 | { 34 | if (base.Init() == false) 35 | return false; 36 | 37 | BindText(typeof(Texts)); 38 | 39 | GetText((int)Texts.QuestNameText).text = _questNameText; 40 | GetText((int)Texts.QuestDescText).text = _qeustDescText; 41 | 42 | return true; 43 | } 44 | 45 | void FixedUpdate() 46 | { 47 | if (_quest.IsNull() == true || isSuccess == true) 48 | return; 49 | 50 | // 퀘스트 목표 달성 시 51 | if (_quest.currnetTargetCount == _quest.targetCount) 52 | { 53 | // text 완료 표시 54 | GetText((int)Texts.QuestNameText).text = _quest.titleName + $@" [완료]"; 55 | isSuccess = true; 56 | } 57 | 58 | // 퀘스트 진행 상황 표시 59 | if (GetText((int)Texts.QuestDescText).IsNull() == false) 60 | GetText((int)Texts.QuestDescText).text = $"{_targetName} : {_quest.currnetTargetCount} / {_quest.targetCount}"; 61 | } 62 | 63 | public void SetInfo(QuestData quest) 64 | { 65 | _quest = quest; 66 | 67 | // 퀘스트 타겟 이름 68 | _targetName = Managers.Data.Monster[_quest.targetId].GetComponent().Name; 69 | 70 | // 퀘스트 제목 71 | _questNameText = quest.titleName; 72 | _qeustDescText = $"{_targetName} : {_quest.currnetTargetCount} / {_quest.targetCount}"; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_ShopBuySlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using TMPro; 4 | using UnityEngine; 5 | using UnityEngine.UI; 6 | using UnityEngine.EventSystems; 7 | 8 | /* 9 | * File : UI_ShopBuySlot.cs 10 | * Desc : UI_ShopPopup.cs에서 생성되며 아이템 구매 버튼을 담당 11 | * 12 | & Functions 13 | & [Public] 14 | & : Init() - 초기 설정 15 | & : SetInfo() - 기능 설정 16 | & 17 | & [Prviate] 18 | & : OnClickBuyButton() - 구매 버튼 클릭 기능 19 | * 20 | */ 21 | 22 | public class UI_ShopBuySlot : UI_ItemSlot 23 | { 24 | enum Images 25 | { 26 | BuyItemImage, 27 | } 28 | 29 | enum Texts 30 | { 31 | BuyItemName, 32 | BuyItemPrice, 33 | } 34 | 35 | private Sprite buySprite; // 구매 아이템 sprite 36 | private string itemNameText; // 아이템 이름 text 37 | private string itemPriceText; // 아이템 가격 text 38 | 39 | public override bool Init() 40 | { 41 | if (base.Init() == false) 42 | return false; 43 | 44 | BindImage(typeof(Images)); 45 | BindText(typeof(Texts)); 46 | 47 | icon = GetImage((int)Images.BuyItemImage); 48 | icon.sprite = buySprite; 49 | 50 | GetText((int)Texts.BuyItemName).text = itemNameText; 51 | GetText((int)Texts.BuyItemPrice).text = itemPriceText; 52 | 53 | // 버튼 기능 등록 (onClick.AddListener이랑 같음.) 54 | gameObject.BindEvent(OnClickBuyButton, Define.UIEvent.Click); 55 | 56 | SetEventHandler(); 57 | 58 | return true; 59 | } 60 | 61 | public void SetInfo(ItemData itemData) 62 | { 63 | item = itemData; 64 | 65 | buySprite = item.itemIcon; 66 | itemNameText = item.itemName; 67 | itemPriceText = item.itemPrice.ToString(); 68 | } 69 | 70 | private void OnClickBuyButton(PointerEventData eventData) 71 | { 72 | // 인벤 크기 확인 73 | if (Managers.Game._playScene._inventory.IsInvenMaxSize() == true) 74 | { 75 | Managers.UI.MakeSubItem().SetInfo("인벤토리가 가득 찼습니다.", Color.red); 76 | return; 77 | } 78 | 79 | Managers.Game._playScene._slotTip.OnSlotTip(false); 80 | 81 | // 금액 확인 82 | if (Managers.Game.Gold < item.itemPrice) 83 | { 84 | Managers.UI.MakeSubItem().SetInfo("금액이 부족합니다.", Color.yellow); 85 | return; 86 | } 87 | 88 | // < 구매 시작 > 89 | // 소비 아이템이면 개수 선택 90 | if (item.itemType == Define.ItemType.Use) 91 | { 92 | UI_NumberCheckPopup numberCheckPopup = Managers.UI.ShowPopupUI(); 93 | if (numberCheckPopup.IsNull() == true) 94 | return; 95 | 96 | numberCheckPopup.SetInfo(item, (int itemCount)=> 97 | { 98 | Managers.Game.Gold -= item.itemPrice * itemCount; 99 | Managers.Game._playScene._inventory.AcquireItem(item.ItemClone(), itemCount); 100 | }); 101 | } 102 | else 103 | { 104 | UI_ConfirmPopup confirmPopup = Managers.UI.ShowPopupUI(); 105 | if (confirmPopup.IsNull() == true) 106 | return; 107 | 108 | confirmPopup.SetInfo(()=> 109 | { 110 | Managers.Game.Gold -= item.itemPrice; 111 | Managers.Game._playScene._inventory.AcquireItem(item.ItemClone()); 112 | }, Define.ShopSaleMessage); 113 | } 114 | } 115 | 116 | public override void SetInfo() {} 117 | } 118 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_ShopSaleSlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | /* 7 | * File : UI_ShopSaleSlot.cs 8 | * Desc : UI_ShopPopup.cs에서 생성되며 아이템 판매가 등록됐을 때 기능 9 | * 10 | & Functions 11 | & [Public] 12 | & : Init() - 초기 설정 13 | & : SetInfo() - 기능 설정 14 | & : GetSale() - 판매 진행 15 | & : Clear() - 초기화 16 | & 17 | & [Prviate] 18 | & : OnClickCloseButton() - 판매 등록 취소 19 | * 20 | */ 21 | 22 | public class UI_ShopSaleSlot : UI_Base 23 | { 24 | enum Buttons 25 | { 26 | CloseButton, 27 | } 28 | 29 | enum Images 30 | { 31 | SaleItemIcon, 32 | } 33 | 34 | enum Texts 35 | { 36 | SaleItemCountText, 37 | } 38 | 39 | private UI_InvenSlot _invenItem; // 인벤토리 슬롯 40 | private Image _icon; 41 | 42 | private int _saleItemCount = 0; // 판매될 개수 43 | private string _itemCountText; 44 | 45 | public override bool Init() 46 | { 47 | if (base.Init() == false) 48 | return false; 49 | 50 | BindButton(typeof(Buttons)); 51 | BindImage(typeof(Images)); 52 | BindText(typeof(Texts)); 53 | 54 | GetButton((int)Buttons.CloseButton).onClick.AddListener(OnClickCloseButton); 55 | 56 | GetImage((int)Images.SaleItemIcon).sprite = _icon.sprite; 57 | GetText((int)Texts.SaleItemCountText).text = _itemCountText; 58 | 59 | return true; 60 | } 61 | 62 | public void SetInfo(UI_InvenSlot invenItem, int subItemCount = 1) 63 | { 64 | _invenItem = invenItem; 65 | _saleItemCount = subItemCount; 66 | _icon = _invenItem.icon; 67 | 68 | // 판매할 인벤토리의 슬롯 잠그기 69 | _invenItem.IsLock = true; 70 | 71 | // 소비 아이템이면 개수 활성화 72 | if (invenItem.item is UseItemData) 73 | _itemCountText = _saleItemCount.ToString(); 74 | else 75 | _itemCountText = ""; 76 | } 77 | 78 | // 판매 진행 79 | public void GetSale() 80 | { 81 | // 장비면 강화 확인 후 판매 82 | if ((_invenItem.item is EquipmentData) == true) 83 | { 84 | EquipmentData equipment = _invenItem.item as EquipmentData; 85 | Managers.Game.Gold += _invenItem.item.itemPrice + (int)((equipment.itemPrice / 4) * (equipment.upgradeCount)); 86 | } 87 | else 88 | Managers.Game.Gold += _invenItem.item.itemPrice * _saleItemCount; 89 | 90 | // 판매된 슬롯에 개수 차감 91 | _invenItem.SetCount(-_saleItemCount); 92 | 93 | Clear(); 94 | } 95 | 96 | // 판매 등록 취소 97 | private void OnClickCloseButton() 98 | { 99 | Managers.Game._playScene._shop.saleList.Remove(this); 100 | 101 | Clear(); 102 | } 103 | 104 | public void Clear() 105 | { 106 | _invenItem.IsLock = false; 107 | 108 | Managers.Resource.Destroy(this.gameObject); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_SkillBarSlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using TMPro; 4 | using UnityEngine; 5 | using UnityEngine.UI; 6 | using UnityEngine.EventSystems; 7 | 8 | /* 9 | * File : UI_SkillBarSlot.cs 10 | * Desc : Scene UI의 하단 퀵슬롯에서 스킬바로 사용되며 11 | * 스킬이 적용될 시 key를 눌러 스킬 사용이 가능하다. 12 | * 13 | & Functions 14 | & [Public] 15 | & : SetInfo() - 기능 설정 16 | & : ClearSlot() - 초기화 17 | & 18 | & [Protected] 19 | & : OnEndDragSlot() - 마우스 클릭을 해제하면 "UI가 아니면 초기화" 20 | & : OnDropSlot() - 현재 슬롯에 마우스 클릭을 때면 "스킬 등록" 21 | & 22 | & [Prviate] 23 | & : ChangeSkill() - 스킬 교체 24 | & : SetSkill() - 스킬 설정 25 | & : UpdateCoolDown() - 쿨타임 26 | & : IsCoolDown() - 쿨타임 여부 27 | * 28 | */ 29 | 30 | public class UI_SkillBarSlot : UI_SkillSlot 31 | { 32 | enum Images 33 | { 34 | CoolDownBlock, 35 | ItemImage, 36 | } 37 | 38 | enum Texts 39 | { 40 | MpText, 41 | } 42 | 43 | [SerializeField] 44 | private Define.KeySkill keySkill; // 입력 key 45 | private Image coolDownImage; // 쿨타임 이미지 46 | 47 | public override void SetInfo() 48 | { 49 | BindImage(typeof(Images)); 50 | BindText(typeof(Texts)); 51 | 52 | coolDownImage = GetImage((int)Images.CoolDownBlock); 53 | coolDownImage.gameObject.SetActive(false); 54 | 55 | GetText((int)Texts.MpText).text = ""; 56 | 57 | SetEventHandler(); 58 | 59 | // 시작할 때 스킬이 현재 키에 장착 중이라면 60 | if (Managers.Game.SkillBarList.TryGetValue(keySkill, out skillData) == true) 61 | { 62 | skillData.skillSprite = Managers.Data.Skill[skillData.skillId].skillSprite; 63 | SetSkill(skillData); 64 | } 65 | } 66 | 67 | void Update() 68 | { 69 | UpdateCoolDown(); 70 | } 71 | 72 | protected override void OnEndDragSlot(PointerEventData eventData) 73 | { 74 | // 마우스 마지막 드래그 위치가 UI가 아니라면 75 | if (skillData.IsNull() == false && !EventSystem.current.IsPointerOverGameObject()) 76 | { 77 | // 현재 전투 중인 몬스터가 없다면 초기화 78 | if (Managers.Game.currentMonster.IsNull() == true) 79 | ClearSlot(); 80 | } 81 | 82 | base.OnEndDragSlot(eventData); 83 | } 84 | 85 | protected override void OnDropSlot(PointerEventData eventData) 86 | { 87 | UI_Slot dragSlot = UI_DragSlot.instance.dragSlotItem; 88 | 89 | if (dragSlot.IsNull() == false) 90 | { 91 | // 자기 자신이라면 92 | if (dragSlot == this) 93 | return; 94 | 95 | // 스킬 슬롯 확인 96 | if ((dragSlot is UI_SkillSlot) == false) 97 | return; 98 | 99 | // 스킬 장착 100 | ChangeSkill(dragSlot as UI_SkillSlot); 101 | } 102 | } 103 | 104 | private void ChangeSkill(UI_SkillSlot skillSlot) 105 | { 106 | // 스킬 설정 107 | SetSkill(skillSlot.skillData); 108 | 109 | // 넘어온 스킬의 쿨타임 여부 110 | IsCoolDown(skillData.isCoolDown); 111 | 112 | // 기존 슬롯 삭제 113 | if (skillSlot is UI_SkillBarSlot) 114 | (skillSlot as UI_SkillBarSlot).ClearSlot(); 115 | } 116 | 117 | private void SetSkill(SkillData skill) 118 | { 119 | // 궁극기 경우 5렙 이상 스킬만 가능 120 | if (keySkill == Define.KeySkill.R) 121 | { 122 | if (skill.minLevel < 5) 123 | return; 124 | } 125 | 126 | // 기존 스킬 쿨타임 여부 127 | IsCoolDown(skillData.isCoolDown); 128 | 129 | skillData = skill; 130 | 131 | GetText((int)Texts.MpText).text = skillData.skillConsumMp.ToString(); 132 | 133 | // 게임 데이터에 스킬 저장 134 | if (Managers.Game.SkillBarList.ContainsKey(keySkill) == false) 135 | Managers.Game.SkillBarList.Add(keySkill, skillData); 136 | else 137 | Managers.Game.SkillBarList[keySkill] = skillData; 138 | 139 | try 140 | { 141 | icon.sprite = skillData.skillSprite; 142 | } 143 | catch 144 | { 145 | icon.sprite = skillData.skillSprite = Managers.Data.Skill[skillData.skillId].skillSprite; 146 | } 147 | 148 | SetColor(255); 149 | } 150 | 151 | // 쿨타임 진행 152 | private void UpdateCoolDown() 153 | { 154 | // 쿨타임 155 | if (skillData.IsNull() == true) 156 | return; 157 | 158 | if (skillData.isCoolDown == true) 159 | { 160 | // 쿨타임 객체 활성화 161 | if (coolDownImage.gameObject.activeSelf == false) 162 | coolDownImage.gameObject.SetActive(true); 163 | 164 | // 시계 방향으로 밝아지는 fillAmount 165 | coolDownImage.fillAmount -= 1 * Time.smoothDeltaTime / skillData.skillCoolDown; 166 | 167 | // fillAmount가 0이 되면 쿨타임 끝 168 | if (coolDownImage.fillAmount <= 0) 169 | { 170 | skillData.isCoolDown = false; 171 | coolDownImage.fillAmount = 1; 172 | coolDownImage.gameObject.SetActive(false); 173 | } 174 | } 175 | } 176 | 177 | // 쿨타임 여부 178 | private void IsCoolDown(bool isTrue) 179 | { 180 | coolDownImage.fillAmount = 1; 181 | 182 | skillData.isCoolDown = isTrue; 183 | coolDownImage.gameObject.SetActive(isTrue); 184 | } 185 | 186 | public override void ClearSlot() 187 | { 188 | base.ClearSlot(); 189 | 190 | // 쿨타임 이미지 초기화 191 | coolDownImage.fillAmount = 1; 192 | coolDownImage.gameObject.SetActive(false); 193 | 194 | GetText((int)Texts.MpText).text = ""; 195 | 196 | Managers.Game.SkillBarList.Remove(keySkill); 197 | skillData = null; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_SkillPopupSlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using TMPro; 4 | using UnityEngine; 5 | using UnityEngine.EventSystems; 6 | 7 | /* 8 | * File : UI_SkillPopupSlot.cs 9 | * Desc : UI_SkillPopup.cs에서 사용되며 스킬을 저장한다. 10 | * 레벨이 충족 되면 우클릭을 통해 스킬을 활성화할 수 있다. 11 | * 12 | & Functions 13 | & [Public] 14 | & : SetInfo() - 기능 설정 15 | & : ClearSlot() - 초기화 16 | & 17 | & [Protected] 18 | & : OnClickSlot() - 우클릭하여 스킬 활성화 19 | & : OnBeginDragSlot() - 슬롯 드래그 시작 20 | & : OnDragSlot() - 슬롯 드래그 진행 21 | & : OnEndDragSlot() - 슬롯 드래그 끝 22 | & 23 | & [Prviate] 24 | & : LevelCheck() - 스킬 레벨 확인 25 | * 26 | */ 27 | 28 | public class UI_SkillPopupSlot : UI_SkillSlot 29 | { 30 | enum Gameobjects 31 | { 32 | LevelBlock, 33 | } 34 | 35 | enum Texts 36 | { 37 | SkillLevelText, 38 | } 39 | 40 | [SerializeField] 41 | private int skillId; 42 | 43 | public override void SetInfo() 44 | { 45 | // 자식 객체 불러오기 46 | BindObject(typeof(Gameobjects)); 47 | BindText(typeof(Texts)); 48 | 49 | // 게임데이터에 스킬 아이디 존재 확인 50 | if (Managers.Data.Skill.TryGetValue(skillId, out skillData) == false) 51 | Debug.Log($"SkillData {skillId} : Failed"); 52 | 53 | GetText((int)Texts.SkillLevelText).text = skillData.minLevel.ToString(); 54 | icon.sprite = skillData.skillSprite; 55 | 56 | // 시작 시 스킬이 흭득 상태인지 확인 57 | foreach(SkillData skill in Managers.Game.CurrentSkill) 58 | { 59 | // 획득 상태면 Lock 해제 60 | if (skillId == skill.skillId) 61 | { 62 | skillData.isLock = skill.isLock; 63 | break; 64 | } 65 | } 66 | 67 | if (skillData.isLock == false) 68 | Managers.Resource.Destroy(GetObject((int)Gameobjects.LevelBlock)); 69 | 70 | base.SetInfo(); 71 | } 72 | 73 | protected override void OnClickSlot(PointerEventData eventData) 74 | { 75 | if (Input.GetMouseButtonUp(1) && skillData.isLock == true) 76 | { 77 | if (LevelCheck() == true) 78 | { 79 | UI_ConfirmPopup confirmPopup = Managers.UI.ShowPopupUI(); 80 | if (confirmPopup.IsNull() == true) return; 81 | 82 | confirmPopup.SetInfo(()=> 83 | { 84 | skillData.isLock = false; 85 | Managers.Game.CurrentSkill.Add(this.skillData); 86 | Managers.Resource.Destroy(GetObject((int)Gameobjects.LevelBlock)); 87 | }, Define.SkillOpenMessage); 88 | } 89 | else 90 | Managers.UI.MakeSubItem().SetInfo("레벨이 부족합니다.", new Color(1f, 0.5f, 0f)); 91 | } 92 | } 93 | 94 | protected override void OnBeginDragSlot(PointerEventData eventData) 95 | { 96 | if (skillData.isLock == false) 97 | base.OnBeginDragSlot(eventData); 98 | } 99 | 100 | protected override void OnDragSlot(PointerEventData eventData) 101 | { 102 | if (skillData.isLock == false) 103 | base.OnDragSlot(eventData); 104 | } 105 | 106 | protected override void OnEndDragSlot(PointerEventData eventData) 107 | { 108 | if (skillData.isLock == false && skillData.IsNull() == false) 109 | base.OnEndDragSlot(eventData); 110 | } 111 | 112 | // 스킬 레벨 체크 113 | private bool LevelCheck() { return Managers.Game.Level >= skillData.minLevel; } 114 | } 115 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_SkillSlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.EventSystems; 5 | 6 | /* 7 | * File : UI_SkillSlot.cs 8 | * Desc : 모든 Skill 관련 슬롯은 해당 클래스를 상속 받는다. 9 | * 10 | & Functions 11 | & [Protected] 12 | & : OnBeginDragSlot() - 슬롯 드래그 시작 13 | & : OnDragSlot() - 슬롯 드래그 진행 14 | & : OnEndDragSlot() - 슬롯 드래그 끝 15 | * 16 | */ 17 | 18 | public class UI_SkillSlot : UI_Slot 19 | { 20 | public SkillData skillData; 21 | 22 | // 스킬이 등록된 상태라면 마우스로 들기 가능. 23 | protected override void OnBeginDragSlot(PointerEventData eventData) 24 | { 25 | if (skillData.IsNull() == true) 26 | return; 27 | 28 | UI_DragSlot.instance.dragSlotItem = this; 29 | UI_DragSlot.instance.DragSetImage(icon); 30 | 31 | UI_DragSlot.instance.icon.transform.position = eventData.position; 32 | } 33 | 34 | // 마우스 드래그 방향으로 이동 35 | protected override void OnDragSlot(PointerEventData eventData) 36 | { 37 | if (skillData.IsNull() == false) 38 | UI_DragSlot.instance.icon.transform.position = eventData.position; 39 | } 40 | 41 | protected override void OnEndDragSlot(PointerEventData eventData) 42 | { 43 | UI_DragSlot.instance.SetColor(0); 44 | UI_DragSlot.instance.dragSlotItem = null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_Slot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.EventSystems; 5 | using UnityEngine.UI; 6 | 7 | /* 8 | * File : UI_Slot.cs 9 | * Desc : 모든 슬롯은 해당 클래스를 상속 받는다. 10 | * 11 | & Functions 12 | & [Public] 13 | & : Init() - 초기 설정 14 | & : SetInfo() - 기능 설정 15 | & : RefreshUI() - 새로고침 UI 16 | & : ClearSlot() - 초기화 17 | & 18 | & [Protected] 19 | & : SetEventHandler() - EventHandler 설정 20 | & : OnEnterSlot() - 마우스 포인터가 나랑 닿을 경우 21 | & : OnExitSlot() - 마우스 포인터가 나에게서 벗어날 경우 22 | & : OnClickSlot() - 마우스 나를 클릭할 경우 23 | & : OnBeginDragSlot() - 마우스 드래그 시작 24 | & : OnDragSlot() - 마우스 드래그 진행 25 | & : OnEndDragSlot() - 마우스 드래그 종료 26 | & : OnDropSlot() - 마우스 드래그가 내 위에서 끝났을 때 27 | & : SetColor() - 투명도 설정 (0 ~ 255) 28 | * 29 | */ 30 | 31 | public abstract class UI_Slot : UI_Base 32 | { 33 | enum Images { ItemImage, } 34 | 35 | public Image icon; 36 | 37 | public override bool Init() 38 | { 39 | if (base.Init() == false) 40 | return false; 41 | 42 | SetInfo(); 43 | SetEventHandler(); 44 | 45 | return true; 46 | } 47 | 48 | public virtual void SetInfo() 49 | { 50 | BindImage(typeof(Images)); 51 | icon = GetImage((int)Images.ItemImage); 52 | } 53 | 54 | public virtual void RefreshUI() {} 55 | 56 | protected virtual void SetEventHandler() 57 | { 58 | gameObject.BindEvent((PointerEventData eventData)=>{ OnEnterSlot(eventData); }, Define.UIEvent.Enter); 59 | gameObject.BindEvent((PointerEventData eventData)=>{ OnExitSlot(eventData); }, Define.UIEvent.Exit); 60 | gameObject.BindEvent((PointerEventData eventData)=>{ OnClickSlot(eventData); }, Define.UIEvent.Click); 61 | gameObject.BindEvent((PointerEventData eventData)=>{ OnBeginDragSlot(eventData); }, Define.UIEvent.BeginDrag); 62 | gameObject.BindEvent((PointerEventData eventData)=>{ OnDragSlot(eventData); }, Define.UIEvent.Drag); 63 | gameObject.BindEvent((PointerEventData eventData)=>{ OnEndDragSlot(eventData); }, Define.UIEvent.EndDrag); 64 | gameObject.BindEvent((PointerEventData eventData)=>{ OnDropSlot(eventData); }, Define.UIEvent.Drop); 65 | } 66 | 67 | protected virtual void OnEnterSlot(PointerEventData eventData) {} // 마우스 포인터가 나랑 닿을 경우 68 | protected virtual void OnExitSlot(PointerEventData eventData) {} // 마우스 포인터가 나에게서 벗어날 경우 69 | protected virtual void OnClickSlot(PointerEventData eventData) {} // 마우스 나를 클릭할 경우 70 | protected virtual void OnBeginDragSlot(PointerEventData eventData) {} // 마우스 드래그 시작 71 | protected virtual void OnDragSlot(PointerEventData eventData) {} // 마우스 드래그 진행 72 | protected virtual void OnEndDragSlot(PointerEventData eventData) {} // 마우스 드래그 종료 73 | protected virtual void OnDropSlot(PointerEventData eventData) {} // 마우스 드래그가 내 위에서 끝났을 때 74 | 75 | // 투명도 설정 (0 ~ 255) 76 | protected virtual void SetColor(float _alpha) 77 | { 78 | Color color = icon.color; 79 | color.a = _alpha; 80 | icon.color = color; 81 | } 82 | 83 | // 슬롯 초기화 84 | public virtual void ClearSlot() 85 | { 86 | icon.sprite = null; 87 | 88 | SetColor(0); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_UpgradeSlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | using UnityEngine.EventSystems; 6 | 7 | /* 8 | * File : UI_UpgradeSlot.cs 9 | * Desc : UI_UpgradePopup.cs에서 사용되며 장비를 업그레이드하는 등록 Slot 10 | * 11 | & Functions 12 | & [Public] 13 | & : SetInfo() - 기능 설정 14 | & : ClearSlot() - 초기화 15 | & 16 | & [Protected] 17 | & : OnClickSlot() - 슬롯 우클릭 시 "장비 등록 해제" 18 | & : OnEndDragSlot() - 마우스 클릭을 해제하면 "등록 해제" 19 | & : OnDropSlot() - 현재 슬롯에 마우스 클릭을 때면 "장비 등록" 20 | & : ChangeSlot() - 슬롯 교체 21 | & 22 | & [Private] 23 | & : GetSlotInteract() - 현재 슬롯의 아이템 타입 체크 24 | * 25 | */ 26 | 27 | public class UI_UpgradeSlot : UI_ItemDragSlot 28 | { 29 | public override void SetInfo() 30 | { 31 | base.SetInfo(); 32 | 33 | // 인벤으로 부터 우클릭 아이템 받기 등록 34 | Managers.Game._getSlotInteract -= GetSlotInteract; 35 | Managers.Game._getSlotInteract += GetSlotInteract; 36 | } 37 | 38 | protected override void OnClickSlot(PointerEventData eventData) 39 | { 40 | if (item.IsNull() == true || UI_DragSlot.instance.dragSlotItem.IsNull() == false) 41 | return; 42 | 43 | // 슬롯 우클릭 시 44 | if (Input.GetMouseButtonUp(1)) 45 | { 46 | // 인벤토리로 이동 47 | Managers.Game._playScene._inventory.AcquireItem(item); 48 | ClearSlot(); 49 | } 50 | } 51 | 52 | protected override void OnEndDragSlot(PointerEventData eventData) 53 | { 54 | // 아이템을 버린 위치가 UI가 아니라면 55 | if (item.IsNull() == false && !EventSystem.current.IsPointerOverGameObject()) 56 | { 57 | // 인벤토리로 이동 58 | Managers.Game._playScene._inventory.AcquireItem(item); 59 | ClearSlot(); 60 | } 61 | 62 | base.OnEndDragSlot(eventData); 63 | } 64 | 65 | protected override void OnDropSlot(PointerEventData eventData) 66 | { 67 | UI_Slot dragSlot = UI_DragSlot.instance.dragSlotItem; 68 | 69 | // 자기 자신 확인 70 | if (dragSlot == this) 71 | return; 72 | 73 | // 슬롯 교체 74 | ChangeSlot(dragSlot as UI_ItemSlot); 75 | } 76 | 77 | protected override void ChangeSlot(UI_ItemSlot itemSlot) 78 | { 79 | // 장비가 아니라면 80 | if ((itemSlot.item is EquipmentData) == false) 81 | return; 82 | 83 | // 강화 슬롯에 아이템이 있다면 인벤으로 돌려 보내기 84 | if (item.IsNull() == false) 85 | Managers.Game._playScene._inventory.AcquireItem(item); 86 | 87 | EquipmentData equipment = itemSlot.item as EquipmentData; 88 | 89 | Managers.Game._playScene._upgrade.RefreshUI(equipment); 90 | AddItem(itemSlot.item); 91 | 92 | (itemSlot as UI_InvenSlot).ClearSlot(); 93 | } 94 | 95 | // 인벤토리로 부터 우클릭으로 장비 받기 96 | private void GetSlotInteract(UI_InvenSlot invenSlot) 97 | { 98 | // UI_UpgradePopup Prefab이 활성화 되어 있다면 99 | if (Managers.Game._playScene._upgrade.gameObject.activeSelf == true) 100 | ChangeSlot(invenSlot); 101 | } 102 | 103 | public override void ClearSlot() 104 | { 105 | base.ClearSlot(); 106 | Managers.Game._playScene._upgrade.Clear(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_UseItemSlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | using TMPro; 6 | using UnityEngine.EventSystems; 7 | 8 | /* 9 | * File : UI_UseItemSlot.cs 10 | * Desc : Scene UI의 하단 퀵슬롯에서 소비아이템바로 사용되며 11 | * 아이템 적용될 시 key를 눌러 아이템을 사용할 수 있다. 12 | * 13 | & Functions 14 | & [Public] 15 | & : SetInfo() - 기능 설정 16 | & : AddItem() - 아이템 등록 17 | & : ClearSlot() - 초기화 18 | & 19 | & [Protected] 20 | & : OnEndDragSlot() - 마우스 클릭을 해제하면 "초기화" 21 | & : OnDropSlot() - 현재 슬롯에 마우스 클릭을 때면 "아이템 등록" 22 | & : ChangeSlot() - 슬롯 교체 23 | * 24 | */ 25 | 26 | public class UI_UseItemSlot : UI_ItemDragSlot 27 | { 28 | public int key; 29 | 30 | [SerializeField] 31 | private TextMeshProUGUI keyText; 32 | 33 | public override void SetInfo() 34 | { 35 | base.SetInfo(); 36 | 37 | keyText.text = key.ToString(); 38 | 39 | if (Managers.Game.UseItemBarList.ContainsKey(key) == true) 40 | { 41 | UseItemData useItem = Managers.Game.UseItemBarList[key]; 42 | AddItem(useItem, useItem.itemCount); 43 | } 44 | } 45 | 46 | public override void AddItem(ItemData _item, int count = 1) 47 | { 48 | base.AddItem(_item, count); 49 | 50 | if (Managers.Game.UseItemBarList.ContainsKey(key) == false) 51 | Managers.Game.UseItemBarList.Add(key, _item as UseItemData); 52 | else 53 | Managers.Game.UseItemBarList[key] = _item as UseItemData; 54 | } 55 | 56 | protected override void OnEndDragSlot(PointerEventData eventData) 57 | { 58 | if (item.IsNull() == false && !EventSystem.current.IsPointerOverGameObject()) 59 | { 60 | if (Managers.Game._playScene._inventory.AcquireItem(item, itemCount) == true) 61 | ClearSlot(); 62 | } 63 | 64 | base.OnEndDragSlot(eventData); 65 | } 66 | 67 | protected override void OnDropSlot(PointerEventData eventData) 68 | { 69 | UI_Slot dragSlot = UI_DragSlot.instance.dragSlotItem; 70 | 71 | if (dragSlot.IsNull() == false) 72 | { 73 | // 자기 자신이라면 74 | if (dragSlot == this) 75 | return; 76 | 77 | // 같은 종류의 슬롯이거나 인벤 슬롯일 때 통과 78 | if ((dragSlot is UI_UseItemSlot) == true || (dragSlot is UI_InvenSlot) == true) 79 | ChangeSlot(dragSlot as UI_ItemSlot); 80 | } 81 | } 82 | 83 | protected override void ChangeSlot(UI_ItemSlot itemSlot) 84 | { 85 | // 소비 아이템 확인 86 | if ((itemSlot.item is UseItemData) == false) 87 | return; 88 | 89 | // 지금 슬롯에 아이템이 존재할 때 90 | if (item.IsNull() == false) 91 | { 92 | // 아이디 확인 후 개수 증가 or 체인지 93 | if (item.id == itemSlot.item.id) 94 | SetCount((itemSlot.item as UseItemData).itemCount); 95 | else 96 | { 97 | if (Managers.Game._playScene._inventory.AcquireItem(item, itemCount) == false) 98 | return; 99 | 100 | AddItem(itemSlot.item, (itemSlot.item as UseItemData).itemCount); 101 | } 102 | } 103 | else AddItem(itemSlot.item, (itemSlot.item as UseItemData).itemCount); 104 | 105 | // 기존에 온 슬롯 삭제시키기 106 | if (itemSlot is UI_UseItemSlot) (itemSlot as UI_UseItemSlot).ClearSlot(); 107 | if (itemSlot is UI_InvenSlot) (itemSlot as UI_InvenSlot).ClearSlot(); 108 | } 109 | 110 | public override void ClearSlot() 111 | { 112 | base.ClearSlot(); 113 | 114 | Managers.Game.UseItemBarList[key] = null; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Scripts/UI/SubItem/UI_WeaponSlot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.EventSystems; 5 | 6 | /* 7 | * File : UI_WeaponSlot.cs 8 | * Desc : UI_EqStatPopup.cs의 하위객체에서 사용되며 무기 아이템을 장착/해제할 수 있다. 9 | * 10 | & Functions 11 | & [Public] 12 | & : SetInfo() - 기능 설정 13 | & : ChangeWeapon() - 무기 교체 14 | & : AddItem() - 아이템 등록 15 | & : UpgradeMeshEffect() - 업그레이드 이펙트 적용 16 | & : ClearSlot() - 초기화 17 | & 18 | & [Protected] 19 | & : OnClickSlot() - 슬롯 우클릭 시 "장비 해제" 20 | & : OnEndDragSlot() - 마우스 클릭을 해제하면 "초기화" 21 | & : OnDropSlot() - 현재 슬롯에 마우스 클릭을 때면 "장비 장착" 22 | & : ChangeSlot() - 슬롯 교체 23 | & 24 | & [Private] 25 | & : GetPart() - 장비 파츠 장착 26 | * 27 | */ 28 | 29 | public class UI_WeaponSlot : UI_ItemDragSlot 30 | { 31 | [SerializeField] 32 | private Define.WeaponType weaponType = Define.WeaponType.Unknown; 33 | private WeaponItemData weaponItem; 34 | 35 | public override void SetInfo() 36 | { 37 | Managers.Game._playScene._equipment.weaponSlot = this; 38 | 39 | // 해당 부위 장비가 장착되어 있다면 40 | if (Managers.Game.CurrentWeapon.id != 0) 41 | AddItem(Managers.Game.CurrentWeapon); 42 | else 43 | Managers.Game.CurrentWeapon = null; 44 | 45 | base.SetInfo(); 46 | } 47 | 48 | // 무기 장착 49 | public void ChangeWeapon(UI_ItemSlot itemSlot) { ChangeSlot(itemSlot); } 50 | 51 | public override void AddItem(ItemData _item, int count = 1) 52 | { 53 | base.AddItem(_item, count); 54 | 55 | weaponItem = _item as WeaponItemData; 56 | 57 | // 장착 중인 무기가 있다면 비활성화 58 | if (Managers.Game.CurrentWeapon.IsNull() == false) 59 | { 60 | // 장비 파츠 확인 61 | GetPart(Managers.Game.CurrentWeapon); 62 | 63 | Managers.Game.CurrentWeapon.charEquipment.SetActive(false); 64 | } 65 | 66 | // 장비 파츠 확인 67 | GetPart(weaponItem); 68 | Managers.Game.CurrentWeapon = weaponItem; 69 | 70 | UpgradeMeshEffect(weaponItem); 71 | 72 | weaponItem.charEquipment.SetActive(true); 73 | } 74 | 75 | // 업그레이드 일정 수치 넘으면 Mesh 적용 76 | public void UpgradeMeshEffect(EquipmentData equipment) 77 | { 78 | // 해당 무기 오브젝트 찾기 79 | GameObject weaponObj = (equipment as WeaponItemData).charEquipment; 80 | if (weaponObj.IsFakeNull() == true) 81 | weaponObj = (Managers.Data.Item[equipment.id] as WeaponItemData).charEquipment; 82 | 83 | // 객체 안에 자식들 삭제 84 | foreach(Transform child in weaponObj.transform) 85 | Managers.Resource.Destroy(child.gameObject); 86 | 87 | // 레벨 충족이 안되면 종료 88 | if (equipment.upgradeCount < 6) 89 | return; 90 | 91 | // 무기 객체 자식으로 이펙트 배치 92 | string path = "Effect/Upgrade/UpgradeEffect_" + equipment.upgradeCount; 93 | GameObject effectObj = Managers.Resource.Instantiate(path, weaponObj.transform); 94 | 95 | PSMeshRendererUpdater meshRenderer = effectObj.GetComponent(); 96 | meshRenderer.UpdateMeshEffect(weaponObj); 97 | } 98 | 99 | protected override void OnClickSlot(PointerEventData eventData) 100 | { 101 | if (item.IsNull() == true || UI_DragSlot.instance.dragSlotItem.IsNull() == false) 102 | return; 103 | 104 | // 장비 벗기 105 | if (Input.GetMouseButtonUp(1)) 106 | { 107 | if (Managers.Game._playScene._inventory.AcquireItem(weaponItem) == true) 108 | ClearSlot(); 109 | } 110 | } 111 | 112 | protected override void OnEndDragSlot(PointerEventData eventData) 113 | { 114 | // 아이템을 버린 위치가 UI가 아니라면 115 | if (item.IsNull() == false && !EventSystem.current.IsPointerOverGameObject()) 116 | { 117 | // 아이템 인벤으로 이동 118 | if (Managers.Game._playScene._inventory.AcquireItem(weaponItem) == true) 119 | ClearSlot(); 120 | } 121 | 122 | base.OnEndDragSlot(eventData); 123 | } 124 | 125 | protected override void OnDropSlot(PointerEventData eventData) 126 | { 127 | UI_Slot dragSlot = UI_DragSlot.instance.dragSlotItem; 128 | 129 | if (dragSlot.IsNull() == false) 130 | { 131 | // 자기 자신이라면 132 | if (dragSlot == this) 133 | return; 134 | 135 | // 장비 장착 (or 교체) 136 | ChangeSlot(dragSlot as UI_ItemSlot); 137 | } 138 | } 139 | 140 | protected override void ChangeSlot(UI_ItemSlot itemSlot) 141 | { 142 | // 장비 확인 143 | if ((itemSlot.item is WeaponItemData) == false) 144 | return; 145 | 146 | // 같은 부위 확인 147 | WeaponItemData weapon = itemSlot.item as WeaponItemData; 148 | if (weaponType != weapon.weaponType) 149 | return; 150 | 151 | // 레벨 체크 152 | if (Managers.Game.Level < weapon.minLevel) 153 | { 154 | Managers.UI.MakeSubItem().SetInfo("레벨이 부족합니다.", new Color(1f, 0.5f, 0f)); 155 | return; 156 | } 157 | 158 | ItemData _tempItem = item; 159 | 160 | // 장비 장착 161 | AddItem(itemSlot.item); 162 | 163 | // 기존 장비 인벤 이동 164 | UI_InvenSlot inven = itemSlot as UI_InvenSlot; 165 | if (_tempItem.IsNull() == false) 166 | inven.AddItem(_tempItem); 167 | else 168 | inven.ClearSlot(); 169 | } 170 | 171 | private void GetPart(WeaponItemData weapon) 172 | { 173 | if (weapon.charEquipment.IsNull() == true) 174 | weapon.charEquipment = (Managers.Data.Item[weapon.id] as WeaponItemData).charEquipment; 175 | } 176 | 177 | public override void ClearSlot() 178 | { 179 | base.ClearSlot(); 180 | 181 | Managers.Game.CurrentWeapon.charEquipment.SetActive(false); 182 | Managers.Game.CurrentWeapon = null; 183 | weaponItem = null; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /Scripts/UI/UI_Base.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using TMPro; 5 | using UnityEngine; 6 | using UnityEngine.EventSystems; 7 | using UnityEngine.UI; 8 | 9 | /* 10 | * File : UI_Base.cs 11 | * Desc : UI 자동화 12 | * Bind()를 사용하여 Enum으로 필요한 하위 객체를 찾아 딕셔너리로 저장하여 관리한다. 13 | * BindEvent()를 사용하여 EventHandler를 간단하게 등록하여 사용할 수 있다. 14 | * [ Rookiss의 MMORPG Game Part 3 참고. ] 15 | */ 16 | 17 | // 모든 UI의 부모 18 | public abstract class UI_Base : MonoBehaviour 19 | { 20 | // 컴포넌트 타입 별로 담기 21 | protected Dictionary _objects = new Dictionary(); 22 | 23 | protected bool _init = false; 24 | 25 | public virtual bool Init() 26 | { 27 | if (_init) 28 | return false; 29 | 30 | return _init = true; 31 | } 32 | 33 | private void Start() 34 | { 35 | Init(); 36 | } 37 | 38 | protected void Bind(Type type) where T : UnityEngine.Object 39 | { 40 | // C++과 다르게 C#은 enum안에 있는 내용을 읽을 수 있다! 41 | string[] names = Enum.GetNames(type); 42 | 43 | if (_objects.ContainsKey(typeof(T)) == true) 44 | return; 45 | 46 | // enum의 개수만큼 배열 생성 후 _objects에 추가 47 | UnityEngine.Object[] objects = new UnityEngine.Object[names.Length]; 48 | _objects.Add(typeof(T), objects); 49 | 50 | for(int i = 0; i < names.Length; i++) 51 | { 52 | if (typeof(T) == typeof(GameObject)) 53 | objects[i] = Util.FindChild(gameObject, names[i], true); 54 | else 55 | objects[i] = Util.FindChild(gameObject, names[i], true); 56 | 57 | if (objects[i].IsNull() == true) 58 | Debug.Log($"Failed to bind({names[i]})"); 59 | } 60 | } 61 | 62 | protected void BindObject(Type type) { Bind(type); } 63 | protected void BindImage(Type type) { Bind(type); } 64 | protected void BindText(Type type) { Bind(type); } 65 | protected void BindButton(Type type) { Bind