본문 바로가기

Unity 공부/Unity_2D

[Day 9] 능력 업그레이드 구현

[ 능력 업그레이드 구현 ] --> 능력 업그레이드 UI를 만들고 로직 작성

  • C# 스크립트 생셩(ItemData)
    ㅡ아래는 완성된 코드ㅡ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "Item", menuName = "Scriptble Object/ItemData")] // 커스텀 메뉴 생성
public class ItemData : ScriptableObject
{
    public enum ItemType { Melee, Range, Glove, Shoe, Heal } // 타입 종류
    [Header("# Main Info")] // 헤더를 통해 이쁜이 작업
    public ItemType itemType; // 타입
    public int itemId; // 번호
    public string itemName; // 이름
    public string itemDesc; // 설명
    public Sprite itemIcon; // 아이콘

    [Header("# Level Data")]
    public float baseDamage; // 기본데미지
    public int baseCount; // 기본개수
    public float[] damages; // 레벨에 따른 데미지
    public int[] counts; // 레벨에 따른 개수

    [Header("# Weapon")]
    public GameObject projectile; // 투사체
}
  • Undead Survivor 폴더에 새로운 폴더 생성(Data)
        - Data 폴더에서 우클릭 --> Create --> Scriptble Object --> ItemData 생성
        - Inspector에서 아이템이 가진 속성들 설정 (Type, Id, Name, Icon, Base Damage, Base Count)
        - Damages, Counts = 5 설정 (레벨 개수)
        - Damages Element에는 레벨업 할 때마다, 데미지 증가율 넣어주기 ex) 0.5(50%) | 1 (100%) | ...
        - Counts Element에는 레벨업 할 때마다, 추가 할 개수 넣어주기 ex)1(1개 추가) | 1(1개 추가) | ...
        - Projectile에 Prefabs의 Bullet 0 넣어주기(드래그&드랍)
  • 작성한 Item 0를 Ctrl + D 하여 5가지 모두 설정해주기(Melee, Range, Glove, Shoe, Heal)
        - Bullet 1에서의 Counts는 관통을 의미
        - Glove와 Shoe에서 Counts는 모두 0으로, Projectile의 프리펩은 None으로 설정
        - Heal은 # Main Info를 제외한 모든 값을 0으로 설정
  • 캔버스 아래에 빈 오브젝트 추가
        - Width = 22, Height = 150으로 설정
  • 자식 오브젝트로 UI --> Legacy --> Button 추가 (Item 0)
        - Source Source를 Panel로 변경
        - 이름을 Text Level로 변경
        - 앵커 선택 후 Shift + Alt 입력 후 정 중앙 이미지 선택
        - Width = 0, Height = 0 선택
        - Horizontal Overflow, Vertical Overflow 모두 Overflow로 설정
        - Font를 Fonts 폴더에서 설정(드래그&드랍)
        - Font Size = 5로 설정
        - Text = Lv.00으로 설정
        - Pos Y = -9로 설정
  • Item 0 의 자식 오브젝트로 UI --> Image 추가
        - Source Image를 Sprite의 Select 0 로 변경
        - Set Native Size 버튼 클릭
  • C# 스크립트 생성(Item)
    ㅡ아래는 완성된 코드ㅡ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // Image를 입력받기 위해 import

public class Item : MonoBehaviour
{
    public ItemData data; // ItemData 변수
    public int level; // 레벨
    public Weapon weapon; // Weapon 변수
    public Gear gear; // Gear 변수

    Image icon; // Image 변수
    Text textLevel; // Text 변수

    private void Awake()
    {
        icon = GetComponentsInChildren<Image>()[1]; // 자식 오브젝트의 컴포넌트를 다룸
        icon.sprite = data.itemIcon; // 초기화

        Text[] texts = GetComponentsInChildren<Text>(); // 자식 오브젝트의 컴포넌트를 다룸
        textLevel = texts[0]; // 초기화
    }

    private void LateUpdate()
    {
        textLevel.text = "LV." + (level + 1); // 레벨을 화면에 표현
    }

    public void OnClick() // 버튼 클릭 시그널 함수
    {
        switch(data.itemType) // Type별로 다르게 작성
        {
            case ItemData.ItemType.Melee: // Weapon
            case ItemData.ItemType.Range: // Weapon
                if(level == 0){
                    GameObject newWeapon = new GameObject(); // 처음 눌렀을 때 게임 오브젝트 생성
                    weapon = newWeapon.AddComponent<Weapon>(); // 게임 오브젝트에 Weapon 컴포넌트 추가
                    weapon.Init(data); // Weapon data 초기화
                }
                else{
                    float nextDamage = data.baseDamage; // nextDamage 초기화
                    int nextCount = 0; // nextCount 초기화

                    nextDamage += data.baseDamage * data.damages[level]; // baseDamage + 레벨에 따라 증가된 damages
                    nextCount += data.counts[level]; // baseCount + 레벨에 따라 증가된 Counts

                    weapon.LevelUp(nextDamage, nextCount); // LevelUp 함수 호출
                }

                level++;
                break;
            case ItemData.ItemType.Glove: // Gear
            case ItemData.ItemType.Shoe: // Gear
                if (level == 0){
                    GameObject newGear = new GameObject(); //최초 레벨업시 게임 오브젝트 초기화
                    gear = newGear.AddComponent<Gear>(); // gear에 게임 오브젝트 생성
                    gear.Init(data); // gear 초기화
                }
                else{
                    float nextRate = data.damages[level]; // nextRate 변수
                    gear.LevelUp(nextRate); // LevelUp 함수 호출
                }

                level++;
                break;
            case ItemData.ItemType.Heal:
                GameManager.instance.hp = GameManager.instance.maxHp; // 현재 hp를 maxHp로 채움
                break;
        }

        if(level == data.damages.Length) // Damages의 레벨 개수와 같아짐(현재는 5개)
        {
            GetComponent<Button>().interactable = false; // 버튼 상호작용 비활성화
        }
    }
}
  • Item 0에 Add Component
        - Item(Script)
  • LevelUp 오브젝트에 Add Component
        - Vertical Layout Group
  • Item 0를 5개 복사 후 각각 Data 폴더의 Scriptble Object 연결
  • 5개의 Item 오브젝트 이름 변경(Item 0~4)
  • Item 0~4 오브젝트에 On Click()에 자신의 스크립트 넣어주기(드래그&드랍)
  • Item 0~4 On Click()의 Function을 Item --> OnClick으로 변경
  • Item 0~4의 Navigation = None 설정
  • Hierachy의 Player 자식오브젝트인 Weapon 0,1 삭제
  • 기존의 Weapon 스크립트에 코드 추가
    ㅡ아래는 완성된 코드ㅡ
using System.Collections;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
using UnityEngine;

public class Weapon : MonoBehaviour
{
    public int id;
    public int prefabId;
    public float damage;
    public int count;
    public float speed;

    float timer;
    Player player;

    void Awake(){
        player = GameManager.instance.player; // GameManager 활용으로 변경
    }

	// Start 함수 로직은 삭제

    void Update(){
        switch (id){
            case 0:
                transform.Rotate(Vector3.back * speed * Time.deltaTime);
                break;

            default:
                timer += Time.deltaTime;

                if(timer > speed){
                    timer = 0f;
                    Fire();
                }
                break;

        }

        // .. Test Code ..
        if (Input.GetButtonDown("Jump")){
            LevelUp(10, 1);
        }
    }

    public void LevelUp(float damage, int count)
    {
        this.damage += damage;
        this.count += count;

        if(id == 0){
            Batch();
        }

        player.BroadcastMessage("ApplyGear",SendMessageOptions.DontRequireReceiver); // ApplyGear 함수를 가진 모든 오브젝트에 실행 명령을 내림
    }

    public void Init(ItemData data) // 매개변수 추가
    {
        // Basic Set
        name = "Weapon " + data.itemId; //오브젝트 이름
        transform.parent = player.transform; // player의 자식 오브젝트로 생성
        transform.localPosition = Vector3.zero; // player의 안쪽에서 위치 초기화

        // Property Set
        id = data.itemId; // Scriptble Object Id 초기화
        damage = data.baseDamage; // Scriptble Object Damage 초기화
        count = data.baseCount; //Scriptble Object Count 초기화

        for(int index=0; index<GameManager.instance.pool.prefabs.Length; index++){
            if (data.projectile == GameManager.instance.pool.prefabs[index]){ // Prefab Id는 PoolManager의 변수에서 찾아서 초기화
                prefabId = index;
                break;
            }    
        }

        switch(id){
            case 0:
                speed = 150; // (+) --> 반시계방향
                Batch();
                break;

            default:
                speed = 0.3f;
                break;
        }

        player.BroadcastMessage("ApplyGear", SendMessageOptions.DontRequireReceiver); // ApplyGear 함수를 가진 모든 오브젝트에 실행 명령을 내림
    }

    void Batch(){
        for(int index=0; index < count; index++) {
            Transform bullet;
           
            if(index < transform.childCount){
                bullet = transform.GetChild(index);
            }
            else{
                bullet = GameManager.instance.pool.Get(prefabId).transform;
                bullet.parent = transform;

            }

            bullet.localPosition = Vector3.zero;
            bullet.localRotation = Quaternion.identity;

            Vector3 rotVec = Vector3.forward * 350 * index / count;
            bullet.Rotate(rotVec);
            bullet.Translate(bullet.up * 1.5f, Space.World);
            bullet.GetComponent<Bullet>().Init(damage, -1, Vector3.zero); // -1 is Infinity Per.

        }
    }

    void Fire()
    {
        if (!player.scanner.nearestTarget){
            return;
        }

        Vector3 targetPos = player.scanner.nearestTarget.position;
        Vector3 dir = targetPos - transform.position;
        dir = dir.normalized;

        Transform bullet = GameManager.instance.pool.Get(prefabId).transform;
        bullet.position = transform.position;
        bullet.rotation = Quaternion.FromToRotation(Vector3.up, dir);
        bullet.GetComponent<Bullet>().Init(damage, count, dir);

    }
}
  • C# 스크립트 생성(Gear)
    ㅡ아래는 완성된 코드ㅡ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Gear : MonoBehaviour
{
    public ItemData.ItemType type; // ItemType 변수
    public float rate; // 수치 변수

    public void Init(ItemData data) // 초기화 함수
    {
        // Basic set
        name = "Gear " + data.itemId; // 오브젝트 이름
        transform.parent = GameManager.instance.player.transform; // player의 자식 오브젝트로 설정
        transform.localPosition = Vector3.zero; // player의 안에서 위치 초기화

        // Property set
        type = data.itemType; // ItemType 초기화
        rate = data.damages[0]; // 수치 초기화
        ApplyGear(); // ApplyGear 함수 호출
    }

    public void LevelUp(float rate) // Weapon과 동일한 LevelUp 함수
    {
        this.rate = rate;
        ApplyGear(); // ApplyGear 함수 호출
    }

    void ApplyGear() // Gear 관리 함수
    {
        switch (type)
        {
            case ItemData.ItemType.Glove:
                RateUp();
                break;
            case ItemData.ItemType.Shoe:
                SpeedUp(); 
                break;

        }
    }

    private void RateUp() // 수치 증가 함수
    {
        Weapon[] weapons = transform.parent.GetComponentsInChildren<Weapon>(); // Weapon 가져옴

        foreach(Weapon weapon in weapons) // Weapon에 하나씩 접근
        {
            switch (weapon.id) // Weapon의 id별로 나누어 작성
            {
                case 0: // 근접
                    weapon.speed = 150 + (150 * rate);
                    break;
                default: // 원거리
                    weapon.speed = 0.5f * (1f - rate);
                    break;
            }
        }
    }

    void SpeedUp() // 이동속도 증가 함수
    {
        float speed = 3;
        GameManager.instance.player.speed = speed + speed * rate;
    }
}

 

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

 

★Slow and Steady★

'Unity 공부 > Unity_2D' 카테고리의 다른 글

[Day 11] 레벨업 시스템  (0) 2023.07.17
[Day 10] 무기 장착 표현  (0) 2023.07.16
[Day 8] HUD 제작  (0) 2023.07.14
[Day 7] 타격감 있는 몬스터 처치 구현  (0) 2023.07.13
[Day 6] 자동 원거리 공격 구현  (0) 2023.07.12