벡터 내적 주사위 출목 판정 기반

5566 단어 UnityC#
PONOS Advent Calendar 2019의 19일째 보도.
어제는 @loveRice 선생님의 "[Unity] DOTween을 사용해 보도록 하겠습니다."입니다.

입문


오늘은 유니티에서 주사위를 만드는 이야기를 허락해 주세요.

위의 그림은 완성형이지만 물리 연산에서 굴러가는 주사위의 출목은 텍스트로 표시됩니다.
어떤 게임 기획에서 주사위를 꺼내달라는 요청이 있어서 해봤어요.

요구 사항


주사위의 필요조건은 다음과 같다.
① 3D 공간에 주사위가 나오고 싶다
② 물리 연산을 통해 진실하게 굴리고 싶다
③ 스크롤이 끝났을 때의 출발점을 판정하고 싶다
기술적으로 가능한지 물어보면 당연히 설치할 수 있습니다!바로 대답했지만...
유니티라면 ①과 ②가 다과였다면 ③의 등판 판정은 어떻게 됐을까...?
여기는 엔지니어의 기술을 보여주는 곳입니다!나는 좀 진지하게 고려해 보았다.

운과 우연


갑작스럽지만 약간 탈선했다.
주사위와 룰렛 등 운과 우연한 요소의 공연이 즐거워 유저들을 즐겁게 한다.
그러나 이 게임들은 거의 모두 뜨기 전에 결과를 결정한다
내가 알기로는 대부분'결과를 미리 확정한 토대에서 시각적 효과를 결과와 일치하게 처리한다'고 한다.
지금 실시간으로 확실해 보여도 사실은 그렇게 생각해요.
예를 들어 주사위 던지기 버튼을 3초 정도 누르면 나오는 게임이 확정되는 경우
 1.주사위 던지는 버튼이 눌렸어요.
 2.무작위 수에 따라 "5"선택(※ 이때 확인)
 3.주사위 애니메이션 재생 시작(재생 시작점은 5초 전 설정)
 4.5로 시작한 상태에서 주사위 애니메이션 중지
 5.유저 식별 결과 5
이렇게 말하면 가짜라고 할 수도 있지만 사용자의 당선권을 보호하고 확률을 적절하게 통제하기 위한 방법이다.
(적어도 자신이 전에 참여한 게임은 거짓말이 아니다.^^;)
이유는 각기 다르지만 대부분의 게임은 룰렛이 돌아가기 전에 결과를 확정한다.
따라서 이번 등판 판정은 주사위가 등장하는 게임에서도 특이하다고 할 수 있다.

몇 가지 방안


그렇다면 주사위의 출처를 실시간으로 어떻게 판정할 수 있을까?탭 페이지에서 항목 작성 또는 편집
간단히 생각해봐도 몇 가지 생각이 든다.
· 6개의 면에 각각 Collider(충돌 판정)를 배치하여 지면과 충돌하는 면의 반대편을 이음매로 합니다.
· 6면의 각 면의 중심 좌표를 비교하여 가장 높은 위치에 있는 면을 볼록점으로 한다.
· 입방체 모형의 각의 8개 정점 중 높이의 앞 4개 정점의 조합에서 볼록점을 구한다.
· 입방체 모형의 방향(회전 각도)에 따라 돌출점을 구한다.
등등
이번에는 입방체 모형의 방향에서 목적을 구하는 방법을 선택했다.

회전 각도에서 이음매를 구하다


대상의 회전 각도는 Unity의 Game Object인 경우transform입니다.루트에서 얻을 수 있습니다.
그러나 오라각으로 각도를 판정하는 것은 경험적으로 좋은 것이 없다(0도나 360도를 넘을 때의 처리, 만방향 자물쇠 등...)나는 조금도 주저하지 않고 벡터 내적으로 판정하기로 결정했다.
측정한 적은 없지만 계산 처리도 가벼울 거예요.
벡터 내적
벡터의 내적을 사용하면 다음과 같은 내용을 볼 수 있다.
두 벡터의 투영
· 두 벡터의 평행 여부 판단
두 벡터가 수직인지 아닌지 판단
두 벡터의 각도 차이
유니티는 이렇게 편리한 물건을 준비했다.

시나리오


처음에 고려된 방안은 주사위의 6면의 법선 벡터(면과 수직의 벡터) 중에서 세계 공간에 가장 가까운 상향 벡터(상향 벡터)를 찾는 것이다.
그러나 면과 뒷면의 법선 벡터는 기호만 다르기 때문에 중간에 6면의 법선 벡터가 아니더라도 3면의 양은 가능하다는 것을 발견했다.조금만 더 깊이 들어가면 법선이 필요 없어요.transform의 세 개의 회전 벡터 분량이면 충분해요.
정리 좀...
세계 공간의 상방향 벡터와 주사위의 회전 벡터 XYZ를 비교하여 상방향 벡터에 가장 가까운 벡터를 찾아 이 벡터가 정방향인지 음방향인지에 따라 점을 판단한다.

설치를 시도해 보았다


Unity 편집기에서 X는 빨간색, Y는 녹색, Z는 파란색입니다.
우선, 기준이 되는 세계 공간의 좌표는 다음과 같다.
Vector3.Dot()
회색 원점에서 위로 뻗은 녹색 선을 기준으로 위쪽 벡터(=Vector3.up)로 설정합니다.
한편, 주사위가 굴러간 후의 벡터는 이런 상태이다.

주사위의 붉은색과 파란색, 노란색 선 중 상기 세계 공간의 상향 방향과 일치하는 것은 파란색이다.파란색 선은 Z 방향 벡터(.foward)이고 양방향이므로 1로 판정할 수 있습니다.
스크립트는 다음과 같습니다.
(Unity c# MonoBehaviour)

    // 出目チェック
    int GetNumber (Transform diceTransform)
    {
        int result = 0;

        float innerProductX = Vector3.Dot (diceTransform.right, Vector3.up);
        float innerProductY = Vector3.Dot (diceTransform.up, Vector3.up);
        float innerProductZ = Vector3.Dot (diceTransform.forward, Vector3.up);

        if ((Mathf.Abs (innerProductX) > Mathf.Abs (innerProductY)) && (Mathf.Abs (innerProductX) > Mathf.Abs (innerProductZ))) {
            // X軸が一番近い
            if (innerProductX > 0f) {
                result = 4;
            } else {
                result = 3;
            }
        } else if ((Mathf.Abs (innerProductY) > Mathf.Abs (innerProductX)) && (Mathf.Abs (innerProductY) > Mathf.Abs (innerProductZ))) {
            // Y軸が一番近い
            if (innerProductY > 0f) {
                result = 5;
            } else {
                result = 2;
            }
        } else {
            // Z軸が一番近い
            if (innerProductZ > 0f) {
                result = 1;
            } else {
                result = 6;
            }
        }

        return result;
    }
다음은 벡터 내적을 구하는 부분, 주사위의 X방향 벡터(.right)와 세계 공간의 상방향 벡터(.up)의 내적, 즉 각도 차이입니다.
float innerProductX = Vector3.Dot (diceTransform.right, Vector3.up);
XYZ의 벡터 중 세계 공간의 상방향 벡터와 각도 차이가 가장 적은 벡터를 판정하고 정방향인지 아닌지를 분리한 답안을result로 되돌려줍니다.
실제 가동을 시도한 그런 판정 결과를 얻었습니다!

지원


주사위의 눈 배치는 세계 표준에 의해 결정된다고 한다.
나는 정반대의 눈을 더해 7이 되는 규칙이 유명하다고 생각한다. 그 외의 설정도 결정되었다.
"하루하루 육동오서이남삼북사"
1은 하늘의 방향, 6은 땅의 방향, 4는 북의 방향...그런 의미에서 그 배치는 세계 표준이다.
하지만 잘 알려지지 않았기 때문에 주사위 3D 모델이 규칙에 따라 만들어졌는지도 확인해야 한다.위의 샘플은 주사위 3D 모델의 Z 방향이 "1"이므로 스크립트도 이에 일치합니다.

끝내다


이번 판정 방법은 단지 하나의 수단에 불과하다.
더 스마트한 방법도 있겠지, 어떤 방법이 가장 적합한지는 조건에 따라 달라지겠지.
결과가 아니라 반복적으로 시도하는 과정을 보셨으면 좋겠습니다.
원래 그렇게 많은 사람이 없으니까 주사위를 만들어야죠.
내일은 선생님!

좋은 웹페이지 즐겨찾기