Unity Chapter 7-1. C# 프로그래밍 [중급] (2/2) : 레이캐스트
카테고리: Unity Lesson 1
태그: C Sharp Unity Game Engine
인프런에 있는 이제민님의 레트로의 유니티 C# 게임 프로그래밍 에센스 강의를 듣고 정리한 필기입니다. 😀
🌜 [레트로의 유니티 C# 게임 프로그래밍 에센스] 강의 들으러 가기!
Chapter 7. C# 프로그래밍 : 중급 (2/2)
🔔 레이캐스트
레이캐스트란?
레이캐스트
: 오브젝트의 정중앙에서 눈에 보이지 않는 광선을 날려서 특정 거리 이내에 오브젝트가 있는지 없는지를 충돌 감지해주는 과정.
📜RayInteracion.h
1인칭으로 플레이어가 눈 앞에 응시하고 있는 오브젝트를 다른 곳으로 옮기는 코드를 작성해 보자.
- 카메라 오브젝트의 정중앙에서 광선을 쏜다.
- 마우스 좌클릭 하는 동안
레이캐스트
를 통해 광선에 충돌 감지 되는 Collider가 있는지 검사해야한다. - 마우스 좌클릭 하는 동안 광선에 감지 된 오브젝트를 카메라의 시야를 따라가게끔 이동시킨다.
- Hierarchy
- 바닥이 될
Plane
오브젝트 - 1인칭 카메라
FPSController
오브젝트- FPS 1인칭 카메라가 될 프리팹
FPSController
을 강의에서 Import 받았고 오브젝화시킨 후 여기에 📜RayInteracion.h을 붙인다.- 이 프리팹 안에 자식 오브젝트로 카메라가 붙어있는 상태.
- FPS 1인칭 카메라가 될 프리팹
Cube
오브젝트FPSController
가 레이캐스트로 감지할 대상.- 대충 카메라 앞에 위치시켜주자.
- 바닥이 될
Camera.ViewportToWorldPoint, Physics.Raycast
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RayInteration : MonoBehaviour
{
private Camera playerCam;
public float distance = 100f;
void Start()
{
playerCam = Camera.main;
}
void Update()
{
Vector3 rayOrigin = playerCam.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0.0f));
Vector3 rayDir = playerCam.transform.forward;
if (Input.GetMouseButtonDown(0))
{
if (Physics.Raycast(rayOrigin, rayDir, distance))
{
Debug.Log("뭔가 광선에 걸렸다!");
}
}
}
}
- private Camera
playerCam
;- 카메라 오브젝트
- 광선을 쏘는 곳이 카메라니 카메라의 위치를 알아야하기 때문에 사용
- public float
distance
= 100f;- 광선의 길이를 100으로 설정
- 100짜리 광선에 닿으면 감지하게 할 것
- 광선의 길이를 100으로 설정
- void Start()
- playerCam = Camera.main;
- “MainCamera” 태그가 붙은 현재 활성화 되어 있는 카메라.
playerCam
은 현재 활성화 되어있는 메인인 카메라로 초기화 됐다.
- playerCam = Camera.main;
- void Update()
- Vector3
rayOrigin
= playerCam.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0.0f));- 광선 발사 위치
- 카메라 뷰포트의 정중앙에서부터 광선이 발사되게 할 것이다.
- Camera.ViewportToWorldPoint(Vector3)
- 카메라 오브젝트에 내장되어 있는 함수로서 카메라가 찍고 있는 화면(뷰포트)상의 위치를 Vector3로 넘기면 그 위치를 실제 게임 월드의 위치로 리턴해준다.
- new Vector3(0.5f, 0.5f, 0.0f)
- 카메라가 찍고 있는 화면은 (x, y) 2D 좌표를 사용하며 화면의 좌측 하단은 (0,0)이며 화면의 우측 상단은 (1,1)이다.
- z 값은 카메라 오브젝트로부터 실제 카메라가 찍고 있는 화면까지의 실제 게임 월드 거리, 즉 카메라의 깊이를 의미한다.
- (0.5f, 0.5f, 0.0f)를 넘겨준다는 것은 카메라 화면의 정중앙 + 화면과 카메라 오브젝트는 위치를 일치하게 라는 듯이다.
- playerCam.transform.position을 사용해도 된다.
- 카메라가 정중앙으로 찍고 있는 화면상 위치의 실제 게임 월드 위치에서 광선을 발사할 것이다.
- 광선 발사 위치
- Vector3
rayDir
= playerCam.transform.forward;- 발사 방향
- 즉, 카메라의 앞쪽 방향 벡터
- if (Input.GetMouseButtonDown(0))
- 좌클릭을 누르는 순간 레이캐스트, 카메라 중심에서 눈에 보이지 않는 광선을 쏴서 광선에 닿는 Collider가 있는지 감지할 것이다.
- if (Physics.Raycast(rayOrigin, rayDir, distance))
- 광선을 쏘고 이에 감지되는 오브젝트가 있다면 True 리턴
- Physics.Raycast(rayOrigin, rayDir, distance)
- Raycast는 물리에 관련된 함수와 데이터를 모아둔 집합 Physics 에서 지원한다.
- 매개변수로 (발사 위치, 발사 방향, 광선 거리)를 넘겨준다.
- Vector3
여기까지 하고 실행하면
Cube
와Plane
(바닥) 오브젝트를 카메라 화면 정중앙에 담게끔 바라보고 마우스 좌클릭 하면 “뭔가 광선에 걸렸다!” 콘솔 메세지를 출력한다.
Layer 설정, Debug.drawRay
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RayInteration : MonoBehaviour
{
private Camera playerCam;
public float distance = 100f;
public LayerMask whatIsTarget;
void Start()
{
playerCam = Camera.main;
}
void Update()
{
Vector3 rayOrigin = playerCam.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0.0f));
Vector3 rayDir = playerCam.transform.forward;
Debug.DrawRay(ray.origin, ray.direction * 100f, Color.green);
if (Input.GetMouseButtonDown(0))
{
if (Physics.Raycast(rayOrigin, rayDir, distance, whatIsTarget))
{
Debug.Log("뭔가 광선에 걸렸다!");
}
}
}
}
Plane
(바닥)은 광선 감지 처리 안하고 싶고Cube
만 광선 감지를 하고 싶다.Cube
에 Layer를 붙여 해당 Layer만 광선에 충돌 감지하도록 하자.
광선이 씬창에 보이게하기 위해 Debug.drawRay 로 광선을 그려주었다.
- public LayerMask
whatIsTarget
;- 어떤 레이어를 가진 Collider만 충돌 처리를 할 것인지 레이어 설정.
- 우선 새로운 Layer를 추가해서
Cube
에 붙여주면 (“Interactable”이라는 이름으로 했다.) 유니티에서 드롭 다운 슬롯이 열릴텐데 여기서Cube
에 붙어있는 layer를 선택한다.Cube
만 광선의 충돌 감지에 걸릴 수 있도록.
- Debug.DrawRay(ray.origin, ray.direction * 100f, Color.green)
- 개발자가 시각적으로 광선을 볼 수 있도록 광선을 그려주는 함수다.
- Debug에서 지원함
- 실제 게임에는 반영되지 않는다. 오직 개발자가 씬창에서 광선을 볼 수 있게 도와주는 역할!
- 광선이 씬창에서 초록색 선으로 그려지게 된다.
이제
Plane
(바닥)을 바라보고 좌클 눌러도 충돌 감지 되지 않는다.Cube
만 충돌 감지 함
RaycastHit, 광선에 감지된 오브젝트의 색깔을 바꾸게끔
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RayInteration : MonoBehaviour
{
private Camera playerCam;
public float distance = 100f;
public LayerMask whatIsTarget;
void Start()
{
playerCam = Camera.main;
}
void Update()
{
Vector3 rayOrigin = playerCam.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0.0f));
Vector3 rayDir = playerCam.transform.forward;
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
if (Physics.Raycast(rayOrigin, rayDir, out hit, distance, whatIsTarget))
{
GameObject hitTarget = hit.collider.gameObject;
hitTarget.GetComponent<Renderer>().material.color = Color.red;
Debug.Log(hit.collider.gameObject.name);
}
}
}
}
- RaycastHit
hit
;- 레이캐스트로부터 정보를 얻기 위한 타입으로
- 광선에 감지된 Collider의 정보를 담는 컨테이너다.
- 변수 RaycastHit hit 일때
- hit.normal : 광선에 감지된 Collider의 노말 벡터 (충돌 표면의 방향벡터)
- hit.distance : 광선 발사 위치로부터 광선에 감지된 Collider까지의 거리
- hit.point : 광선에 감지된 Collider의 충돌 위치벡터
- hit.collider : 광선에 감지된 Collider
- 변수 RaycastHit hit 일때
- if (Physics.Raycast(rayOrigin, rayDir,
out hit
, distance, whatIsTarget))- hit을
out
으로 넘기면 입력으로 들어간 값이 내부에서 어떤 값을 지정받고 빠져나온다. - raycast가 hit을 받게되면 이 hit에 정보들을 채워서 넘겨준다. 그런 다음에 hit로부터 정보를 가져온다.
- Physics.Raycast 함수의 실행이 끝나자마자
hit
에 광선에 충돌 감지된 Collider의 정보가 들어가게 된다.
- hit을
- GameObject
hitTarget
= hit.collider.gameObject;- 광선에 충돌 감지된 Collider 컴포넌트를 가진 오브젝트를
hitTarget
에 대입한다.
- 광선에 충돌 감지된 Collider 컴포넌트를 가진 오브젝트를
- hitTarget.GetComponent<Renderer>().material.color = Color.red;
- 광선에 충돌 감지된 Collider 오브젝트의 색깔을 빨간색으로 바꿈
- Debug.Log(hit.collider.gameObject.name);
- 광선에 충돌 감지된 Collider 오브젝트의 이름 출력
여기까지 하고 실행하면 좌클릭했을때 카메라 중앙에 오브젝트가 들어온다면 해당 오브젝트의 색깔이 빨간색으로 바뀐다.
Ray, 광선에 닿는 오브젝트 잡고 이동시키기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RayInteration : MonoBehaviour
{
private Camera playerCam;
public float distance = 100f;
private Transform moveTarget;
private float targetDistance;
public LayerMask whatIsTarget;
void Start()
{
playerCam = Camera.main;
}
void Update()
{
Ray ray = new Ray(rayOrigin, rayDir);
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
if (Physics.Raycast(ray, out hit, distance, whatIsTarget))
{
GameObject hitTarget = hit.collider.gameObject;
hitTarget.GetComponent<Renderer>().material.color = Color.red;
Debug.Log(hit.collider.gameObject.name);
Debug.Log("뭔가 광선에 걸렸다!");
moveTarget = hitTarget.transform;
targetDistance = hit.distance;
}
}
if(Input.GetMouseButtonUp(0))
{
if(moveTarget != null)
{
moveTarget.GetComponent<Renderer>().material.color = Color.white;
}
moveTarget = null;
}
if(moveTarget != null)
{
moveTarget.position = ray.origin + ray.direction * targetDistance;
}
}
}
- private Transform
moveTarget
;- 광선에 충돌 감지된 오브젝트를 Transform 타입으로 여기에 넣을 것
- private float
targetDistance
;- 광선에 충돌 감지된 오브젝트로부터 광선을 발사한 카메라 위치까지의 거리
- 거리를 유지하면서 오브젝트를 이동시킬거라서 필요
- Ray
ray
= new Ray(rayOrigin, rayDir);- 발사 위치와 발사 방향을 담는 광선
- if (Physics.Raycast(
ray
, out hit, distance, whatIsTarget))- 발사위치와 발사 방향을
ray
에 담아 넘긴다.
- 발사위치와 발사 방향을
- moveTarget = hitTarget.transform;
- 광선에 충돌 감지된 오브젝트의 위치
- targetDistance = hit.distance;
- 광선에 충돌 감지된 오브젝트로부터 광선을 발사한 카메라 위치까지의 거리
- if(Input.GetMouseButtonUp(0))
- 마우스 좌클에서 손을 떼자마자
- if(moveTarget != null)
- 광선에 충돌에 감지되서 moveTarget에 저장된게 있다면
- moveTarget.GetComponent<Renderer>().material.color = Color.white;
- 원래 색인 하얀색으로 돌려준다.
- moveTarget = null
- null로 초기화
- if(moveTarget != null)
- 계속 마우스 좌클을 유지하고 있는 동안이 됨
- moveTarget.position = ray.origin + ray.direction * targetDistance;
- 거리를 유지한 채로 광선에 감지된 오브젝트의 위치를 광선의 위치에서 광선이 쏘는 방향으로 옮겨준다.
- 거리를 유지한 채 카메라가 보는 방향으로 이동시키는게 된다.
🔔 레이캐스트를 사용하는 경우
- RayCast는 주로 정확한 물리처리를 요구할때 사용한다.
- OntriggerEnter OnCollision은 메세지 기반 프레임 기반 처리기 떄문에 딱 정확할때 처리 못할 수도 있다.
- Raycast로 아래에 광선을 쏴서 바닥을 검사하는 등등의 방식으로 더 많이 씀
- FPS는 레이케스트를 많이 쓴다. 광선을 쏴서 거기에 닿으면 데미지를 깎는 식으로!
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기