2014. 4. 7. 16:30
   , ,

오브젝트를 클릭했을 때 무언가 동작하기 위해서는 이전에 썼던 포스팅만으로 충분합니다.

참고 : [Unity] 스크립이 적용된 오브젝트에 클릭 구현하기

 

문제는 이것이 'Physics.Raycast'를 이용하는 것인데 이것은 충돌체를 날려서 제일 처음 부딪친 오브젝트를 반환한다는 것입니다.

 

 

1. 문제 확인

큐브를 이용하여 큐브를 만들어 봅시다.

바닥을 클릭하면 안에 있는 큐브가 다른 위치로 이동하는 샘플을 만들어 봅시다.

 

 

 

위와 같이 큐브를 만들고 안에 큐브를 넣었습니다.

 

다른 포스팅에서 만들었던 클릭 클래스를 바닥에 적용합니다.

(참고 : [Unity] 스크립이 적용된 오브젝트에 클릭 구현하기)

 

아래는 테스트용 스크립입니다.

 

using UnityEngine; 
using System.Collections; 

public class claMouseClick : MonoBehaviour 
{
	
	Camera _mainCam = null; 
	
	/// <summary>
	/// 마우스의 상태
	/// </summary>
	private bool _mouseState;
	
	/// <summary>
	/// 마우스가 다운된 오브젝트
	/// </summary>
	private GameObject target;
	/// <summary>
	/// 마우스 좌표
	/// </summary>
	private Vector3 MousePos;

	private GameObject m_goCube;

	void Start() 
	{
		_mainCam = Camera.main;

		m_goCube = GameObject.Find ("Cube");
	} 
	
	// Update is called once per frame 
	void Update () 
	{
		
		//마우스가 내려갔는지?
		if ( true == Input.GetMouseButtonDown(0)) 
		{
			//내려갔다.
			
			//타겟을 받아온다.
			target = GetClickedObject(); 
			
			//타겟이 나인가?
			if ( true == target.Equals(gameObject)) 
			{
				//있으면 마우스 정보를 바꾼다.
				_mouseState = true; 
			}
			
		}
		else if ( true == Input.GetMouseButtonUp(0)) 
		{
			//마우스가 올라 갔다.
			//마우스 정보를 바꾼다.
			_mouseState = false; 
		}

		//마우스가 눌렸나?
		//스케일을 조절하여 클릭됬음을 확인한다.
		if (true == _mouseState)
		{
			//눌렸다!
			m_goCube.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
		}
		else
		{
			m_goCube.transform.localScale = new Vector3(1f, 1f, 1f);
		}
		
	} 
	
	
	/// <summary>
	/// 마우스가 내려간 오브젝트를 가지고 옵니다.
	/// </summary>
	/// <returns>선택된 오브젝트</returns>
	private GameObject GetClickedObject() 
	{
		//충돌이 감지된 영역
		RaycastHit hit;
		//찾은 오브젝트
		GameObject target = null; 
		
		//마우스 포이트 근처 좌표를 만든다.
		Ray ray = _mainCam.ScreenPointToRay(Input.mousePosition); 
		
		//마우스 근처에 오브젝트가 있는지 확인
		if( true == (Physics.Raycast(ray.origin, ray.direction * 10, out hit))) 
		{
			//있다!
			
			//있으면 오브젝트를 저장한다.
			target = hit.collider.gameObject; 
		} 
		
		return target; 
	} 
}

 

 

이제 테스트 해봅시다.

 

 

 

 

 

'Click1'을 누르면 큐브가 반응이 없습니다.

'Click2'를 누르면 큐브가 반응합니다.

 

이것은 눈에는 보이지 않지만 'Click1'부분에는 'Table_Bottom'의 앞에 다른 오브젝트가 있기 때문입니다.

 

2. 해결하기

이 문제는 해결하려면 'Physics.Raycast'를 호출 할 때 'layerMask'를 맴버 변수로 넘겨 주어야 합니다.

(참고 : 유니티 스크립트 레퍼런스 - Physics.Raycast)

 

'layerMask'는 레이어의 인덱스기 때문에 미리 레이어를 지정해야 합니다.

 

2-1. 레이어 지정하기

오브젝트를 선택하고 'Inspector'에 보면 'Layer'를 눌러 'Add Layer...'를 선택하여 레이어를 추가합니다.

 

 

이렇게 만든 레이어는 'Table_Bottom'를 선택하여 만든 레이어로 지정해 줍니다.

 

 

2-2. 코드 수정

이제 'Physics.Raycast'를 찾아 아래와 같이 수정해줍니다.

 

//마우스 근처에 오브젝트가 있는지 확인
if (true == (Physics.Raycast(ray.origin
							, ray.direction * 10
							, out hit
							, Mathf.Infinity
							, (1 << 8))))
{
	//있다!

	//있으면 오브젝트를 저장한다.
	target = hit.collider.gameObject;
}

 

 

'layerMask'는 'int'인데 이게 그냥 'int'가 아니라 '비트 마스크'입니다.

(참고 : 유니티 스크립트 레퍼런스 - Layers)

레프트 연산자를 이용하여 여러 개의 레이어 번호를 넣어 주면 됩니다.

'layerMask'는 충돌 체크할 레이어의 번호를 레프트 연산으로 넣어 주시면 됩니다.

 

반대로 특정 레이어를 빼고 싶다면

'(-1) - (1 << 8)'

이렇게 하면 됩니다.

 

 

3. 수정한 코드 확인 하기

이제 완성된 코드를 확인해 봅시다.

이제 어디있던 'Table_Bottom'만 클릭된다면 큐브가 동작 합니다.

 

 

마무리

어떤 이유로 비트 마스크방식을 쓰는지 저는 잘 모르겠습니다.

이렇게 가독성이 떨어지는 코드를 싫어하는 저로서는 난감합니다 ㅎㅎㅎ

어찌됐건 원하는 동작을 하니 일단은 된것 같습니다 ㅎㅎㅎ

 

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

 

참고 자료

데브피아 - 블루아사님

 



 

 

  1. Cargold 2014.09.18 13:44  Address  Edit/Delete  Reply

    아하... 쉬프트 연산자는 무조건이네요 = = 뭐이리 불편하게 만들었지 ㅋㅋ

    • Favicon of https://blog.danggun.net BlogIcon 당근천국 2014.09.18 14:41 신고  Address  Edit/Delete

      이유는 모르겠는데 그냥 리스트를 사용하는게 더 좋을 텐데 말이죠.
      다른 스크립트 언어와 호환성이나 3d작업에는 이게 더 유리해서 그런걸 수도 있겠죠 ㅎㅎㅎㅎ
      곧 5버전이 나오는데 그대로일지 궁금 하네요.

댓글 작성

이름
패스워드
홈페이지
비밀글