0.8초 뒤에 깨어난 코루틴이 새 판의 손님을 넘겨 버린다
판정 직후 곧바로 다음 손님으로 넘기면 무엇을 잘못 눌렀는지 볼 시간이 없다. 그래서 0.8초 지연 + 진열대 잠금을 넣었는데, 이 대기가 화면 수명보다 오래 살아남는 것이 문제였다.
- 증상
- 결과 화면에서 다시하기를 0.8초 안에 누르면, 새 판이 시작되자마자 손님이 한 명 그냥 넘어간다.
- 원인
await는 화면이 바뀌어도 대기를 취소하지 않는다. 이전 판의 대기가 뒤늦게 깨어나 새 판의NextCustomer()를 호출한다.- 판단
CancellationTokenSource를 하나 더 두는 대신 정수 세대 토큰을 골랐다. 취소는 대기를 끊을 뿐 "이 대기가 어느 판의 것인가"를 알려주지 않는다. 토큰을 비교하면 깨어난 쪽이 스스로 자기가 구세대임을 알고 조용히 빠진다.
/// <summary>판정 대기가 끝났을 때 그 사이 새 판이 시작됐는지 구분하는 표식.</summary>
private int judgeToken;
public void Prepare()
{
clock.Reset(playTimeSeconds);
SetRunning(false);
// …
// 진행 중이던 판정 대기를 무효.
// 다시하기를 0.8초 안에 누르면 이전 판의 대기가 뒤늦게 깨어나 새 판의 손님을 넘겨 버린다.
judgeToken++;
SetJudging(false);
NextCustomer();
// …
}
/// <summary>
/// 바로 다음 손님으로 넘어가지 않고 딜레이를 줌.
///
/// 연타하면 실패 → 새 주문 → 또 실패가 0.1초 안에 줄줄이 일어나는 현상 방지.
/// 실패한 순간 주문이 바뀌면 무엇을 잘못 눌렀는지 확인 불가능하므로 플레이어가 알 수 있도록 약간의 딜레이 줌.
/// </summary>
private async void BeginJudgeDelay()
{
int token = ++judgeToken;
SetJudging(true);
try
{
await Awaitable.WaitForSecondsAsync(judgeDelaySeconds, destroyCancellationToken);
}
catch (OperationCanceledException)
{
return;
}
// 대기 중에 Prepare() 가 불렸다면(다시하기) 이 대기는 이미 다른 판이 되었으므로 대기를 종료.
if (token != judgeToken)
{
return;
}
NextCustomer();
SetJudging(false);
}