폴리브릿지는 비결정론적(non-deterministic)이다
예전에 폴리브릿지를 하다가 운빨로 스테이지를 넘기고 뿌듯하게 리플레이를 돌려보는데 가끔 실패하는 경우를 발견해서 저장해놨었다가 이에 대해서 정리하려고 방치해뒀던 주제입니다.쓰다보니 좀 애매하고 범위를 정하기가 어려웠습니다. 물리엔진에서 발생할 수 있는 문제와 원인들에 대해서 대략적으로 알아봅니다.
상황
좌표는 폴리브릿지 4-7
이렇게 성공할 때도 있지만
이렇게 실패할 때도 있습니다.
저렇게 다리가 흔들거리게 만들어두면 어떻게 재생할 때마다 결과가 달라집니다.
이런 상황에 대한 버그리포트는 이미 있지만 좀 더 자세히 알아봅시다.
특히 리더보드에서 순위를 따지는데 저런 상황이 발생하면 엄밀히 말해서 점수가 의미 없는 상황이 되죠(스코어링 곤란해짐)
Bug? Non-deterministic behaviour in jump, jump again (2015.09.28)
https://steamcommunity.com/app/367450/discussions/0/517142892064744193/
Q : 점프를 계속 반복하면 실패할 때가 발생한다, 이거 버그 아니냐?
A : 버그 맞다, 부동소수점 문제일 수도 있지만 근본적인 원인은 런타임에 바디들을 만들어서 발생하는 문제다. Unity, Box2D 구현에선 동일
The physics engine is not deterministic. (2016.06.24)
https://steamcommunity.com/app/367450/discussions/0/351660338688467230/
Q : 나 리더보드에서 순위작 하는데 같은 조건에서도 점수가 계속 다르게 나온다.
A : Box2D는 결정론적 시뮬레이션을 제공한다. 하지만 Unity에 사용할 때 문제가 생겼다. 시뮬레이션(bodies, constraints) 을 만들고 파괴하는 과정에서 객체가 해싱 테이블에 추가되는 순서가 무작위이며 이에 따라서 다른 시뮬레이션 결과가 발생한다. 대신 온라인 갤러리(https://gallery.drycactus.com/)에선 같은 순서를 보장하므로 업로드해서 확인하면 된다.
결정론적, 비결정론적 알고리즘
물리엔진에서 결정론적, 비결정론적은 시뮬레이션 결과를 예측할 수 있냐, 재현성이 있느냐라고 이야기할 수 있습니다. 결정론적이라면 동일한 상황에선 언제나 같은 결과가 반복될 것이고, 비결정론적이면 위에 올린 폴리브릿지처럼 같은 상황 같은 조건에서도 다른 결과가 생길 수 있죠.
정보통신기술용어해설 : Deterministic, Nondeterministic 결정론적, 비결정론적
http://www.ktword.co.kr/m/abbr_view.php?m_temp1=4086
Deterministic Algorithm
https://en.wikipedia.org/wiki/Deterministic_algorithm
Nondeterministic Algorithm
https://en.wikipedia.org/wiki/Nondeterministic_algorithm
Stochastic Optimization
비결정론적은 확률적과 엄밀히 따지면 다르게 정의됩니다. 이는 맥락에 따라서 조금씩 다르게 사용되는 것에 주의합시다.
https://cstheory.stackexchange.com/questions/632/what-is-the-difference-between-non-determinism-and-randomness
물리엔진
2D와 3D에서 물리엔진
2D에서는 Box2D(https://github.com/erincatto/Box2D)가 대표적입니다. 폴리브릿지 뿐만 아니라 수많은 게임이 Box2D를 물리 시뮬레이션에 사용하고 있죠.
하지만 3D로 넘어가면 차원이 증가함에 따라서 훨씬 더 많은 연산을 필요로 합니다. 그리고 무엇에 중점을 두느냐에 따라, 어떤 환경에서 개발하냐에 따라서 사용하는 물리 엔진의 종류가 달라집니다. 대표적으로 로켓리그라는 게임은 UE4로 개발했지만 물리엔진은 Bullet Phyics를 섞어서 썼다고 합니다.
하지만 3D로 넘어가면 차원이 증가함에 따라서 훨씬 더 많은 연산을 필요로 합니다. 그리고 무엇에 중점을 두느냐에 따라, 어떤 환경에서 개발하냐에 따라서 사용하는 물리 엔진의 종류가 달라집니다. 대표적으로 로켓리그라는 게임은 UE4로 개발했지만 물리엔진은 Bullet Phyics를 섞어서 썼다고 합니다.
물리엔진 강체(Rigidbody)와 제약(Constraints)
체인을 만들어봤습니다. 우측이 강체로 엮은 사슬, 좌측은 제약으로 엮은 사슬입니다.
강체(Rigidbody)로 엮은 경우 충돌(Collision)을 바탕으로 각 강체들의 다음 위치를 결정합니다. 반면 제약(Constraints)로 엮은 경우 보이지 않지만 각각 설정된 제약조건에 따라서 다음 위치를 결정합니다. 위치를 결정한다는 의미는 한 스텝(step)에서 다음 스텝으로 넘어갈 때 각 강체에 적용될 힘(F=ma)을 어떻게 주냐로 따질 수 있습니다.
사실 처음 폴리브릿지에서 버그를 봤을 때 여기서 에러가 발생한거 아닌가 의심했었습니다. 다리가 출렁이는 움직임이 매번 달리지기 때문이죠. Bullet Physics와 UE4에선 최적화 조건에 따라서 제약조건을 처리하는 Solver에서 속도 높일려고 랜덤을 박아놓는 경우가 있었습니다. 그래서 폴리브릿지도 마찬가지 아닐까? 생각했지만 2D니까 굳이 이럴 필요가 없었내요.
하지만 강체와 제약조건을 등록하는 해싱 테이블에서 등록하는 순서가 바뀌면 처리 순서가 바뀌고 이게 결국 비결정론적인 물리 시뮬레이션을 만든다는 점에서 결국 랜덤신의 가호가 맞았습니다(?)
조금 더 설명된 자료는 아래 슬라이드, 왠만하면 책을 읽어보시길 바랍니다. 저도 아직 공부를 하는 중이라 어디까지, 어떻게 설명해야할지 모르곘습니다ㅠㅠ
[NDC12] 게임 물리 엔진의 내부 동작 원리 이해
정리하자면...
비결정론적인 이유?
물리엔진은 왜 비결정론적일까? 여러 사례를 살펴보면 주로 처리순서에 따른 문제와 부동소수점에 관한 문제가 있습니다. 특히 게임에 쓰이는 물리엔진의 경우 시뮬레이션 정확도보다 속도를 중요시하기 때문에 꼭 필요한 곳이 아니면 비결정론적으로 구현하는게 도움되는 경우가 많습니다.대표적으로 오버워치에서 주변 사물과 상호작용을 할 때 공은 모든 사람들이 동일하게 인식하죠, 루시우볼이 특히 이런 경우였습니다. 하지만 단순히 파괴되면 끝나는 물체들은 모든 사람들이 동일한 화면을 볼 필요가 없으니 보는 사람마다 조금씩 다르게 파괴되는 것을 볼 수 있습니다.












