본문 바로가기
게임 개발/유니티 게임 개발 일지

#14 순간이동 스킬 구현(마나 소비, 마나 자동 회복, 스킬재사용대기시간)

by FlowTree 2020. 7. 24.
반응형

1.구현할 기능 소개

1.1.순간이동

  • 캐릭터가 바라보는 방향으로 일정거리를 순간이동하는 기능이다.

1.2.스킬 소비 마나

  • 스킬 사용에 필요한 마나를 현재 마나에서 소비하는 기능이다.

1.3.마나 자동 회복

  • 플레이어의 현재 마나를 초당 n씩 회복하는 기능이다.

1.4.스킬재사용대기시간

  • 스킬 사용 후 일정 시간 동안 재사용을 방지하는 기능이다.

 

2.구현한 영상

https://www.youtube.com/watch?v=-ZHAZ88ZpS4

 

3.구현 의도

3.1.순간이동

  • 회피 기능
    -사방의 적들이 플레이어를 향해 몰려들 때 플레이어에게 위험 상황을 회피하는 기능 제공
    -다수의 적의 원거리 공격을 효율적으로 회피할 수 있는 기능 제공
  • 공격 거리 확보 기능
    -플레이어가 안전하게 일반 공격 및 원거리 공격을 할 수 있도록 일정 이상의 공격 거리 확보용 기능 제공

3.2.스킬 소비 마나

  • 스킬 소비 마나를 통해 스킬의 강약을 파악 요소 제공
  • 고효율의 스킬 남발 방지
  • 제한된 마나 자원의 효율적 사용 유도

3.3.마나 자동 회복

  • 스킬 사용 활성화 유도:원래는 무조건 맵 주변에 생성되는 마나 회복 아이템만으로 회복 또는 적의 마나 드랍을 통해 해결하려고 했으나 그렇게 되면 일반 공격을 제외한 스킬을 사용하기 어려워진다. 따라서 소량의 마나를 지속적으로 회복하여 이동하는 동안 일정량 마나가 회복되게 하여 일반 공격과 스킬 공격 활성화를 통해 조작하는 재미를 주고 싶었다.

3.4.스킬재사용대기시간

  • 고효율의 스킬 남발 방지 

3.5.순간이동 최대 횟수 2회

  • 순간이동 1회의 거리가 공격 거리 확보하기 적절한 거리기 때문에 매우 위급한 순간에 멀리 회피할 수 있도록 순간이동 횟수 2회 제공함

4.기능 명세

4.1.순간이동 기능 구조도

#내용이 간단하여 추가적인 설명은 아래 세부 기능 명세 참고

 

4.2.순간이동 플로우 차트

 

4.3.세부 기능

#규칙 작성 중에 void Start()에서 게임 첫 시작 시 순간이동 현재 횟수를 2개로 초기화하는데 하나의 기능으로 볼 수 있는가? 그렇다면 "스킬 초기 셋팅 기능"이라고 해야하나? 아니면 데이터 리스트에서 초기 셋팅에 대해 설명해야하는가?

데이터 리스트에서 순간이동 현재 횟수라는 데이터(파라미터)를 명시하고 설명하면 될 것 같은데 근데 초기 값에 대한 규칙을 설명해야... 어디서? 데이터 리스트에서 규칙까지 적는건 내용이 길어지고 설명이라는 목적을 벗어난다. 규칙...

게임 시작 시 순간이동 스킬의 현재 횟수는 2이다. 순간이동 횟수 규칙과 연관있는 기능에서 같이 설명하면 될듯 순간이동 횟수 충전 타이머?

 

 

4.3.1.스킬 입력 기능

4.3.1.1.정의

  • 스킬 버튼 입력 시 스킬 사용 조건을 검사하여 스킬을 실행하는 기능이다.

4.3.1.2.규칙

  • 입력 조건: 해당 스킬 버튼 터치 시
  • 스킬 사용 가능 조건: 순간이동 현재 횟수가 0 이상
  • 스킬 입력 시간 기록: 스킬 사용 조건을 충족 시 스킬 버튼 입력 시간을 저장한다.

 

4.3.2.순간이동 기능
4.3.2.1.정의

  • 캐릭터가 바라보는 방향으로 일정거리를 한번에 순간이동하는 기능이다.

4.3.2.2.규칙

  • 순간이동 현재 횟수 감소: 스킬 사용 시 순간이동 현재 횟수를 -1 감소한다.
  • 순간 이동 이펙트: 스킬 사용 시 푸른빛의 원형 이펙트들이 캐릭터 주변을 1.1초동안 빙빙 휘감으며 사라진다.
  • 캐릭터 이동: 스킬 사용 시 캐릭터의 현재 위치를 캐릭터가 바라보는 방향의 7m 앞 위치로 변경한다.
  • 순간이동 시 장애물 규칙
    - 순간이동 도착 지점이 장애물 안에 있으면 장애물을 통과하지 못한다.
    - 순간이동 도착 지점이 장애물 너머 밖에 있으면 장애물을 넘어간다.
  • 순간이동 현재 횟수 감소: 순간이동 시 순간이동 현재 횟수를 -1 감소한다.
  • 순간이동 횟수 검사: 감소한 횟수를 검사하여 순간이동 횟수UI 색을 갱신한다.

 

4.3.3.순간이동 횟수 충전 타이머

4.3.3.1.정의

  • 순간이동 시 필요한 이동 횟수를 일정 시간 후 충전하는 기능이다.

4.3.3.2.규칙

  • 순간이동 현재 횟수 초기값: 2회
  • 횟수 충전 조건: 순간이동 현재 횟수 < 2 일 때 충전 타이머 시작
  • 횟수 충전 중지 조건: 순간이동 현재 횟수가 2일 때
  • 충전 규칙
    -재사용대기시간 검사: 스킬 입력 시간 + 스킬 재사용대기시간 <  현재 시간 검사일 때 순간이동 현재 횟수를 +1 추가한다.
    -스킬 입력 시간을 현재 시간으로 갱신한다.
    -순간이동 횟수 검사하여 횟수UI를 갱신한다.
    -횟수 충전 조건을 충족하면 위의 단계를 반복한다.

 

4.3.4.순간이동 횟수 검사

4.3.4.1.정의

  • 순간이동의 현재 횟수를 검사하는 기능이다.
  • 현재 횟수에 맞게 순간이동 횟수UI의 색을 변경하여 보유 중인 횟수를 플레이어에게 시각적으로 보여준다.

4.3.4.2.규칙

  • 순간이동 횟수가 변경될 때(순간이동 사용 시, 순간이동 횟수 충전 시) 순간이동 현재 횟수를 검사한다.
  • 순간이동 현재 횟수의 개수에 따라 순간이동 횟수UI 색을 변경한다.
  • 순간이동 횟수UI
    -순간이동 횟수UI 개수는 순간이동 횟수와 같다.(2개)
    -순간이동 횟수UI는 순간이동 버튼 위에 2개를 배치하여 플레이어가 순간이동 횟수 파악하기 좋게한다.
  • 순간이동 횟수UI의 색
    -순간이동 횟수가 있음: 흰색
    -순간이동 횟수가 없음: 검은색
  • 순간이동 횟수UI 색 변경 규칙
    -순간이동 횟수가 감소되면 우측에서 좌측순서대로 횟수가 있는 횟수UI 색을 변경한다.
    -순간이동 횟수가 충전되면 좌측에서 우측 순서대로 횟수가 없는 횟수UI 색을 변경한다.

 

5.구현 과정

#이번 기능은 논리 위주이기 때문에 스크립트를 올렸다.

 

5.1.PlayerStats

  • 플레이어의 스텟, 스킬 마나 감소(데미지 입는 기능과 유사), 마나 자동 회복
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; //Hp,MpBarUI에 접근하여 Value값을 조절하기 위해서

public class PlayerStats : LivingEntity
{
    public Slider playerHpbar;
    public Slider playerMpbar;

    public float moveSpeed = 5f;

    /*
    public AudioClip deathClip; //사망소리
    public AudioClip hitClip; //피격 소리
    public AudioClip manaGetClip; //마나 습득 소리

    private AudioSource playerAudioPlayer; //플레이어 소리 재생기
    */

    protected override void OnEnable()
    {

        //LivingEntity의 OnEnable() 실행(상태초기화)
        base.OnEnable();
      
        //체력 슬라이더 활성화
        playerHpbar.gameObject.SetActive(true);
        //체력 슬라이더의 최댓값을 기본 체력값으로 변경
        playerHpbar.maxValue = startingHealth;
        //체력 슬라이더의 값을 현재 체력값으로 변경
        playerHpbar.value = health;

        //마나 슬라이더 활성화
        playerMpbar.gameObject.SetActive(true);
        //마나 슬라이더의 최댓값을 최대마나값으로 변경
        playerMpbar.maxValue = maxMana;
        //마나 슬라이더의 값을 현재 마나값으로 변경
        playerMpbar.value = mana;

    }

    //체력 회복
    public void RestoreHealth(float newHealth)
    {
        if(dead)
        {
            //이미 사망한 경우 체력을 회복할 수 없음
            return;
        }
        
        //체력 회복
        health += newHealth;
        //체력 회복했는데 최대 체력 이상 회복하면 수정하는거 추가해야함
    }

    //마나 회복
    public void RestoreMana(float newMana)
    {
        if (dead)
        {
            //이미 사망한 경우 마나를 회복할 수 없음
            return;
        }

        //마나 회복
        mana += newMana;

        //회복된 마나량을 Mpbar슬라이더에 업데이트
        playerMpbar.value = mana;
    }


    //데미지 처리
    public override void OnDamage(float damage)
    {

        base.OnDamage(damage);
        playerHpbar.value = health;
    }


    // Update is called once per frame
    void Update()
    {
        ManaRegen();
    }

    public void ManaRegen()
    {
        if (mana != maxMana)
        {
            //마나는 초당 0.5씩 회복
            mana += Time.deltaTime * 10f;

            //마나를 회복했는데 최대 마나보다 크다면 현재마나를 최대마나량으로 변경
            if(mana > maxMana)
            {
                mana = maxMana;
            }
            //회복된 마나량을 Mpbar슬라이더에 업데이트
            playerMpbar.value = mana;
        }

        else
        {
            Debug.Log("마나가 가득찼습니다"+mana);
            //현재 마나가 최대 마나이면 마나 회복X
            return;
        }
    }

    //스킬 사용 시 마나 소비
    public virtual void UseMana(float manaCost)
    {
        //소비 마나만큼 현재 마나 감소
        mana -= manaCost;

        //소비한 마나량을 Mpbar슬라이더에 업데이트
        playerMpbar.value = mana;
    }
}

 

5.2.Teleportation

  • 순간이동, 마나 소비, 재사용대기시간, 순간이동 횟수UI 색 변경
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; //Image 컴포넌트 이용하려고

public class Teleportation : MonoBehaviour
{
    //순간이동 이펙트 제어 변수
    public float spawnEffectTime = 2;
    public AnimationCurve fadeIn;

    ParticleSystem ps;
    float timer = 0;
    Renderer _renderer;

    int shaderProperty;

    private bool bEffect = false;

    //순간 이동 변수
    public float rechargeCooldown = 5;
    private float currentCooldown;
    private bool bSkill = true;

    private bool bTeleportation = false;
    public int teleportationCount = 2;

    //순간이동 횟수UI 
    public Image countImage1;
    public Image countImage2;

    // Start is called before the first frame update
    void Start()
    {
        //순간이동 이펙트
        shaderProperty = Shader.PropertyToID("_cutoff");
        _renderer = GetComponent<Renderer>();
        ps = GetComponentInChildren<ParticleSystem>();

        var main = ps.main;
        main.duration = spawnEffectTime;

        //순간이동 횟수UI 컴포넌트 가져오기
        countImage1 = GameObject.Find("Count1").GetComponent<Image>();
        countImage2 = GameObject.Find("Count2").GetComponent<Image>();

        //순간이동 횟수UI 초기화
        CountCheck();

    }

    // Update is called once per frame
    void Update()
    {
        CoolDown();
        Teleportataion();
        TeleportationEffect();
    }
    
    //버튼 입력
    public void InputTeleportation()
    {
        Debug.Log("텔레포트 버튼 입력");
        if (teleportationCount > 0)
        {
            Debug.Log("텔레포트 조건 검사");
            bTeleportation = true;
            currentCooldown = Time.time;
        }

    }

    //Teleportation
    void Teleportataion()
    {
        if(bTeleportation)
        {
            Debug.Log("텔레포트 실행");
            teleportationCount -= 1;
            bTeleportation = false;
            bEffect = true;
            //스킬 입력 후 횟수 변경되니까 횟수UI 체크
            CountCheck();

            //캐릭터의 현재 위치에서 캐릭터가 바라보는 방향으로 순간이동 거리만큼 이동한다.
            transform.Translate(Vector3.forward * 7f);
        }

        //이동하기전에 레이캐스트로 장애물이 있는지 체크한다.
        //장애물이 있으면 해당 방향벡터의 0.8배 이동한다.
        //이동 후 순간이동 횟수를 1회 줄인다.
        //순간이동 횟수가 없으면 실행x
        //순간이동 스킬 사용 시 순간이동 이펙트가 실행되며 캐릭터 모델링을 숨긴다 >> 안 숨겨도 될듯
        //버튼 입력하면 이펙트 실행하면서 순간이동 하면 딱 어울림
    }

    //재사용대기시간 체크를 통해 순간이동 횟수 추가
    void CoolDown()
    {
        //순간이동 횟수가 최대 횟수인 2 보다 작을 때, 재충전시간을 통해 횟수를 충전한다. 
        if (teleportationCount < 2)
        {
            //위에서 저장한 현재시간 + 스킬 쿨타임보다 현재 시간이 클 경우, 
            if (currentCooldown + rechargeCooldown <= Time.time)
            {
                teleportationCount += 1;
                currentCooldown = Time.time; //횟수를 충전했으니 시간 초기화

                //횟수 충전후 개수가 변경되니까 횟수UI 체크
                CountCheck();
            }
        }
    }

    void TeleportationEffect()
    {
        if (bEffect)
        {
            Debug.Log("이펙트 실행중");
            ps.Play();
            _renderer.material.SetFloat(shaderProperty, fadeIn.Evaluate(Mathf.InverseLerp(0, spawnEffectTime, timer)));
            bEffect = false;
        }
    }

    //순간이동 횟수UI 적용
    //Update에서 계속 실행되서 비효율적인 것 같다.
    //순간이동 횟수가 변경되는 구간에 메서드를 실행하게 하여 그때만 검사함
    void CountCheck()
    {
        if(teleportationCount == 2)
        {
            //순간이동 횟수UI 색상 변경
            countImage1.color = Color.white;
            countImage2.color = Color.white;
        }

        else if(teleportationCount == 1)
        {
            //순간이동 횟수UI 색상 변경
            countImage1.color = Color.white;
            countImage2.color = Color.black;
        }

        else if(teleportationCount == 0)
        {
            countImage1.color = Color.black;
            countImage2.color = Color.black;
        }

    }
}
반응형

댓글