Cocos2d-x 로 만든 게임에서 캐릭터가 기기마다 다른 속도로 움직였다. 빠른 기기에서 빨리 가고 느린 기기에서 느리게 갔다.
Table of contents
Open Table of contents
프레임마다 움직였다
scheduleUpdate() 로 도는 update 가 한 번 그릴 때마다 일정 거리를 더하고 있었다.
void update() { x += 5; }
CCDirector 의 setAnimationInterval 로 초당 60 번을 목표로 뒀지만 그건 목표일 뿐이다. 느린 기기는 30 번밖에 못 그린다. 60 번 그리는 기기에서는 300 을 가고 30 번 그리는 기기에서는 150 을 간다. 코드는 같은데 결과가 두 배 차이다.
시간으로 바꿨다
update 가 받는 ccTime dt 를 곱하게 했다.
void update(float dt) { x += 300 * dt; }
초당 얼마로 정하니 그리는 횟수와 무관해졌다. 60번 그리는 기기는 조금씩 60번 가고 30번 그리는 기기는 그 두 배씩 30번 가서 같은 거리가 된다.
그림이 바뀌는 것도 같은 문제였다. 프레임마다 넘기던 것을 정해진 시간이 지나면 넘기게 바꿨다. Cocos2d-x 의 CCAnimation 은 이미 delay 를 초 단위로 받는다. 손으로 넘기던 자리를 CCAnimate 로 바꿨다.
남는 시간을 버리고 있었다
여기서 하나 걸렸다. 시간을 넘기고 나서 누적값을 0으로 만들면 남는 시간이 사라진다.
elapsed -= frameTime; // 0 으로 만들지 않고 뺀다
한 칸에 0.1초가 필요한데 0.15초가 쌓였을 때 0으로 만들면 0.05초가 버려진다. 매번 조금씩 버려지니 애니메이션이 아주 조금씩 느려진다. 빼면 남는 것이 다음 칸으로 넘어간다.
크게 밀렸을 때의 선택
또 하나 있었다. 기기가 한참 멈췄다 돌아오면 dt 가 크게 들어온다. 그러면 한 번에 훅 이동해서 벽을 뚫고 나갔다.
이상한 것은 개발 중에는 이게 안 났다는 점이다. CCDirector::calculateDeltaTime 을 열어 보니 이유가 있었다.
#ifdef DEBUG
if(m_fDeltaTime > 0.2f)
{
m_fDeltaTime = 1 / 60.0f;
}
#endif
상한이 DEBUG 안에만 있다. 개발 빌드는 0.2 초를 넘으면 잘라 주고 배포 빌드는 안 잘라 준다. 개발 중에 안 나는 것이 당연했다.
방법이 둘이었다. 지난 시간에 상한을 두면 가볍지만 게임이 잠깐 느려진 것처럼 되고, 큰 시간을 여러 번 나눠 계산하면 정확한 대신 밀린 만큼 더 계산해야 한다. 이미 느려서 밀린 것인데 더 계산하면 더 밀린다.
상한 쪽을 골랐다. 조금 느려지는 것과 벽을 뚫는 것 중에서 앞을 골랐다. 프레임워크가 개발 빌드에서 하던 것을 배포 빌드에서도 하게 한 셈이다.
앱이 뒤로 갔다 돌아오는 경우는 상한으로도 어색했다. 몇 분이 지난 것을 0.1 초로 보는 것도 이상하다. 이건 CCDirector 에 이미 있었다. setNextDeltaTimeZero(true) 를 주면 다음 한 번의 dt 가 0 이 된다. 돌아온 시점부터 다시 재는 것이다.
정리
- 프레임마다 움직이면 기기 속도에 따라 결과가 달라진다
- 코드는 같은데 결과가 두 배 차이가 난다
- 지난 시간을 곱해 초당 얼마로 정한다
- 애니메이션도 정해진 시간마다 넘긴다
- 남는 시간을 0으로 만들면 조금씩 느려지므로 빼야 한다
- 크게 밀리면 한 번에 훅 가서 벽을 뚫는다
- 상한을 두면 조금 느려지는 대신 안 뚫린다
- Cocos2d-x 의 그 상한은
DEBUG빌드에만 있다. 배포 빌드에는 없다 - 앱이 뒤로 갔다 오면
setNextDeltaTimeZero로 다시 잰다