프로그래밍/SGDK 메가드라이브 개발

메가드라이브 게임 만들기 #4 - 고정 소수점에 대해

windship 2019. 6. 5. 23:36

 "메가드라이브 게임을 만드는 방법"이란 주제로 블로그를 시작했지만, 이제서야 이 테마를 다루게 된 것을 반성하고 있습니다. 

  C에서 게임 프로그래밍을 거의 독학으로 공부하기 시작하여 아직 2년도 되지 않았습니다. 그것도 복고풍의 슈팅 게임밖에 공부하지 않았는데 도트 먹기 게임의 프로그래밍, 제작을 개인 용도의 메모를 목적으로 쓰기 시작했다지만, 뭔가 잘난체 하는 듯한 테마로 일반에 공개한다고는...

 최근 일주일간, 플레이어의 조작과 맵과의 충돌 판정의 프로그래밍에 대해서 독학으로 정리하고 있습니다만, 좀처럼 제대로 되지 않고 있습니다. 가끔씩 위 그림과 같이 벽에 파고들어가서 움직이지 못하게 되거나 합니다. 

 플레이어를 항상 길의 정 중앙에서만 움직이면, 의외로 간단한 처리만으로 문제는 해결되지만, 오리지널 팩맨은 코너링을 할 때는 최단거리로 움직인다든지, 60분의 1초에 1도트를 움직이는 것 같은 단순한 움직임이 아니라 몬스터를 포함하여 미묘한 속도로 움직입니다. 팩맨은 플레이어나 몬스터 이동이, 미묘한 속도로 조정되어 있는 게임이었습니다.

 

 캐릭터의 좌표는 직관적으로 알기 쉽게 캐릭터의 중심을 기준으로 하기로 했습니다. 또한 팩맨은 플레이어와 몬스터의 미묘한 이동 속도를 표현하기 위해 고정 소수점으로 계산하기로 했습니다. 그런데 고정 소수점을 제대로 이해하지 않고 사용했기 때문에 계산에 미묘한 차이가 발생했던 것입니다.

 그래서 이번에는 고정 소수점에 대해 정리해 보았습니다.

 

<고정 소수점에 대해>

 

 고정 소수란, 정수를 사용하여 소수를 표현한 것으로, 정수 연산밖에 없는 CPU에서도 빠르게 계산할 수 있습니다. 정수에서 고정 소수점 형식으로 변환하려면 시프트 연산을 사용합니다.

 

  SGDK에는 16비트와 32비트의 고정 소수점이 준비되어 있습니다만, 좌표의 계산은 16비트로 충분하다고 생각했습니다. 16비트라고 하는 것은, 16개의 비트의 ON/OFF로 숫자를 표현하는 것입니다. 위의 표와 같이 SGDK의 16비트 고정 소수점은 최상위 비트가 부호(양수인가 음수인가), 하위 6비트가 소수, 나머지 9비트가 정수 부분으로 구성되어 있습니다. 정수 부분의 자릿수는 2의 0승, 2의 1승 등 2의 거듭제곱 단위의 값을 가지며, 같은 방식으로 소수는 2의 -1승, 2의 -2승 등등 음수의 지수값을 가집니다.

 

 최하위 비트가 10진수로 0.015625 (= 2의 -6승)의 값을 갖는다는 것은 더 이상 작은 수치를 다룰 수 없다는 뜻입니다. 예를 들어 0.1이라고 하는 수치는 정확하게 처리할 수 없고, 0.09370을 근사치로 사용하게 됩니다. 반대로, 0.015625 (또는 1/64)로 나눠 떨어지는 숫자로만 계산하면 오차 없이 정확하게 계산할 수 있습니다.

 

 덧붙여서, 이 16비트 고정 소수점으로 취급할 수 있는 값의 범위는 

 

최대 '0111111111111111' = 10진수 511.984375,

최소 '1000000000000000' = 10진수 -512

 

가 됩니다. 메가드라이브의 화면 해상도는 가로 320, 세로 224 도트이므로 적절한 범위입니다. 단지 빠듯하기 때문에 계산시에는 오버플로우에 주의해야 합니다.

 

 그런데 2의 0승이 1이 된다는 것, 여러분은 알고 있었습니까? 위의 표를 엑셀로 만들고 나서야 이해할 수 있었습니다. 옛날에 배웠었을지도 모르지만요. 산수는 재미있네요.