오늘은 디버그를 쉽게 하기 위한 타임라인 시스템을 만들었어.
개발하다보면 오류가 뜨는데 어느 상황에서 오류가 뜬건지 명확하지 않거나 "오류는 안나는데" 원하는대로 작동 안하는 경우가 있더라고
이 경우 한 프레임만 가능한 스택 추적으로는 왜 이런 현상이 발생하는지 찾기도 어렵고 노가다가도 많이 필요해서 게임이 어떻게 흘러가고 있는지 한눈에 볼 수 있으면 좋겠다 싶어서 이런 시스템을 만들어봤어
먼저 만들고 있는 겜에 대해 보여줄게
옛날에도 잠깐 보여준적이 있는데, 기본적으로는 토끼가 밭을 수확하는 방치형 육성 게임이야
(1.3배속)
먼저 밭이 1차 재화인 당근을 생산하고, 일정 수준 이상 당근이 생산되면 토끼가 당근을 수확해. 수확을 마치면 토끼는 당근을 창고로 옮기고, 이렇게 쌓인 당근은 다른 시설을 통해 2차 재화인 클로버로 바꿀 수 있어.
지금은 플레이어가 게임을 켜둔 상태에서의 개발은 거의 완료되었고, 이제 플레이어가 게임을 켜두지 않은 상태의 자동 진행을 구현하고 있어.
이 방법으로 몇 가지 아이디어를 생각해 봤어
가장 먼저 생각한 방법은 클라이언트 서버를 둬서 24시간 서버를 돌리는 건데, 가장 (구현이) 쉽고 확실한 방법이지만 고작 방치형 게임 돌리겠다고 24시간 서버를 사서 돌리는건 너무 비싸.
다음으로는 플레이의 마지막 상태를 저장해두었다가 플레이어가 다시 접속할 때 시간을 빠르게 돌려 현재 시간과 같아질 때까지 기다리는 건데, 몇분 정도야 괜찮겠지만 몇 시간~일 단위로 시간을 돌려야 하면 너무 기다리는 시간이 길어져.
그래서 생각한게, 무작정 시간을 '빨리'돌리는게 아니라 시간을 '스킵'하면 어떨까?
어떤 이벤트가 발생할 시간을 미리 예측해둔 뒤, 이벤트가 없는 시간을 스킵하고 바로 해당 이벤트의 시간으로 진행하면 그 중간 시간을 기다릴 필요 없이 바로 결과를 얻을 수 있게 될거야.
예를 들어, 당근 5개를 수용할 수 있고, 1초에 1개의 당근을 생산할 수 있는 농장이, 1개의 당근을 가지고 시작한다면 다음 이벤트 시간은 (5-1)/4 즉 4
초가 되겠지?
코드를 작성하고 돌려보자
1개의 당근을 가지고 있고, 5개의 당근까지를 수용할 수 있으며, 초당 하나의 당근을 생산하는 농장을 만들고 1000초를 돌렸어.
이러면 1000초 내에 발생한 모든 이벤트들을 실행할 수 있게 되며, 1000초가 되기 전에 모든 이벤트가 완료되었으면 프로그램을 종료해.
마지막 줄에는 농장의 최종 당근 수와 소요 시간을 출력하게끔 했어.
프로그램을 돌려보면?
예상대로 최종 당근 5개, 소요 시간 4초가 나와.
이렇게 결과를 통해 간접적으로 프로그램이 잘 작동하는 걸 확인할 수 있지만, 구체적으로 어떤 일이 있어났는지는 로그를 다 찍어보지 않는 이상 알 수 없어.
로그를 다 찍으면 콘솔에 엄청난 양의 로그가 나올거고, 필요한 정보를 찾아내는 것도 노가다일거야.
나는 이 로그를 좀더 쉽게 보기 위해, 각 객체마다 타임라인을 만든 후, 시간 흐름에 따라 표로 기록하는 로그 시스템을 만들었어!
이 표는 프로그램이 종료될 때 엑셀 파일로 자동으로 출력되게 만들었어.
한번 볼래?
아쉽게도 유니티랑 엑셀이 호환이 안 돼서, c#으로 외부 프로그램을 작성하여 엑셀을 열었어.
아무튼 이렇게 1차원 콘솔보다 2차원 테이블로 보면 훨씬 직관적으로 무슨 일이 있어났는지 알 수 있어서 엄청 편하더라고
예를 들어, 이 게임의 밭들은 당근 수용량이 3 이상이라면 굳이 수용량을 전부 채우지 않았더라도 토끼가 수확할 수 있게끔 해 두었거든?
이 밭은 당근 1개로 시작했으니까 총 생성하는 이벤트는 '3개가 되기까지 걸리는 시간 2초', 그리고 '수용량을 전부 채울때까지 걸리는 시간 4초'로 총 2개가 돼.
그리고, 결과를 보면 각 이벤트들이 성공적으로 등록되어 작동한걸 볼 수 있어!
2초에는 Harvertability를 true로 하는 이벤트가, 4초에는 carrotCount가 5가 되는 이벤트가 등록되었으며, 각 이벤트에서 Farm의 구체적인 상태를 각각 확인할 수 있어서 게임의 전체적인 흐름을 보다 직관적으로 파악할 수 있게 되었어.
이제 토끼를 하나 추가해 볼까?
아까와 같은 농장에, 이번엔 최대 3개의 당근을 수용할 수 있으며 초당 1개의 당근을 수확하거나 창고에 놓을 수 있는 토끼를 하나 추가했어.
아, 그리고 당근 창고의 최대 수용량은 예시를 돕기 위해 2개로 했어.
결과를 볼까?
엄청 긴 결과가 나왔어 ㅋㅋ
여기서 다 보긴 힘드니까 몇가지 중요한 부분만 한번 보고 게임이 잘 작동했는지 확인해보자
먼저 첫번째 수확 상황이야. 농장이 3개의 Carrot를 가지는 순간 농장이 수확 가능 상태로 되고, 토끼가 수확을 시작해.
이어지는 상황인데, 토끼의 당근 수용량은 3이니 총 수확 시간은 3초가 걸려.
따라서 아까의 2초에서 3초가 지는 5초에 이벤트가 발동한 것을 볼 수 있어.
토끼는 수확을 종료하고, 농장은 Harvest 상태에서 Respective 상태로 전환되어 다시 당근 생산을 시작해.
한편 토끼는 당근 창고로 즉시 이동하여 Unload 상태로 전환된 후 당근 저장을 시작해.
원래는 이동 시간도 계산하는데, 이건 글 쓰고 구현할 예정이야 ㅎㅎ
아무튼 당근 창고의 수용량은 당근 2개이니, 이 당근을 다 내리는 데에는 2초가 걸리고, 이 이벤트는 현재 5초에 2초를 더한 7초가 돼.
이 7초 이벤트도 위 그림에서 볼 수 있는데, 이 이벤트를 마치면 토끼는 3개에서 방금 저장한 2개의 당근을 뺀 1개의 당근을 가지게 돼.
그리고 이 정보도 토끼의 파란색 표에서 바로 볼 수 있어.
이 토끼는 아까의 농장이 다시 Harvestable 상태가 될 때까지 Idle 상태로 대기하게 돼.
이건 표의 마지막 부분인데, 창고는 수용량인 당근 2개를 모두 채웠으며, 농장 역시 수용량 5개, 토끼도 역시 수용량 3개를 다 채워 더이상 할 수 있는 행동이 없어지자 이벤트 생성이 끝나 프로그램이 성공적으로 종료된 걸 볼 수 있어.
이 표를 통해서, 맨 앞에 적었던 "오류가 뜨긴 했는데 어느 상황에서 오류가 뜬건지 명확하지 않거나" "오류는 안나는데 원하는대로 작동 안하는" 경우를 바로 찾을 수 있었어! 예를 들어 이벤트가 무한히 생성된다거나, 수확을 했는데도 농장의 당근 수가 변하지 않는다거나 하는 상황들을 바로바로 캐치해낼 수 있어서 디버그가 엄청 쉬워지고 재밌어졌어. 예를 들어, 농장 3개에 토끼 2개를 두면
이렇게 엄청 긴 표가 출력되는데 다 제대로 작동하는게 써져있는거 보면 개꼴리더라 ㅋㅋㅋ
암튼 조만간 더 개발이 되면 찾아올게. 오늘도 즐거운 개발하길 바래!
이렇게 만들었으니 이제 버그만 와장창 나오면!
사실 버그 와장창 나와서 이거 언제고치나 하다 그냥 추적 시스템 만듦 ㅋㅋ 버그 고치는것보다 그냥 추적시스템 만드는게 더 빠르겠더라고
그래도 막상 만드니까 버그 싹다 잡아서 1년묵은 변비 해결한거같았다
멋진개발추인거에요. 저도 멋진거 만들어서 자랑하고 싶음 !
구글 스프레드 시트 apps script써서 아예 크래시 나면 스프레드 시트로 쏴버리게 만들어도 괜찮겠는데... 나중에 한번 R&D해봐야겠다
서버는 플팹쓰시나요? - dc App