백준 문제 중에 구현 문제를 풀면서 얻었던 아이디어였다.
N^3으로 이루어진 큐브에서 N개의 연결된 큐브를 클릭하면 해당 큐브가 터지면서 점수를 얻는 그런 스타일의 게임을 생각했다.
큐브가 위에서부터 떨어지는 형식으로 생각했다. 애니팡을 생각하면 된다. 그렇지만 애니팡처럼 자리를 옮겨서 N개를 맞추거나 연속으로 터트리는 것이 아닌 N개인지 추리하면서 푸는 3D 지뢰 찾기 정도의 아이디어였다.
결국 생각의 변화로 다른 아이디어로 바꾸었지만 그 부분은 뒷부분에서 더 이야기하고 지금은 큐브가 위에서 떨어지는 이동을 구현하고자 한다.
유니티에서 오브젝트를 이동시킬 때 Rigidbody로 이동시키는 방법과 Transform으로 이동시키는 방법이 있다.
Rigidbody와 Transform의 차이는 물리적인 연산의 차이로 알고 있다.
그렇기에 물리 연산을 하는 Rigidbody가 더 느리지 않을까 싶었다.
출처 : https://docs.unity3d.com/ScriptReference/Rigidbody-position.html
이외로 Rigidbody가 더 빠르다.
Transform.position은 이동 후 다시 계산하기 때문에 느리다는 것이다.
개인적으로 좀 충격적인 내용이었다.
그렇기에 Rigidbody로 이동하기로 결심했다.
충돌 범위인 콜라이더와 물리 제어가 가능하게 해주는 Rigidbody, 그리고 그것을 조절해 줄 스크립트를 붙여준다.
GetComponent의 경우 비용이 비싸기에 매 순간 사용해 주기보단 Awake 또는 Start에서 저장해 주는 것이 좋다.
이동과 관련한 내용은 코루틴을 활용하여 작성해 줬다.
지속시간의 경우 목표지점과 현재 지점까지의 차이를 전체 높이로 나누어서 0~1초의 시간으로 변환했다.
반복문은 0초에서부터 시작하여 duration에 도달할 때까지 Time.deltaTime만큼 더해주며 반복한다.
Time.deltaTime은 마지막 프레임으로부터 현재 프레임까지의 시간 간격이다.
즉 반복문은 duration의 시간 동안만 이루어진다.
우리가 원하는 것은 텔레포트가 아니기에 목적지까지 가는 동안의 중간 지점을 구해줘야 한다.
목적지까지 도달하기 위해 필요한 두 지점 사이의 좌표를 구할 때 사용되는 Lerp는 첫 번째 값을 두 번째 값으로 변환해 주는데 세 번째 인자로 사용된 0~1 사이의 값에 따라 두 번째 값에 가깝게 변환해 준다.
변화를 부드럽게 해줄 수 있는 것이다.
Lerp는 Mathf말고도 Vector2, Vector3, Vector4, Quaternion, Color에도 존재한다.
이렇게 구한 좌표는 로컬 좌표였기에 월드 좌표로 변환해 준 뒤 사용하면 된다.
출처 : https://docs.unity3d.com/ScriptReference/Rigidbody.MovePosition.html
설명에도 쓰여있듯 순간 이동을 하려면 position을 사용하면 된다. 하지만 이동을 하려면 MovePosition을 사용해 주는 것이 좋다.
출처 : https://docs.unity3d.com/ScriptReference/WaitForFixedUpdate.html
yield return은 일정 시간 동안 기다리게 해준다.
반복문을 도는 동안 WaitForFixedUpdate만큼 기다린다.
이름에서 알 수 있듯이 FixedUpdate의 주기만큼 기다린다.
테스트를 위한 코드이다.
5x5x5만큼의 큐브가 눈에 펼쳐져야 할 것이다.
충분히 재밌는 결과긴 하지만 의도했던 결과는 아니다.
큐브들 간의 충돌에 의해서 일어난 현상인 것이다.
큐브 간의 충돌을 무시할 필요가 있다.
충돌을 무시하기 위해서는 인스펙터에서 레이어를 추가해 준다.
이후 Edit -> Project Settings -> Physics에서 Layer Collision Matrix를 수정해 준다.
충돌 상호작용을 해제할 레이어를 체크해제하면 된다.
지금 같은 경우는 큐브들 간의 상호작용을 없애기 위해서이므로 Cube를 체크해제한다.
원하는 대로 작동했다.
사실 큐브들이 서로 충돌하는 것은 Is Kinematic을 체크하는 방식으로도 해결이 가능하다.
출처 : https://docs.unity3d.com/kr/560/Manual/RigidbodiesOverview.html
isKinematic은 물리적 힘이나 충돌에 영향을 받지 않는다.
그렇기에 지금 상황에서 사용하면 큐브끼리의 충돌이 일어나지 않아서 원하는 결과를 얻어낼 수 있다.
하지만 결국에는 물리 엔진의 제어에서 벗어나는 것이기에 물리적인 힘, 충돌이 필요한 상황에선 다시 켜야 한다.
키고 끄는 과정에서 성능 오버헤드가 발생할 수 있기에 개발자가 판단하여 작성하는 것이 옳다고 생각한다.
원래는 N^3에 해당하는 큐브를 돌려가며 M개의 값을 찾는 퍼즐 게임을 고려했다. (찾아보니 Match3 게임이라고 불린다. 즉 3D Match3 게임인 것이다)
N과 M을 동일하게 할지는 생각해 보고 있던 요소였다.
이외에도 중간에 큐브를 보충시킬지, 아니면 N을 고정시킬지, M개 초과의 값도 허용해서 한 번에 터트리게 해줄지 등을 고민했다.
맨 처음 아이디어는 1개에서 시작하여서 스테이지를 진행하며 N을 늘릴지 M을 늘릴지 종류를 늘릴지를 선택하며 최대한 많은 스테이지를 깨는 것이긴 했다. (물론 특정 한 개만 늘리면 너무 쉬울 테니 골고루 올리게끔 하려 했다)
여러 생각을 거친 뒤에 깨닫게 된 치명적인 문제가 있었다.
4번의 문제와 1번의 문제가 가장 큰 원인이었다.
N이 계속 커지는 것이 이 게임의 근본적인 재미요소라고 생각했다.
그래서 이름도 제곱을 강조한 Square Square Square로 지었다. (큐브가 정사각형인 것도 의도해서 지었다)
N이 50^3이 되면 125000이다.
물론 여기까지 도달하는 사람이 극소수겠지만 매우 많은 오브젝트이다.
이 부분에 대해서는 시야에 보이는 겉면에만 큐브를 둔다, 마인크래프트처럼 메쉬를 병합시킨다 등이 있었지만 빠르게 만들 목적으로 만드는 게임이어서 부적합하다는 생각도 들었다.
마우스를 위에 둔 큐브의 경우 주변에 동일한 큐브를 발광시켜서 M개인지 확인시키는 것도 생각했었다. 이것 또한 처음 생각했을 땐 괜찮았는데 막상 지금 와서 생각해 보면 이러면 못 깨는 사람이 없을 거 같다는 생각도 들었고 없애자니 너무 운에 맡긴다는 생각도 들었다.
그래서 템포를 빠르게 하기 위해서 N을 제한시키고 큐브가 애니팡처럼 보충시키는 것도 생각해 봤는데 결국 큐브가 큐브를 가리는 것으로 인해 N과 M이 커지면 운에 의존할 수밖에 없고 N과 M이 작으면 너무 쉬운 게임이 된다는 것이 문제였다.
다른 아이디어가 떠올랐기에 해당 아이디어를 시도해 보고자 한다.
그렇다고 지금 만들던 프로젝트를 버리기에는 아쉬워서 더 생각해 보니 다른 방향으로 변경하면 빠르게 완성하면서 꽤 괜찮을 거 같다는 생각이 들었다.