코루틴이란?
일시 중지할 수 있는 함수
Unity에서 일을 한 번에 다 하지 않고, 잠깐 멈췄다가 나중에 다시 계속 실행할 수 있는 함수야.
즉, 시간을 기반으로 동작을 나눠 처리하고 싶을 때 사용하는 기능!
📦 일반 함수 vs 코루틴
일반 함수 코루틴
한 번에 다 실행됨 중간에 멈췄다가 다시 실행됨
void MyFunc() IEnumerator MyCoroutine()
시간 지연 불가능 yield return으로 지연 가능
StartCoroutine(MyCoroutine());
IEnumerator MyCoroutine() {
Debug.Log("Start");
yield return new WaitForSeconds(2f); // 2초 멈춤
Debug.Log("After 2 seconds");
}
new WaitForSeconds(x) x초 동안 멈춤
null 다음 프레임까지 대기
new WaitUntil(() => 조건) 조건이 참일 때까지 대기
new WaitForEndOfFrame() 렌더링 끝날 때까지 대기
🎮 언제 쓰냐?
애니메이션 끝날 때까지 기다릴 때
공격 후 후딜 주기
몬스터 스폰 주기 만들기
페이드 인/아웃 같은 시간 기반 연출
상태 전환 타이밍 맞추기
⛔ 주의할 점
StartCoroutine()으로 실행해야 함
리턴값은 못 받음 (코루틴은 값을 반환 안 해)
중간에 StopCoroutine()으로 끊을 수 있음
//Attack
if(Input.GetMouseButtonDown(0)) {
//공격 속도 적용
StartCoroutine(PlayAttackAnimation());
}
IEnumerator PlayAttackAnimation()
{
m_animator.speed = m_stats.attackSpeedMultiplier;
m_animator.SetTrigger("Attack");
//애니메이션 기다림
AnimatorClipInfo[] clipInfos = m_animator.GetCurrentAnimatorClipInfo(0);
//만약 재생중인 애니메이션이 있다면 그 길이를 가져옴. 없다면 0.5초 반환
float clipLength = clipInfos.Length > 0 ? clipInfos[0].clip.length : 0.5f; // fallback 0.5초
yield return new WaitForSeconds(clipLength / m_stats.attackSpeedMultiplier);
m_animator.speed = 1.0f;
}
공격속도 증가를 주고 싶은데, 이를 그냥 함수로 적용하면 애니메이션 실행 전에 바로 원래 공격속도로 돌아오므로 공격속도를 애니메이션 끝나고 돌아오게 함수 적용
Hero의 히트 센서와 웨폰 콜라이더가 겹쳐서 항상 자기 자신이 맞던 문제를 레이어를 나눠서 프로젝트 세팅에서 아예 충돌을 막아버림.
공격을 맞았을 때 센서로 OnTriggerEnter2D로 체크하고 이를 몬스터에서 처리하는 방법이었는데
Monster
if (m_hitSensor.State())
{
Debug.Log("맞음");
m_animator.SetTrigger("Hurt");
m_hitSensor.Disable(m_invincibleTimer);
}
Sensor
public class Sensor_Monster : MonoBehaviour {
private int m_ColCount = 0;
private float m_DisableTimer;
private void OnEnable()
{
m_ColCount = 0;
}
public bool State()
{
if (m_DisableTimer > 0)
return false;
return m_ColCount > 0;
}
void OnTriggerEnter2D(Collider2D other)
{
Debug.Log("몬스터 충돌 감지: " + other.name);
m_ColCount++;
Debug.Log("enter 충돌 체크: " + m_ColCount);
}
void OnTriggerExit2D(Collider2D other)
{
m_ColCount--;
Debug.Log("exit 충돌 체크: " + m_ColCount);
}
void Update()
{
m_DisableTimer -= Time.deltaTime;
}
public void Disable(float duration)
{
m_DisableTimer = duration;
}
}
공격이 너무 빠르면 Update가 실행되기 전에 ColCount값이 0이 되서 결국 맞기도 전에 판정이 끝남. 이를 해결하기 위해 Sensor로 hurt에 관련된 값을 옮김.
항상 Update()가 즉시 실행되는게 아니란 걸 생각하자
가드를 작동하는 도중에 다른 애니메이션이 1프레임만 실행됨. 아마도 이것 역시 입력받는 타이밍 문제인 것 같음.
그래서 애니메이션 조건문에 guard가 false일 때로 했음. 근데 다른 공격은 트리거 형식으로 해놨는데 이걸 선입력 받아서 이미 가드상태에서 다음 공격을 받고 가드가 끝나면 무조건 실행함
//Guard
bool isGuarding = Input.GetMouseButton(1);
m_animator.SetBool("Guard", isGuarding);
if (!m_animator.GetBool("Guard"))
{
//Attack
if(Input.GetMouseButtonDown(0)) {
//공격 속도 적용
StartCoroutine(PlayAttackAnimation());
}
//Parrying
else if(Input.GetKeyDown("f"))
m_animator.SetTrigger("Parrying");
}
그냥 막아버림