Collision으로 오브젝트가 충돌하는 것도 있지만 레이저(광선)을 쏴서 오브젝트에 충돌/인식 하는 Raycast라는 것이 있다.
Raycast의 특징은 Game 화면에서는 보이지않고, Scene 화면에서만 볼 수 있다.
RayCast
Raycast (가장 가까운 1개 감지)
기본적인 Raycast는 가까운 1개의 오브젝트만 감지하고
물체에 닿으면 true를 반환한다.
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class Raycast : MonoBehaviour
{
public float lineSize = 16f;
void Update()
{
// Game화면에는 보이지않는다
// Scene화면에는 보인다
// 해당 위치부터, 특정 방향으로 길이만큼, yellow색상으로
// 씬에서 눈으로 확인
Debug.DrawRay(transform.position, transform.forward * lineSize, Color.yellow);
//물체에 닿으면 true를 반환
// 가까운 1개의 오브젝트만 감지한다.
RaycastHit hit;
if(Physics.Raycast(transform.position, transform.forward, out hit, lineSize))
{
// 닿은 물체의 이름을 출력
Debug.Log(hit.collider.gameObject.name);
}
}
}
여기서의 Raycast는 가까운 1개의 게임 오브젝트만 인식이 가능하다.
밑의 사진에는 빨간색과 파란색의 Enemy1과 Enemy2가 있지만 뒤에 있는 파란색 Enemy2는 레이저가 관통하지만 인식하지 못하는 이 콘솔 로그에 나온다.
RaycastAll을 사용하여 둘 다 인식 해보자.
RaycastAll
위에서 사용한 Raycast와 조금 다르게 레이저에 닿는 게임 오브젝트들의 정보를 배열에 저장하고
RaycastAll을 사용하여 hits에 저장하고 나타낼 수 있다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RaycastAll : MonoBehaviour
{
public float lineSize = 16f;
void Update()
{
// 눈에 보이는 선을 그린다 ( 다른 작용은 없음)
Debug.DrawRay(transform.position, transform.forward * lineSize, Color.yellow);
// 선에 닿는 모든 오브젝트 정보를 배열에 저장한다.
RaycastHit[] hits;
hits = Physics.RaycastAll(transform.position, transform.forward, lineSize);
for (int i = 0; i<hits.Length; i++)
{
RaycastHit hit = hits[i];
Debug.Log(hit.collider.gameObject.name);
}
}
}
아래의 사진으로 두 오브젝트가 동시에 레이저에 닿아 같이 인식되는 것을 볼 수 있다.
Raycast - 게임 오브젝트 인식하고 파티클 나타내기
게임 오브젝트를 따라 쳐다보는 Turret 오브젝트를 만들고 Raycast에 인식되었을 때 파티클이 생성되는 씬을 만들어 보자.
LookEnemy
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LookEnemy : MonoBehaviour
{
public Transform enemy;
Transform spPoint;
Transform viewer;
RaycastHit hit;
public float lineSize = 4f;
public Object particle;
void Start()
{
// 계층 구조로 자식 오브젝트의 transform을 찾고 싶을 때
spPoint = transform.Find("/Turret/Tower/SpawnPoint");
viewer = transform.Find("/Turret/Viewer");
}
void Update()
{
// 주시( 적의 방향으로 회전)
//this.transform.LookAt(enemy);
viewer.LookAt(enemy);
this.transform.rotation = viewer.rotation;
Vector3 newSpPoint = new Vector3(spPoint.position.x,
viewer.position.y,
spPoint.position.z);
Debug.DrawRay(newSpPoint, spPoint.forward * lineSize, Color.yellow);
//out hit에 정보를 담는다.
//if(Physics.Raycast(spPoint.position, spPoint.forward, out hit, lineSize))
if (Physics.Raycast(newSpPoint, spPoint.forward, out hit, lineSize))
{
if (hit.collider.tag.Equals("Enemy"))
{
//GameObject partObj = (GameObject)Instantiate(particle, hit.collider.transform);
GameObject partObj = (GameObject)Instantiate(particle, hit.point, Quaternion.Euler(0f, 0f, 0f));
Debug.Log(hit.collider.gameObject.name);
Destroy(partObj, 0.5f);
}
}
}
}
spPoint = transform.Find("/Turret/Tower/SpawnPoint");
viewer = transform.Find("/Turret/Viewer");
계층 구조로 되어있는 자식 오브젝트의 Transform을 찾을 수도 있다.
viewer의 회전정보를 Tower에 전달한다.SpawnPoint의 spPoint는 높이가 Enemy보다 높기 때문에 레이저가 빗나갈 때가 있다. 그래서 viewer.position.y로 높이를 맞추고 newSpPoint에 저장한다. 저장한 newSpPoint에서 레이저를 그린다.
만약 레이저가 닿았다면 hit에 정보를 담고나서, 그것이 Enemy 태그일 경우 파티클을 생성한다.
ScreenPointToRay
마우스로 클릭했을 때 그 좌표로 오브젝트 이동시키기 - Camera Raycast
TouchManager라는 빈 게임 오브젝트를 생성하여 박스를 클릭했을 때 이동하는 스크립트를 만들어준다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScreenPointMove : MonoBehaviour
{
public Transform box;
void Update()
{
// 마우스 왼쪽 버튼을 눌렀다 뗄 때
if (Input.GetButtonUp("Fire1"))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
//닿은 물체가 있다면
if (Physics.Raycast(ray, out hit))
{
// 클릭한 위치로 box를 위치시킨다.
Vector3 newPos = new Vector3(hit.point.x,
box.position.y,
hit.point.z);
box.position = newPos;
}
}
}
}
마우스 왼클릭을 눌렀다 뗄 때, mainCamera 태그를 가진 카메라가 시작 점이되어 클릭한 마우스좌표(Input.mousePosiotion)를 목표 좌표로하는 광선 정보 ray를 생성한다.
그 후, 닿은 물체가 있다면 클릭한 위치로 박스를 이동시킨다.
마우스로 클릭했을 때 게임 오브젝트 회전시켜보기 - Camera Raycast
3개의 박스에 묶어서 회전하는 스크립트를 넣어주었다.
RotateBox
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateBox : MonoBehaviour
{
public float accTime = 0f;
public float speed = 100f;
public float rotTime = 1f;
bool isRotate = false;
void Update()
{
if (accTime > rotTime)
{
isRotate = false;
}
if(isRotate)
{
accTime += Time.deltaTime;
this.transform.Rotate(Vector3.up * speed * Time.deltaTime);
}
}
// 외부에서 접근해서 호출되게 하려고 public
public void RotateByHit()
{
accTime = 0f;
isRotate = true;
}
}
누적된 회전시간(accTime)이 rotTime초가 지나면 회전을 멈추기 위해 isRotate를 false로 만든다.
아래 if(isRotate)구문은 동작하지 않게 하기위함이다.
외부에서 RotateByHit()가 호출됨으로써 isRotate가 true가 되면 회전시간을 누적하고 회전이 시작된다.
RotateByHit()를 public으로 접근지정자를 정함으로써 외부에서 접근이 가능하다.
TouchManager에 스크립트를 추가해보자.
ScreenPointTouch
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScreenPointTouch : MonoBehaviour
{
void Update()
{
if (Input.GetButtonUp("Fire1"))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray, out hit))
{
if (hit.transform.tag.Equals("Box"))
{
// 닿은 오브젝트의 컴포넌트중에 RotateBox 스크립트 컴포넌트 객체를 얻어라
RotateBox boxScript = hit.transform.GetComponent<RotateBox>();
// 스크립트 컴포넌트 객체를 얻었다면
if(boxScript != null)
{
boxScript.RotateByHit();
}
}
}
}
}
}
마우스 클릭 좌표의 광선 정보 ray를 생성하고 만약 레이저에 닿은 물체가 있는데, 그것이 Box라면 (Tag활용)
닿은 오브젝트의 컴포넌트에서 RotataeBox 스크립트 컴포넌트 객체를 boxScript에 저장하고
boxScript 컴포넌트 객체가 null이 아니면 (저장해서 얻었다면)
boxScript.RotateByHit(); 해당 객체에서 RotateByHit() 메서드를 호출한다.
RotateByHit()는 위에서 public으로 두었기 때문에 접근이 가능하다.
'개발 > Unity' 카테고리의 다른 글
[Unity] 유니티 메서드 접근, 데이터저장 [Method(public, private, static) , PlayerPrefs] (0) | 2023.03.04 |
---|---|
[Unity] 코루틴 [Coroutine] (0) | 2023.02.27 |
[Unity] 프리팹의 복제와 재사용, Instantiate [Prefab, Instantiate] (0) | 2023.02.24 |
[Unity] 간단한 카메라 기법 [1인칭, 3인칭, FollowCamera, 백미러, Multi Camera] (1) | 2023.02.24 |
[Unity] 오디오 사운드 [Audio Sound] (0) | 2023.02.23 |