페이지

2017년 3월 13일 월요일

[Unty3D]난수 발생기 상태 저장하기 / 불러오기

동기


최근 개발중인 프로젝트에서 확인되는 버그들 중에는 쉽게 재현하기 힘든 것들이 꽤 있다.
테스트중에 훅~ 하고 버그가 지나가는데 버그 발생 전, 어플리케이션 상태가 어땠는지 쉽게 확인이 힘들다.
그래서 어플리케이션 라이프사이클 동안 버그 발생 가능성이 높은 지점의 상태를 주기적으로 저장해서,
나중에 문제가 발생되면 해당 버그를 쉽게 재현할 수 있도록 하였다.
어플리케이션에 스냅샷을 남기고 다시 불러오는 기능을 추가한 것이다.

이를 위해서는 난수 발생기의 Seed값도 복원이 필요해서
Unity3D가 제공하는 Random클래스에 해당 기능이 있는지 확인을 해보게 되었다.

How to???


Unity5.3에서 5.4로 넘어갈 때 Random.seed가 사라지고,
대신해서 Random.state가 제공되고 있음을 확인 하였다.
https://docs.unity3d.com/ScriptReference/Random-state.html

단순히 시드값만을 복원하는 것이 아니고 난수 발생기 상태 자체를 저장하고 불러오면,
스냅샷을 저장하고 불러올 때, 보다 손쉽게 버그를 재현할 수 있을 것이다.
이를 위해서는 난수 발생기의 상태를 파일에 저장하고, 다시 불러올 수 있어야 한다.

하지만 유니티 공식 문서에 난수 상태를 파일에 저장하고 불러오는 방법에 대해서는 언급이 없다.
구글링을 좀 해보니 아래 유니티 커뮤니티 링크에서 단서가 될만한 내용을 얻을 수 있었다.
https://forum.unity3d.com/threads/saving-and-loading-random-state-unity-5-4.423109/

JsonUtility라고 이것도 Unity5.4부터 지원되기 시작한 기능인데,
특정 object의 public필드를 json형태로 추출하고, 이렇게 추출한 json을 가지고 데이터 복원도 가능해 보인다.
그래서 아래 소스 코드와 같이 JsonUtility와 MiniJson을 사용해서 난수 발생기 상태를 저장/복원해 보았다.

소스 코드


난수발생기 상태 json으로 저장하기
        // 현재 상태를 딕셔너리에 담기.
        Dictionary<string, object> snapShotInfo = SomeDictionaryFunction( true );

        // 난수 발생기 상태 딕셔너리에 담기.
        string randomJsonString = JsonUtility.ToJson( UnityEngine.Random.state );
        Dictionary<string, object> randomStateDictionary = 
            MiniJSON.Json.Deserialize( randomJsonString ) as Dictionary<string, object>;

        // 그 외에 필요한 정보 딕셔너리에 담기.
        snapShotInfo[ "random_state" ] = randomStateDictionary;

        // 딕셔너리 json으로 쓰기.
        string snapShotJson = MiniJSON.Json.Serialize( snapShotInfo );

        // 파일 쓰기 완료.
        System.IO.File.WriteAllText( finalFolderPath, snapShotJson );

json에 저장된 난수발생기 상태 불러오기
        string snapShotJson = System.IO.File.ReadAllText( snapShotPath );
        Dictionary<string, object> snapShotInfo = 
            MiniJSON.Json.Deserialize( snapShotJson ) as Dictionary<string, object>;

        // 난수 발생기 상태 복원.
        Dictionary<string, object> randomStateInfo = 
            snapShotInfo[ "random_state" ] as Dictionary<string, object>;
        string loadedRandomStateJson = MiniJSON.Json.Serialize( randomStateInfo );
        Random.state = JsonUtility.FromJson<Random.State>( loadedRandomStateJson );

결론


의도한 대로 난수 발생기 상태 복원은 잘 되는 것 같다.
하지만 아쉬운 점은 때로 난수 발생 횟수가 어플리케이션 분기 흐름에 따라 교묘히 바뀔때가 있어서
가끔씩 어플리케이션이 스냅샷을 뜬 이후의 움직임과 다르게 동작할 때가 있었다.
그래도 수차례 시도하면 만족할만한 확률로 스냅샷 뜬 이후의 움직임과 동일한 상태로 동작하는 어플리케이션을 확인할 수 있었다.

완벽한 스냅샷을 위해서는 난수 발생기 상태 저장 시점을 좀 더 고민해 봐야 할 것 같다.

댓글 1개: