cocos2d-1.0.1-x-0.11.0 으로 만드는 iOS·Android 클라이언트에서 아이템 능력치 계산을 맡았다. 반지는 공격력을 고정값으로 올리고 목걸이는 비율로 올린다.
Table of contents
Open Table of contents
두 규칙이 서로 다른 값을 냈다
둘을 같이 끼면 화면마다 숫자가 달랐다. InventoryScene 에서는 125 인데 BattleScene 에서는 124 로 나왔다.
// 인벤토리
atk = (base + ring.flat) * (1 + neck.rate);
// 전투
atk = base * (1 + neck.rate) + ring.flat;
같은 값을 InventoryScene과 BattleScene 두 곳에서 따로 계산하고 있었다. 순서가 달라 결과가 갈렸다.
원인이 갈린 조합
하나만 끼면 인벤토리와 전투 두 화면이 같았다. 반지만, 목걸이만 끼웠을 때는 차이가 없었다.
둘을 같이 끼운 경우에만 벌어졌다. 비율이 곱해지는 대상에 고정값이 들어가느냐 마느냐의 차이였다.
계산 순서를 따라가 봤다
두 곳의 C++ 코드를 나란히 놓고 값을 손으로 계산해 봤다. base 100 에 반지 20, 목걸이 5% 면 인벤토리는 126 이고 전투는 125 여야 한다.
그런데 화면은 125 와 124 였다. 둘 다 1 이 모자랐다.
짧은 코드로 찍어 보니 이유가 나왔다.
(100 + 20) * (1 + 0.05f) = 125.999992
(int)125.999992 = 125
0.05f 가 딱 떨어지지 않는다. (int) 는 반올림이 아니라 0 쪽으로 자른다. 그래서 126 이 125 가 됐다.
SVN 에 있는 기획서를 다시 읽어 보니 어느 쪽이 맞는지 적혀 있지 않았다. 고정값을 먼저 더한다는 말도, 나중에 더한다는 말도 없었다.
기획 쪽에 물어보니 고정값을 먼저 더하는 쪽이 의도였다. 적혀 있지 않아서 구현하는 사람마다 다르게 짠 것이다.
순서가 필요한 자리를 다 찾았다
같은 형태가 또 있는지 작업 사본에서 grep -rn "rate" 로 뒤졌다. 방어력과 이동속도에도 고정값과 비율이 함께 있었다.
C++ 쪽 방어력은 두 곳이 같은 순서였고 이동속도는 갈려 있었다. 세 군데 중 두 군데가 어긋나 있었던 셈이다.
계산을 StatCalc::attack() 하나로 모으고 두 CCScene 이 그것을 부르게 했다. 자르는 것도 (int) 대신 floorf(x + 0.5f) 로 바꿨다. 순서를 고칠 일이 생기면 한 곳만 고친다.
상한과 확인 자리
이동속도에는 상한이 있었는데 그 처리도 화면마다 달랐다. 한쪽은 곱하기 전에 자르고 한쪽은 자른 뒤에 곱했다.
상한도 같은 함수 안으로 넣었다. 자르는 시점을 마지막으로 통일했다.
Cocos2d-x 쪽 화면 코드에서는 그 함수만 부르고 계산은 하지 않게 했다.
값이 맞는지 확인하는 자리는 아직 없다. 지금은 화면을 열어 눈으로 보는 것 말고는 방법을 모르겠다.
정리
- 고정값과 비율이 함께 있으면 더하는 순서에 따라 결과가 갈린다
0.05f는 딱 떨어지지 않는다.(int)로 자르면 126 이 125 가 된다- 자를지 반올림할지도 정해서 한 곳에 둔다
- 하나만 적용할 때는 차이가 안 나서 늦게 발견된다
- 기획서에 순서가 적혀 있지 않아 구현마다 달랐다
- 손으로 계산해 두 화면의 값을 대조하면 갈린 지점이 보인다
- 같은 형태를 SVN에서 전부 찾으니 세 군데 중 두 군데가 어긋나 있었다
- 계산을 한 함수로 모아 두 화면이 그것을 부르게 했다
- 상한을 자르는 시점도 함께 통일했다
- 값이 맞는지 자동으로 확인하는 방법은 아직 모른다