2014. 3. 29. 15:00

물리엔진이 적용된 주사위의 가장 큰 문제는 값이 뭐가 나왔는지 알 수 없다는 것입니다 ㅡ,.ㅡ;

주사위의 회전을 물리엔진이 담당하니 어떤 값이 위로 올라왔는지 확인할 수가 없죠.

 

 

1. 해결 방법

이 문제를 해결하려면 맨 위로 올라온 오브젝트를 찾아 값을 체크하는 방법뿐입니다.

이 '맨 위로 올라온 오브젝트'가 무언인가에 따라 다르죠.

 

몇 가지 방법이 있습니다.

(1) 매쉬를 각 면 단위로 분리한다. (3D경우)

(2) 특정 위치의 점이나 면의 위치값을 비교한다.

(3) 위치 확인용 모델을 추가한다.

 

여기서 가장 간단한 방법은 (3)번입니다.

주사위에 들어간 숫자가 너무 많다면 (1)번이 더 좋을 수도 있습니다.

 

이렇게 오브젝트를 구성하였으면 구성한 오브젝트의 위치를 비교하여 가장 위에 있는 오브젝트를 숫자로 변환하여 사용하면 됩니다.

 

 

2. 구현해 보기

전 유니티를 이용하여 구현하겠습니다.

방법은 (3)번 방법입니다.

 

 

2-1. 화면 구성

주사위로 쓸 모델을 가지고 와서 물리엔진을 적용합니다.

주사위가 좀 튕겨야 더 랜덤한 숫자가 나올 테니 탄성도 적용합니다.

(참고 : [Unity] 탄성 적용하기)

 

 

2-1-1. 숫자 확인용 오브젝트 추가

이제 주사위 오브젝트의 숫자에 해당하는 면에 오브젝트를 추가합니다.

 

이때 숫자 면에 오브젝트가 붙지 않아도 상관없습니다.

 

제가 사용할 주사위는 4면체로 삼각뿔 쪽에 적힌 숫자를 읽는 주사위입니다.

 

 

2-1-2. 확인용 오브젝트 가리기

이 확인용 오브젝트는 '충돌'이 적용되면 안 되기 때문에 충돌체(Collider)는 추가하지 않습니다.

눈에 보여도 안 되기 때문에 메쉬를 'None'이나 'Transparency 1'로 변경해 줍니다.

'Transparency 1'는 .png 파일입니다.

아래 파일을 받아서 넣어 주시면 됩니다.

Transparency.zip
다운로드

이 파일을 임포트 시킨 후 텍스쳐를 이 택스쳐를 선택하거나 'Shader'속성을 'Transparent/Cutout/Soft Edge Unlit'로 선택 합니다.

 

 

모든 확인용 오브젝트를 투명 처리되면 아래 처럼 보입니다.

 

2-2. 코드 작성하기

이 포스팅의 툴은 '유니티'이므로 유니티 스크립트를 만들겠습니다.

 

2-2-1. 코드 작성

제가 사용한 모델은 4면체지만 다른 다면체도 사용할 수 있도록 확인용 오브젝트를 유동적으로 받기 위해 확인용 오브젝트는 배열로 작성합니다.

///<summary>
/// 숫자 리스트.
///</summary>
public GameObject[] goNumList;

 

 

각각의 확인용 오브젝트의 위치를 검사하여 가장 높은 위치에 있는 오브젝트의 인덱스를 선택 값으로 취급합니다.

여기서는 설정된 오브젝트만큼 돌려서 세로 위치 값(y)을 체크합니다.

SelectNum = 0;
m_fTemp = 0;

for (int i = 0; i < m_ListCount; i++)
{
	m_goTemp = goNumList[i];
	if (m_goTemp.transform.position.y > m_fTemp)
	{
		m_fTemp = m_goTemp.transform.position.y;
		SelectNum = i;
	}
}

 

1번 줄 : 가장 높은 높이 값을 가지고 있는 숫자 값입니다.

 

2번 줄 : 가장 높은 높이 값을 저장해둘 변수입니다.

 

7번 줄 : 기존에 저장된 값보다 높은 위치를 가졌을 때 

높이 값과 숫자를 저장합니다.

 

 

'SelectNum'가 나온 주사위의 값입니다.

 

전체 코드입니다.

using UnityEngine;
using System.Collections;

public class Dice_Rotate : MonoBehaviour
{
	private TextMesh m_tmText;

	///<summary> 
	/// 숫자 리스트.
	///</summary> 

	public GameObject[] goNumList;

	private int m_ListCount;
	private GameObject m_goTemp;
	private float m_fTemp;

	public int SelectNum;
	
	
	// Use this for initialization
	void Start () 
	{
		m_ListCount = goNumList.Length;

		//화면에 표시할 3D Text.
		m_tmText = (TextMesh)GameObject.Find("NewText").GetComponent(typeof(TextMesh));

		//다이스가 굴러갑니다~,
		m_tmText.text = "다이스가 굴러갑니다~,";

		RandomRotate();
	}
	
	// Update is called once per frame
	void Update () 
	{
		//마우스가 내려갔는지?
		if (true == Input.GetMouseButtonDown(0))
		{
			//내려갔다.
			RandomRotate();
		}

		GetSelectNum();
		
	}

	private void RandomRotate()
	{
		//초기 위치 설정.
		transform.position = new Vector3(Random.Range(9,12), Random.Range(8, 9), Random.Range(-11, -6));

		//랜덤한 각도만큼 회전.
		transform.Rotate(new Vector3(Random.Range(1, 100), Random.Range(1, 100), Random.Range(1, 100)), Space.Self);
	}

	private void GetSelectNum()
	{
		SelectNum = 0;
		m_fTemp = 0;

		for (int i = 0; i < m_ListCount; i++)
		{
			m_goTemp = goNumList[i];
			if (m_goTemp.transform.position.y > m_fTemp)
			{
				m_fTemp = m_goTemp.transform.position.y;
				SelectNum = i;
			}
		}

		m_tmText.text = "선택 : " + (SelectNum + 1);
	}
}

 

이 코드에서는 값 확인을 위해 '3D Text'를 이용하여 출력하는 코드가 포함되어 있습니다.

만약 직접 이 코드를 사용할 경우 출력용 코드들은 지우셔야 합니다.

 

테스트를 위해서 오브젝트의 위치를 리셋하는 코드를 이 스크립트 안에 넣었습니다.

사용할 때는 'RandomRotate()'를 지우고 'GetSelectNum()'만 호출해서 사용하시면 됩니다.

 

 

2-2-2. 오브젝트 설정

'2-2-1.'에서 작성한 스크립트를 우리가 '2-1-2.'에서 만들어둔 오브젝트에 적용합니다.

그러면 오브젝트 속성에 스크립트를 보시면 'Go Num List'라는 항목이 생긴 것을 알 수 있습니다.

 

'Size'를 원하는 크기로 넣습니다.

(저는 4면체이므로 4를 넣었습니다.)

 

이제 1번에 해당하는 오브젝트를 'Element 0'에 연결합니다.

이렇게 모든 숫자에 해당하는 오브젝트를 순서대로 넣습니다.

 

 

3. 테스트

테스트해봅시다.

 

화면에 선택된 값이 나오는 것을 볼 수 있습니다.

 

 

마무리

프로젝트 전체 소스 : 테스트에 사용한 프로젝트 다운 받기(클릭)

 

최익필 님께서 추가해주신 방법이 추가된 프로젝트입니다.(5.0 이상)

테스트에 사용한 프로젝트 다운 받기(클릭)

( 참고 : Unity Community - Dice - Which face is up? )

 

3D 작업을 해본 적이 없어서 어떤 프로세스로 3D 작업을 하는지는 모르겠지만 아마도 주사위값을 가지고 오는 것 빼고는 디자이너가 할 듯 한데 말이죠.

그런 건 사치인 우리 같은 사람들은 그냥 작업하면 됩니다 ㅎㅎㅎㅎ