본문 바로가기
2D 콘텐츠 제작/[언데드 서바이벌] 제작 일지

[언데드 서바이벌 05] HUD 구현 & 피격 액션 추가 & 능력 업그레이드 구현

by 잰쟁 2023. 9. 18.
728x90

 

 

 

1. HUD (Head Up Display) 구현

 

-Exp, Level, Kill, Time, Health

 

 

 

 

<구현 목표>

 

 

 

- GameManger 스크립트에 Player Info 정보 추가하기

 

 

 

- HUD 스크립트 새로 생성하여 아래와 같이 입력

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class HUD : MonoBehaviour
{
    public enum eInfoType 
    {
        Exp, Level, Kill, Time, Health
    }
    public eInfoType type;

    //UI 타입들
    private Text text;
    private Slider slider;

    void Awake()
    {
        this.text = this.GetComponent<Text>();
        this.slider = this.GetComponent<Slider>();
    }

 


 

1) Exp

Hierarchy - UI-Canvas 추가

아래와 같이 설정 맞추기

 

 

Slider 추가하고 아래와 같이  설정 하기

 

[Slider 주요 속성들]

- Interactable : 특정 슬라이더의 입력 수용 여부를 결정( false일 경우 비활성화)
- Transition(전환모드) : 각 상태의 슬라이더 모양과 상태간 전환 유형을 관리 
- Navigation : 키보드나 컨트롤러 입력을 이용 할 때 UI 엘리먼트의 선택 방식을 설정

 

다 체크 해제 해주기!

 

 

- HUD 스크립트 Udate()에 아래와 같이 작성

 

- GameManager 스크립트에 Exp 갱신하는 메서드 추가하기

 

- Monster가 죽을때 Kill 증가 및 Exp 갱신 되어야 하므로

Monster 스크립트에서 몬스터 죽을때 로직에 아래와 같이 추가 작성하기

 

 


 

02. Level

 

Text UI 추가 후 아래와 같이 설정

 

 

- HUD 스크립트에 아래와 같이 case 추가

 


 

 

03. Kill

 

UI Image와 text 추가 후 아래와 같이 text 설정

 

HUD 스크립트에 아래와 같이 case 추가

 

** 새로 알게된 것

string.Format에서 F0 => 소수점자리 지정 & String 형식으로 반환

 

따라서 

string.Format("{0:F0}", GameManager.instance.kill);

string.Format("{0}", GameManager.instance.kill.ToString());

둘 다 출력 값이 같음

 

 

 


 

 

04. Timer

 

UI Text 추가 후 아래와 같이 설정

 

HUD 스크립트에 아래와 같이 case 추가

 

 

**새로 알게 된 것

string.Format에서 D  => 표시할 자릿수 지정

(EX) D2 -> 2자리수 , D5 -> 5자리수

 

 

 


 

05. Health

 

Exp 슬라이더 그대로 가져와 아래와 같이 변경

 

 

HUD 스크립트에 아래와 같이 case 추가

 

 

 

구현 결과

 

 


 

 

2. 몬스터 피격 액션 추가

 

 

몬스터가 피격 당할 때 좀 더 타격감을 주기 위해 피격 액션 추가해주자

 

 

기존에 Update()로 설정했던 몬스터 이동 부분을 FixedUpdate()로 변경 후 변수로 추가하기

 

Monster 스크립트에 KnockBack 코루틴 함수 추가

 

**yield return new WaitForFixedUpdate

: FixedUpdate()가 끝나면 yield return new WaitForFixedUpdate 밑에 있는 구문 실행!

 

 

몬스터가 공격 받는 시점에 코루틴 실행

 

 

 

실행 결과

피격시 살짝 뒤로 밀리는 모습

 

 

 


 

 

03. 능력 업그레이드

 

 

**ScriptableObject 란?

: 클래스 인스턴스와는 별도로 대량의 데이터를 저장하는 데 사용할 수 있는 데이터 컨테이너.

: 첨부된 MonoBehaviour 스크립트에 변경되지 않는 데이터를 저장하는 프리팹이 있는 프로젝트의 경우 유용

=>  즉, 메모리에 데이터 사본을 하나만 저장

 

**기능

- 에디터 세션 동안 데이터 저장 및 보관

- 데이터를 프로젝트의 에셋으로 저장하여 런타임 시 사용

 

 

- ItemData 스크립트 생성하고 아래와 같이 작성하기

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "Item", menuName ="Scriptable Object/ItemData")]
public class ItemData : ScriptableObject
{
    public enum eItemType 
    { 
        Melee, Range, Glove, Shoe, Heal
    }

    [Header("# Main Info")]
    public eItemType 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;
}

 

Data폴더 생성 후 Scriptable Object -> ItemData 생성

 

 

- 생성한 Item Data(5개)들에 설정값 입력하기

 

item Button UI 갯수에 맞게 생성 후 Data에 해당하는 itemData 넣어주기

 

 

실행해 보면 입력해준 값들대로 변경되어 출력됨

 

 


 

- 무기 업그레이드 -

 

- Item 스크립트에 버튼 클릭 이벤트로 OnClick 메서드 추가, 아래와 같이 입력

//버튼 클릭 이벤트
public void OnClick()
    {
        switch (data.itemType) 
        {
            case ItemData.eItemType.Melee:
                break;
            case ItemData.eItemType.Range:
                break;
            case ItemData.eItemType.Glove:
                break;
            case ItemData.eItemType.Shoe:
                break;
            case ItemData.eItemType.Heal:
                break;

        }
        //level 증가
        level++;

        //...레벨 개수 넘기지 않게 로직 추가
        //Damages의 배열길이:5 를 넘어가면 Button의 interactable 비활성화(투명하게)
        if(level == data.damages.Length)
        {
            GetComponent<Button>().interactable = false;
        }
    }

 

Object에 각 item 스크립트를 넣어주고 function에 OnClick 입력

 

 

 

실행한 모습

레벨 개수 초과 클릭하면 interactable 비활성화 됨

 


 

-무기 업그레이드-

 

 

- 기존에 Player 자식으로 있던 무기 삭제하기

 

 

-Weapon 스크립트 수정

: Init()에 매개변수 ItemData data 입력후 아래와 같이 추가

 

Item 스크립트, OnClick()에 아래와 같이 추가

 

 

실행한 모습

 

 

 

- Item 스크립트에 OnClick()에 아래와 같이 추가 입력

//버튼 클릭 이벤트
    public void OnClick()
    {
        switch (data.itemType)
        {
            //..2개 같이도 가능
            case ItemData.eItemType.Melee:
            case ItemData.eItemType.Range:
                if(level == 0)
                {
                    GameObject newWeapon = new GameObject();
                    this.weapon = newWeapon.AddComponent<Weapon>();
                    //Weapon 초기화
                    this.weapon.Init(data);
                }
                else
                {
                    //...처음 이후의 레벨업은 데미지와 횟수를 계산
                    //새로운 무기이면 damage 올려줘야함
                    float nextDamage = data.baseDamage;
                    int nextCount = 0;

                    //damages가 백분률 값이므로 곱해준 다음 더해줌
                    nextDamage += data.baseDamage * data.damages[level];
                    nextCount += data.counts[level];

                    //Weapon 레벨업
                    weapon.LevelUp(nextDamage, nextCount);
                }
                break;

 

 

- Weapon 스크립트 Init()에 아래와 같이 추가 입력

               

Weapon 스크립트 LevelUp() 메서드

실행 결과

 

 


 

 

- 장비 업그레이드 -

 

 

[장비 기능]

장갑 : weapon의 speed 증가

장화 : player의 speed 증가

음료수: player의 health -> maxhealth 충전

 

 

 

Gear 스크립트 생성 후 아래와 같이 작성

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Gear : MonoBehaviour
{
    public ItemData.eItemType type;
    public float rate;

    public void Init(ItemData data)
    {
        //...Basic Set
        name = "Gear" + data.itemId;
        //부모 transform 설정
        this.transform.parent = GameManager.instance.player.transform;
        this.transform.localPosition = Vector3.zero;

        //...Property Set
        type = data.itemType;
        rate = data.damages[0];

        //...장비가 생성되면 Gear의 기능을 적용시킴
        this.ApplyGear();
    }

    //...레벨업
    public void LevelUp(float rate)
    {
        //rate 값 갱신
        this.rate = rate;

        //...장비가 새롭게 추가 되거나 레벨업 할 때 로직적용 함수 호출
        //플레이어의 weapon들에게 다시 적용
        this.ApplyGear();
    }


    void ApplyGear()
    {
        switch (type)
        {
            case ItemData.eItemType.Glove:
                this.RateUp();
                break;
            case ItemData.eItemType.Shoe:
                this.SpeedUp();
                break;
        }
    }

    //...삽의 회전속도 증가
    void RateUp()
    {
        Weapon[] weapons = transform.parent.GetComponentsInChildren<Weapon>();
        
        foreach(Weapon weapon in weapons)
        {
            switch (weapon.id)
            {
                //id =0 -> 삽 
                case 0:
                    weapon.speed = 180 + (180 * this.rate);
                    break;
                default:
                    weapon.speed = this.rate * (1f - this.rate);
                    break;
            }
        }
    }

    //...player의 이동속도 증가
    void SpeedUp()
    {
        float speed = 5f;
        GameManager.instance.player.speed = speed + speed * this.rate;
    }
}

 

 

- Item 스크립트의 Init() 부분의 비워놨던 case 구문에 아래와 같이 작성 추가

 

여기서 

Heal은 일회성 아이템 --> 맨 마지막에 써줬던 'level++' 구문을 weapon과 gear case 구문 내로 옮겨줌

 //weapon : 삽, 엽총
            case ItemData.eItemType.Melee:
            case ItemData.eItemType.Range:
                if(level == 0)
                {
                   //...중략
                }
                else
                {
                    //...중략
                }
                //level 증가
                level++;
                break;
               
            //gear : 장갑, 장화
            case ItemData.eItemType.Glove:
            case ItemData.eItemType.Shoe:
                if (level == 0)
                {
                    //...중략
                }
                else
                {
                    //...중략
                }
                //level 증가
                level++;
                break;
                
            case ItemData.eItemType.Heal:
                //heal : player 체력 -> 최대 체력으로
                //1회성 아이템 =>  level++ 필요없음!!
                GameManager.instance.health = GameManager.instance.maxHealth;
                break;

 

 

- 새로운 weapon을 얻어도 똑같이 levelup 적용된  gear 값을 적용해야함.

Weapon 스크립트의 Init(), LevelUp() 메서드의 마지막 구문에

'BroadcastMessage("ApplyGear")' 추가하기!

 

 

**BroadcastMessage 란?

: 특정 함수 호출을 모든 자식에게 방송하는 함수

 public void Init(ItemData data)
    {
        
       	//...중략

        //player가 가지고 있는 모든 장비에 ApplyGear 적용
        //...특정 함수 호출을 모든 자식에게 방송하는 함수
        this.player.BroadcastMessage("ApplyGear");
    }
    
  //레벨업 시 damage와 count 조절하기
  public void LevelUp(float damage, int count)
    {
        this.damage = damage;
        this.count += count;

        //레벨 = 0 , 근접무기일 경우
        if(id == 0)
        {
            //배치해아함
            this.Batch();
        }

        //...BroadcastMessage 초기화
        this.player.BroadcastMessage("ApplyGear");
    }

 

 

실행해보니 

BroadcastMessage에  no receiver 오류가 발생!!

 

 

=> BroadcastMessage에 SendMessageOptions.DontRequireReceiver 추가해주기

(receiver 반드시 필요 -> receiver 반드시 필요 X로 바뀜)

 

오류수정 후 실행 결과