안녕하세요.
이번 글에서는 스킬에 쿨다운을 다루어 보려고 합니다.
생각보다 작업 시간이 꽤 소모가 되어서 스킬 동작 구현은 아직 작업하지 못했습니다.😥
또한 아직 UI 작업을 할 단계는 아닌 것 같아서, 임시로 Canvas와 Image를 생성해 주어 작업을 했습니다.
먼저 canvas의 인스펙터창 설정입니다.
화면 크기의 변화에 따라 UI가 조정되게 Scale Mode > Scale With Screen Size로 해주었고
기준이 되는 해상도로 UI 작업한 해상도로 입력되게 Reference Resolution은 1920 x 1080으로 해주었습니다.
또한 가로 / 세로 비율에 맞게 줄이도록 Match는 0.5로 설정을 해주었습니다.
다음은 간단한 UI 구조를 만들었습니다.
Skill01 : 관리하기 위한 부모 오브젝트입니다.
ImageBackground : 스킬 이미지의 뒷 배경입니다.
ImageSkill : 스킬 이미지입니다.
Text (TMP) Cooldown : 쿨다운 표시를 나타낼 텍스트입니다.
(생성된 오브젝트 이름을 그대로 사용하고 뒤에 이름을 붙이는 것은 팀원과 작업 도중 각 오브젝트 이름을 쉽게 찾기 위한 방법으로 정한 것이니 큰 의미는 없습니다.)
다음은 코드입니다.
먼저 "SkillController"라는 이름의 클래스를 만들었습니다.
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections.Generic;
public class SkillController : MonoBehaviour
{
[Serializable]
public class Skill
{
public Image skillImage;
public TextMeshProUGUI skillText;
public KeyCode skillKeyCode;
public float cooldown;
[HideInInspector] public bool isCoolingDown = false;
[HideInInspector] public float currentCooldown = 0f;
public Action onUseSkill;
}
[SerializeField] private List<Skill> skills;
void Start()
{
foreach (var skill in skills)
{
if (skill.skillImage != null)
skill.skillImage.fillAmount = 0f;
if (skill.skillText != null)
skill.skillText.text = "";
}
if (skills.Count > 0)
skills[0].onUseSkill = Skill1Effect;
if (skills.Count > 1)
skills[1].onUseSkill = Skill2Effect;
}
void Update()
{
foreach (var skill in skills)
{
HandleSkillInput(skill);
HandleSkillCooldown(skill);
}
}
private void HandleSkillInput(Skill skill)
{
if (Input.GetKeyDown(skill.skillKeyCode) && !skill.isCoolingDown)
{
skill.isCoolingDown = true;
skill.currentCooldown = skill.cooldown;
skill.onUseSkill?.Invoke();
}
}
private void HandleSkillCooldown(Skill skill)
{
if (skill.isCoolingDown)
{
skill.currentCooldown -= Time.deltaTime;
if (skill.currentCooldown <= 0f)
{
skill.isCoolingDown = false;
skill.currentCooldown = 0f;
if (skill.skillImage != null)
skill.skillImage.fillAmount = 0f;
if (skill.skillText != null)
skill.skillText.text = "";
}
else
{
if (skill.skillImage != null)
skill.skillImage.fillAmount = skill.currentCooldown / skill.cooldown;
if (skill.skillText != null)
skill.skillText.text = Mathf.Ceil(skill.currentCooldown).ToString();
}
}
}
private void Skill1Effect()
{
Debug.Log("스킬 1 발동!");
}
private void Skill2Effect()
{
Debug.Log("스킬 2 발동!");
}
}
1.Skill이라는 클래스를 정의해 주었고 각 스킬마다 관리할 스킬 이미지, 쿨다운 텍스트, 실행할 키, 쿨다운을 선언해 주었으며,
[HideInInspector]를 인스펙터에 노출될 필요가 없는 쿨다운 플래그, 현재 쿨다운 시간에 붙여주었고
Action으로 OnUseSkill을 선언해 주어 함수를 담게 해 주었습니다.
마지막으로 [Serializable]을 선언해 주어 인스펙터에서 Skill 객체를 리스트 안에 편집할 수 있게 해 주었습니다.
[Serializable]
public class Skill
{
public Image skillImage;
public TextMeshProUGUI skillText;
public KeyCode skillKeyCode;
public float cooldown;
[HideInInspector] public bool isCoolingDown = false;
[HideInInspector] public float currentCooldown = 0f;
public Action onUseSkill;
}
2. Skill 클래스를 담을 리스트를 만들어 주었습니다.
[SerializeField] private List<Skill> skills;
3. 게임 시작 시 스킬 관련된 모든 요소를 초기화해 주었으며
각 스킬에 고유한 함수를 할당해 주었습니다.
void Start()
{
foreach (var skill in skills)
{
if (skill.skillImage != null)
skill.skillImage.fillAmount = 0f;
if (skill.skillText != null)
skill.skillText.text = "";
}
if (skills.Count > 0)
skills[0].onUseSkill = Skill1Effect;
if (skills.Count > 1)
skills[1].onUseSkill = Skill2Effect;
}
4. 매 프레임마다 모든 스킬에 대한 키 입력 처리와 쿨다운 처리를 반복해 주었습니다.
void Update()
{
foreach (var skill in skills)
{
HandleSkillInput(skill);
HandleSkillCooldown(skill);
}
}
5. 해당 키가 눌렸는지 체크와 쿨다운 중이 아니면 쿨다운 상태를 활성화하는 함수를 만들어 주었습니다.
private void HandleSkillInput(Skill skill)
{
if (Input.GetKeyDown(skill.skillKeyCode) && !skill.isCoolingDown)
{
skill.isCoolingDown = true;
skill.currentCooldown = skill.cooldown;
skill.onUseSkill?.Invoke();
}
}
6. isCoolingDown 함수로 쿨다운 여부를 판단해 이미지의 foillAmount에 남은 비율과 text에 남은 시간을 보여주는 함수를 만들었습니다.
private void HandleSkillInput(Skill skill)
{
if (Input.GetKeyDown(skill.skillKeyCode) && !skill.isCoolingDown)
{
skill.isCoolingDown = true;
skill.currentCooldown = skill.cooldown;
skill.onUseSkill?.Invoke();
}
}
7. 마지막으로 스킬 발동 시(Invoke > Action) 실행될 함수입니다.
private void Skill1Effect()
{
Debug.Log("스킬 1 발동!");
}
private void Skill2Effect()
{
Debug.Log("스킬 2 발동!");
}
이 SkillController 스크립트는 Player 오브젝트에 붙여서 사용할 계획입니다.
감사합니다.
키 코드는 Q와 W입니다.
+ 카메라 줌 인/아웃을 할 때 코드상 FOV 기준이 30으로 되어있고, 인스펙터 메인카메라는 FOV가 50으로 설정되어 있어서
맞추는 디버깅 작업을 해주었습니다.😁