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'만 클릭 된다면 큐브가 동작 합니다.

 

 

마무리

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

참고 : 

데브피아 - 블루아사님

 

아마도 하나의 변수에 여러 개의 레이어를 지정할 수 있도록 비트 마스크 방식을 사용하는 것 같습니다.

가독성이 떨어지고 지정할 수 있는 최대 개수가 제한된다는 문제는 있지만.....관리 측면에서는 괜찮은 것 같네요.