Chapter 1. 캐릭터 움직이기 & 지형 제작
팔 구현
🙋♀️ 팔 붙이기
- Player
- 캡슐 모양의 플레이어 오브젝트
- 자식
- Main Camera
- 플레이어의 자식으로서 플레이어를 따라다니며 플레이어의 1인칭 카메라가 되어 줄 오브젝트
- Hand Holder
- 빈 게임 오브젝트로서 팔 모델의 부모 오브젝트로 설정하여 팔 모델의 트랜스폼엔 변화를 주지 않고 이 “Hand Holder” 오브젝트의 트랜스폼에만 변화를 주어 Player 오브젝트 내에서 팔 모델이 자리를 잡도록 한다.
- 카메라에 팔이 알맞는 위치로 잡히도록 Position은 (0, 0.5, -1.3), Rotation은 (0, 90, 0)으로 하였다.
- Y 축 중심으로 90 도 회전하여 카메라에 1인칭 시점으로 팔이 나오도록 카메라 방향과 일치시키도록 회전시켰다.
- 자식
- Hand
- 임포트 받은 기본 팔 3D 모델. 이름을 “Hand”로 변경함
- 우클 - Unpack Prefab 하여 프리팹 상태를 해제 시키고 자식으로 있는 카메라는 삭제해 주었다.
- Hand
- Main Camera
🙋♀️ 팔 애니메이션
팔 애니메이션 만들기
임포트 받은 팔의 3D 모델 파일(lowPoly_arms_Basic Import Settings)은 아래와 같이 이미 애니메이션이 내장되어 있어서 이 애니메이션 프레임을 쪼개어서 각 상황에 맞는 애니메이션을 만들어볼 것이다.
0 ~ 260 프레임 길이의 애니메이션이며 팔을 흔드는 모션을 취하는 애니메이션이다.
- 👉 팔의 3D 모델 파일 애니메이션에서 0 ~ 24 범위의 프레임을 떼 옴
- Loop Time 체크
- 반복 재생
- 정지해 있는 팔 애니메이션이 반복해서 재생되도록
- Loop Pose 체크
- 자연스럽고 부드럽게 반복 재생
위와 같은 방법으로 0 ~ 260 길이의 팔의 3 D 모델의 애니메이션 하나로부터 프레임 구간을 나눠 다음과 같이 8 개의 애니메이션 파일들을 만들었다. 설정한 후 Apply.
- Hand_Idle : 정지해있을 때
- 0 ~ 24 프레임
- 반복 실행해야 하므로 Loop Time 체크, Loop Pose 체크
- Handle_Run : 달릴 때
- 30 ~ 40 프레임
- 반복 실행해야 하므로 Loop Time 체크, Loop Pose 체크
- Handle_Walk : 걸을 때
- 60 ~ 84 프레임
- 반복 실행해야 하므로 Loop Time 체크, Loop Pose 체크
- Handle_Attack : 공격할 때
- 90 ~ 115 프레임
- 반복할 필요는 없다. 한번만 공격 하므로.
- Handle_Work : 일할 때
- 120 ~ 145 프레임
- 반복할 필요는 없다. 한번만 공격 하므로.
- Handle_Weapon_Out : 무기를 꺼내기 위해서 팔을 내릴 때
- 210 ~ 220 프레임
- 반복할 필요는 없다. 한번만 공격 하므로.
- Handle_Weapon_In : 무기를 꺼냈을 때
- 220 ~ 230 프레임
- 반복할 필요는 없다. 한번만 공격 하므로.
- Handle_Item : 아이템 주울 때
- 240 ~ 255 프레임
- 반복할 필요는 없다. 한번만 공격 하므로.
애니메이션 컨트롤러
📂Assets/Animations/Hand 폴더를 만들어서 위와 같이 팔의 애니메이션을 제어할 애니메이션 컨트롤러를 생성한다. 이름은 “Hand Animatior”
이를 Hand의 Animator 컴포넌트의 Controller에 할당한다.
- 파라미터 6 개를 만들어 주었다.
- 네모는 Boolean
- Walk, Run
- 한번 걷거나 뛰면 조건이 바뀔 때까지 계속 재생을 유지 하므로 bool 이 적합
- 동그라미는 Trigger
- Item_Pickup, Weapon_Out, Attack, Work
- 한번만 재생할 것들이기 때문에 Trigger가 적합.
- 네모는 Boolean
- Bool 과 Trigger 의 차이
- True, False 두개의 값을 가진다는 면에서 비슷하다.
- Bool은 한번 값이 바뀌면 다시 수동적으로 바꿔주지 않는 한 그 값을 유지하지만
- Trigger는 True 가 됐다면 곧 바로 다시 False 로 돌아온다
팔 3D 모델에 내장되어 있던 애니메이션 하나를 쪼개서 만들었던 8 가지 애니메이션 파일들이 팔 3D 모델의 자식으로 들어가 있다. 팔 3D 모델의 재생 버튼을 클릭하면 위와 같이 자식 에셋들을 확인할 수 있다. 이 8 가지 애니메이션 파일들을 상태도에 드래그 앤 드롭 해주자.
- 디폴트 애니메이션은 Handle_Weapon_In
- 우클 - Set as Layer Default State
- 게임이 시작될 때처럼 부모인 팔 모델이 활성화 될 때 가장 먼저 실행될 애니메이션.
- 손을 꺼내는 동작으로 시작할거라서
- 두 애니메이션의 순서를 나타내는 트랜지션 만들기
- 우클 - Make Transition
- Handle_WeaPon_In이 끝나면 Handle_Idle을 재생한다.
- Has Exit Time 을 체크한다.
- 이게 체크되어 있으면 종료 시점을 활성화 한다는 뜻으로, 다음 트랜지션을 발동시킬 조건이 만족하더라도 무조건 해당 애니메이션을 종료 시점까지 재생한 후 다음 트랜지션으로 넘어간다는 뜻이다.
- Exit Time 을 1 로 설정한다.
- 이 의미는 Handle_WeaPon_In를 끝까지 다 100퍼센트 재생한 후 Handle_Idle을 재생하겠다는 의미다. 초가 아닌 비율이다.
- 만약 0.5 라면 Handle_WeaPon_In의 절반만 재생한 후 Handle_Idle 재생하러 감.
- Transition Duration 을 0 으로 설정한다.
- 전이가 일어나는데에 두는 지연시간이다.
- 지연시간 없이 바로 Handle_WeaPon_In가 끝나면 Handle_Idle를 재생한다.
- Has Exit Time 을 체크한다.
- Hand_Idle 👉 Hand_Walk
- 전이 조건 Condition
- Walk 파라미터 값이 True 일 때 발동
- Has Exit Time은 체크 해제한다.
- 정지해있다가 걸으려면 바로 걸어야 하므로 정지 애니메이션이 끝날 때까지 기다릴 필요가 없다.
- Transition Duration 을 0.1 으로 설정하여 곧바로 바로 걷기 애니메이션이 재생되는 것이 아닌 0.1초 텀을 두어 자연스럽게 이어지도록 했다.
- 전이 조건 Condition
- Hand_Idle 👉 Hand_Run
- 전이 조건 Condition
- Run 파라미터 값이 True 일 때 발동
- 나머지 설정 똑같이
- 전이 조건 Condition
- Handle_Walk 👉 Handle_Idle
- 전이 조건 Condition
- Walk 파라미터 값이 False 일 때 발동
- 나머지 설정 똑같이
- 전이 조건 Condition
- Handle_Run 👉 Handle_Idle
- 전이 조건 Condition
- Run 파라미터 값이 False 일 때 발동
- 나머지 설정 똑같이
- 전이 조건 Condition
- Handle_Walk 👉 Handle_Run
- 전이 조건 Condition
- Run 파라미터 값이 True 일 때 발동
- 나머지 설정 똑같이
- Run 👉 Walk 를 만들지 않은 이유
- 걷게 된다는건 더 이상 Left Shift를 누르고 있지 않은 채로 WASD 키만 누르게 된다는 것이다. 즉, Run도, Walk도 둘 다 True인 상태에서 Run 만 False가 되는 것이다. 자연스럽게 Idle 상태에 오게 되고, 이어서 자연스럽게 곧 바로 Idle 에서 Walk 로 전이될 수 있다. Walk 가 True 라는 조건이 만족 되므로 Idle이 재생되지 않고 바로 Walk로 전이될 수 있다. (Has Exit Time 해제해서)
- Walk 👉 Run 트랜지션도 Idle을 중간에 거치면 되니까 만들 필요 없는것 아닌가?
- 걷다가 뛴다는 것은 WASD 키 입력으로 Walk만 True인 상태에서 Left SHift + WASD 를 같이 누른다는거니까 Run도 Walk도 둘 다 True가 된다는 것인데, 이 과정에서 Walk 👉 Idle 이 될 수 있는 조건은 없다. 달리려면 걷는 동작인 WASD 도 같이 쉬프트키와 함께 입력 되야 하기 때문에 Walk도 True여야 하기 때문이다.
- 직접 걷다가 WASD 키를 떼고 Idle 이 한번 된 후 달리는 수 밖엔 없다.
- 따라서 직접 바로 Walk 상태에서 Run으로 전이될 수 있도록 트랜지션을 따로 만들어준 것이다.
- 이게 맞는진 모르겠지만 이렇게 이해했다. 헷갈려 ㅠ ㅠ
- 걷다가 뛴다는 것은 WASD 키 입력으로 Walk만 True인 상태에서 Left SHift + WASD 를 같이 누른다는거니까 Run도 Walk도 둘 다 True가 된다는 것인데, 이 과정에서 Walk 👉 Idle 이 될 수 있는 조건은 없다. 달리려면 걷는 동작인 WASD 도 같이 쉬프트키와 함께 입력 되야 하기 때문에 Walk도 True여야 하기 때문이다.
- 전이 조건 Condition
- AnyState 👉 Hand_Attack
- 어느 상태를 재생중이던 간에 Attack Trigger가 발동되면 Hand_Attack을 재생한다.
- 전이 조건 Condition
- Attack가 발동될 때.
- Has Exit Time은 체크 해제한다. 어떤 상태이던간에 공격을 해야 하면 바로 재생을 끊고 공격 애니메이션을 재생할 수 있도록!
- Transition Duration 을 0.1 으로 설정한다.
- 전이 조건 Condition
- 어느 상태를 재생중이던 간에 Attack Trigger가 발동되면 Hand_Attack을 재생한다.
- Hand_Attack 👉 Idle
- 전이 조건은 없다.
- Has Exit Time은 체크 한다.
- 그냥 별다른 전이 조건 없이 공격 애니메이션 다 끝나면 Idle 상태로 되돌아 오도록 한다.
- Exit Time 은 1 로 설정한다.
- 공격 애니메이션이 끝까지 재생을 완료 한 후 Idle 이 될 수 있도록
- Transition Duration 을 0.1 으로 설정한다.
나머지 애니메이션 파일은 추후 설정
📜Hand.cs
Hand 오브젝트에 붙여준다.
손이 가지는 기능과 속성들
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Hand : MonoBehaviour
{
public string handName; // 너클이나 맨 손 구분. 이름으로 구분할 것이다.
public float range; // 공격 범위. 팔을 뻗으면 어디까지 공격이 닿을지
public int damage; // 공격력
public float workSpeed; // 작업 속도
public float attackDelay; // 공격 딜레이. 마우스 클릭하는 순간 마다 공격할 순 없으므로.
public float attackDelayA; // 공격 활성화 시점. 공격 애니메이션 중에서 주먹이 다 뻗어졌을 때 부터 공격 데미지가 들어가야 한다.
public float attackDelayB; // 공격 비활성화 시점. 이제 다 때리고 주먹을 빼는 애니메이션이 시작되면 공격 데미지가 들어가면 안된다.
public Animator anim; // 애니메이터 컴포넌트
// public BoxCollider boxCollider;
}
- 데미지가 입혀지는 범위인 boxCollider를 주먹에 달지 않고, 데미지를 입히는 범위를 화면 중앙 기준으로 잡을 것이다.
- boxCollider를 주먹에 달면 1인칭 시점과 주먹에 달린 boxCollider의 위치가 서로 다르게 보일 수 있어서 문제가 생길 수 있다. 안 맞았는데 맞았다고 처리되는 등등.. 3인칭이였다면 주먹에 boxCollider 달아도 됐었다.
- 따라서 boxCollider은 사용하지 않을거라 주석 처리 함.
공격이 시작되고 0.5 초 후에 데미지가 들어가며 그로부터 0.1 초 후부턴 데미지가 들어가지 않음
📜HandController.cs
Hand Holder 오브젝트에 붙여준다.
모든 무기들의 공통된 부분
무기들 마다 각각 Controller 스크립트를 만드는건 귀찮은 일. 모든 Hand형 무기들은 애니메이션과 공격 범위 같은 것이 같으므로 그와 관련된 것들을 이 스크립트에 작성한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HandController : MonoBehaviour
{
[SerializeField]
private Hand currentHand; // 현재 장착된 Hand 형 타입 무기
private bool isAttack = false; // 현재 공격 중인지
private bool isSwing = false; // 팔을 휘두르는 중인지. isSwing = True 일 때만 데미지를 적용할 것이다.
private RaycastHit hitInfo; // 현재 무기(Hand)에 닿은 것들의 정보.
void Update()
{
TryAttack();
}
private void TryAttack()
{
if(Input.GetButton("Fire1"))
{
if(!isAttack)
{
StartCoroutine(AttackCoroutine());
}
}
}
IEnumerator AttackCoroutine()
{
isAttack = true;
currentHand.anim.SetTrigger("Attack");
yield return new WaitForSeconds(currentHand.attackDelayA);
isSwing = true;
StartCoroutine(HitCoroutine());
yield return new WaitForSeconds(currentHand.attackDelayB);
isSwing = false;
yield return new WaitForSeconds(currentHand.attackDelay - currentHand.attackDelayA - currentHand.attackDelayB);
isAttack = false;
}
IEnumerator HitCoroutine()
{
while (isSwing)
{
if(CheckObject())
{
isSwing = false;
Debug.Log(hitInfo.transform.name);
}
yield return null;
}
}
private bool CheckObject()
{
if(Physics.Raycast(transform.position, transform.forward, out hitInfo, currentHand.range))
{
return true;
}
return false;
}
}
- Edit - Project Setting - Input
- “Fire1”에 마우스 좌클릭(Mouse 0) 뿐만 아니라 Left Ctrl 도 할당이 되어 있어서 이를 지워 주었다. 이 게임에선 Left Ctrl 키를 앉은 키로 쓰기 때문에
- TryAttack()
- 매 프레임마다 좌클릭 입력이 들어오는지를 검사하고 좌클릭 입력시 공격의 과정을 처리하는 코루틴 함수인 AttackCoroutine() 함수를 실행한다.
- AttackCoroutine()
- 좌클릭 입력시 실행된다.
- isAttack을 True 로 설정하고 공격 처리 과정을 시작한다.
- “Attack” 트리거를 발동시켜 공격 애니메이션을 재생하게 한다. Any State 👉 Hand_Attack 전이 됨.
- 📜Hand.cs 의 attackDelayA 시간 동안 대기 한다. (대략 공격 애니메이션에서 팔을 뻗는 데가 재생되는데까지 걸리는 시간)
- isSwing을 True 로 설정한다. 👉 데미지가 들어가기 시작
- HitCoroutine() 코루틴 함수를 실행하여 Raycast에 충돌 된 오브젝트가 있는지 while문으로 계속 검사하는 일을 하자.
- 충돌한 오브젝트를 찾아 HitCoroutine() 코루틴 함수가 끝나면 isSwing이 false가 된 상태일 것이다.
- 📜Hand.cs 의 attackDelayB 시간 동안 추가로 대기 한다. (대략 공격 애니메이션에서 팔을 다 뻗고 이제 집어넣는데 걸리는 시간)
- isSwing을 False 로 설정한다. 👉 이제 데미지가 더 이상 들어가지 않음
- 📜Hand.cs 의 attackDelay -attackDelayA - attackDelayB 즉, 남은 시간동안 추가로 대기 한다.
- “Attack” 트리거를 발동시켜 공격 애니메이션을 재생하게 한다. Any State 👉 Hand_Attack 전이 됨.
- isAttack을 False 로 설정하고 공격 처리 과정을 마친다.
- HitCoroutine()
- 한프레임씩 쉬면서 CheckObject() 함수를 실행하여 이 스크립트가 붙을 Hand Holder의 위치로부터 쏜 Raycast에 충돌한 오브젝트가 있는지 무한 반복하여 검사한다.
- 충돌한 오브젝트를 찾았다면 isSwing을 False로 만들고 while문을 마치고 함수도 끝낸다.
- 일단 추후 여기에 데미지 처리를 할 것이다. 일단 Debug.Log(hitInfo.transform.name) 충돌한 물체의 이름을 출력하기로.
- CheckObject()
- 이 스크립트가 붙을 Hand Holder의 위치로부터 앞 방향으로 📜Hand.cs 의 range 길이로 쏜 Raycast에 충돌한 오브젝트가 있다면 그 정보를 hitInfo에 담고 True를 리턴한다.
- 충돌한 오브젝트가 없다면 False를 리턴한다.
Hand를 CurrentHand에 할당.
🙋♀️ 그 밖의 개선
팔이 카메라를 따라오게끔
위아래로 고개를 흔들 때 팔은 따라오지 않아 부자연스럽다. 팔도 카메라를 따라 회전해야 한다. 따라서 Hand Holder를 Main Camera의 자식으로 넣어 카메라를 따라다니게 해주었다.
팔을 뻗으면 팔 밑부분이 잘리고, 나무에 가까이가면 팔이 파묻히는 현상Permalink
오브젝트에 팔이 닿으면, 팔이 파묻히는 현상이 일어난다.
마우스 좌클로 공격 애니메이션이 재생되면 위와 같이 팔 아랫부분이 잘리는 현상이 일어난다.
이를 해결하기 위해 서브 카메라 하나 더 생성 !
Weapon Camera라는 이름의 카메라를 만든다. 서브 카메라인 셈이다. 이 카메라를 Main Camera의 자식으로 넣어어 Main Camera를 따라다니게 한다.
- 카메라에 기본으로 달려있는 Audio Listner 컴포넌트는 한 Scene에 하나만 존재해야 하기 때문에, Main Camera 하나에만 있으면 된다 따라서 Weapon Camera의 Audio Listner 컴포넌트는 지워주도록 하자.
해결 방법
- 서브 카메라는 오직 팔만 찍고, 메인 카메라는 팔을 제외한 모든 것들을 찍게 한다.
- 이 상태에서 서브 카메라의 depth 값을 메인 카메라 depth 값보다 높게 지정하여 서브 카메라가 메인 카메라보다 우선하게 한다. 즉, 서브카메라의 렌더링을 메인 카메라보다 나중에 한다.
- depth 값이 낮은 카메라일 수록 먼저 렌더링 된다. depth 값이 높은 카메라일 수록 제일 나중에 렌더링 되므로 가장 나중에 덮어 씌우는 격이 된다. 더 우선시 되는 꼴.
- 팔을 찍는 서브 카메라를 렌더링시 더 우선하게 되므로 팔이 오브젝트에 파묻히는 현상 해결!
- 팔을 휘두를때 팔 밑부분이 잘리는 현상은 카메라가 렌더링을 시작하는 지점이 카메라로부터 멀기 때문에 나타나는 현상이다. 따라서 이 지점을 카메라 위치와 최대한 가깝게 해주어 팔의 밑 부분까지 다 렌더링 되도록 한다.
- 팔 아랫부분이 잘리는 현상 해결!
- 이 상태에서 서브 카메라의 depth 값을 메인 카메라 depth 값보다 높게 지정하여 서브 카메라가 메인 카메라보다 우선하게 한다. 즉, 서브카메라의 렌더링을 메인 카메라보다 나중에 한다.
우선 “Weapon” 이라는 이름의 Layer를 추가한 후, Hand의 Layer를 “Weapon”으로 지정한다. 서브 카메라인 Weapon Camera가 오직 Hand만 찍게 할 것이기 때문에 Layer를 달아 구분해주는 것이 필요하기 때문이다.
- Main Camera 설정
- Culling Mask 👉 카메라로 찍을, 즉 렌더링 할 레이어들을 선택한다.
- 렌더링 할 레이어들에서 “Weapon”이 체크 해제 되어 있는 것을 알 수 있다.
- 즉, 팔은 찍지 않고 팔을 제외한 모든 것을 찍는다.
- 아래 사진은 Main Camera가 찍는 화면이다. 팔을 렌더링에서 제외하는 것을 볼 수 있다.
- Depth 👉 해당 카메라가 그려지는 순서.
- -1 값인 것을 확인할 수 있다.
- depth 값이 낮은 카메라일 수록 먼저 렌더링 된다. depth 값이 높은 카메라일 수록 제일 나중에 렌더링 되므로 가장 나중에 덮어 씌우는 격이 된다. 더 우선시 되는 꼴.
- 메인 카메라는 기본 디폴트 Depth 값이 -1로서 주로 가장 먼저, 또 가장 밑에서 렌더링 된다는 것을 의미한다.
- Culling Mask 👉 카메라로 찍을, 즉 렌더링 할 레이어들을 선택한다.
- Weapon Camera 설정
- Clear Flags 👉 Depth Only 로 설정했다.
- 즉, 찍는 대상(레이어)를 제외한 나머지 빈 공간은 투명 처리한다.
- 종류
- SkyBox
- 스카이 박스라 불리는 배경으로 빈 공간을 채운다.
- Solid Color
- 단색으로 빈 공간을 채운다.
- Depth Only
- 빈 공간을 투명 처리한다.
- 이와 같은 특성 때문에 지금과 같이 카메라 2 대 쓸 때와 같이 여러 대의 카메라를 한 화면에 합성할 때 주로 사용한다.
- 각 여러 카메라들의 영상들을 합성하기 위해선 빈 공간은 투명해야 하기 때문이다.
- 유니티는 여러대의 카메라가 있으면 depth 값이 낮은 카메라부터 먼저 렌더링 하여 차곡 차곡 쌓아서 그린다. 즉, depth 값이 낮은 카메라가 가장 먼저 그려지고 그 위에 depth 값이 높은 카메라가 덮어 씌워져 그려지게 된다.
- 빈 공간을 투명 처리한다.
- Don’t Clear
- 화면을 새로 갱신하지 않고 이전의 화면 위에다가 다시 그린다. 때문에 오브젝트가 움직이면 잔상이 화면에 남는다.
- SkyBox
- Culling Mask 👉 “Weapon”
- 즉, 오로지 “Weapon” Layer가 설정되어 있는 오브젝트만 찍는다.
- 따라서 팔만 찍게 된다.
- 아래 사진은 Weapon Camera가 찍는 화면이다. 팔만 찍고 나머지 빈 공간은 투명 처리 된 것을 볼 수 있다.
- Clipping Planes 👉 Near 의 값을 최소 값이 0.01로 해주었다.
- 카메라는 모든 공간을 다 렌더링 하지 않는다. Near 평면과 Far 평면 사이에 해당하는 3 차원 공간만 렌더링 하여 카메라 화면에 담는다.
- Near 값을 최소값인 0.01로 설정하여 최대한 카메라와 가까운 거리도 다 렌더링 될 수 있도록 하였다.
- 팔을 뻗을 때 팔 아랫부분이 렌더링 되지 않고 잘렸던 이유는 Near 값이 높아 카메라가 렌더링 하는, 즉 찍는 시작점이 카메라로부터 멀었기 때문이다. Near 값을 줄여 카메라 바로 앞 거리도 렌더링 하게 됨으로써 팔 아랫부분이 렌더링 되지 않는 현상을 해결하였다.
- Depth 👉 0
- -1 인 Main Camera 보다 depth 값이 높아 더 나중에 렌더링 된다. 즉, 제일 상위에서 덮어 씌워지게 되므로 팔이 더 이상 파묻히지 않게 된다.
- Weapon Camera가 찍는 화면인 아래 사진이 Main Camera가 찍는 화면 위에 그려지게 된다. Depth 값이 0 으로 더 높기 때문에 더 나중에 그려지기 때문이다!
- Clear Flags 👉 Depth Only 로 설정했다.
ㅈ
Depth 값이 가장 낮은 Main Camera 가 촬영하는 화면이 먼저 렌더링 되고, 그 위에 Weapon Camera 가 촬영 하는 화면인 팔이 덮어 씌워져 렌더링 되어 최종적인 카메라 화면이 완성된다.
충돌 처리가 제대로 안되는 이유
private bool CheckObject()
{
if(Physics.Raycast(transform.position, transform.forward, out hitInfo, currentHand.range))
{
return true;
}
return false;
}
📜HandController.cs 에서 이 스크립트가 붙여진 HandHolder로부터 앞 쪽 방향 (Z축 positive 방향)으로 Raycast를 쏘기로 했었다. 그러나 게임을 실행해서 Collider가 이미 기본으로 붙어있는 Terrain 지형을 때려도 아무 반응이 일어나지 않는다.
그 이유는, 이전에 HandHolder을 Y 축으로 90 도 회전했던 탓에 HandHolder의 Z 축도 같이 우측으로 90 도 회전해버렸기 때문이다. 앞 방향(forward)는 Z 축의 Positive 방향을 뜻하는데, 서로 Z 축이 달라서 아무리 Player로서 앞을 보고 땅을 때려도 HandHolder 입장에선 그 방향은 오른쪽이였기 때문에 그 방향으로는 Raycast가 나가지 않아 충돌처리가 일어나지 않았기 때문에 발생한 문제다.
빈 오브젝트 Holder를 하나 더 추가하여 이의 자식으로 HandHolder를 넣어준다. Holder는 트랜스폼 Position, Rotation 모두 원점을 유지하고 트랜스폼 값 변경은 Handle Holder에서 담당하게 한다. 그리고 📜HandControllder.cs를 Handle Holder가 아닌 Holder에 붙여 주면 문제가 해결 된다. Holder는 90도로 회전하지 않으며 90도로 회전하는 것은 자식인 Handle Holder가 대신하기 때문이다. 이제 📜HandControllder.cs가 붙은 Holder의 Z 축과 Player의 Z 축이 일치하게 되므로 문제가 해결 된다.
Player 자신 또한 캡슐 콜라이더가 있기 때문에 자기 자신과의 충돌 처리가 가능하다. 따라서 Player의 Layer를 “Ignore Raycast” 로 설정하여 Raycast 에 의한 충돌처리가 되지 않도록 해준다. 자식들까지 모두 적용한다. 단, 자식들까지 모두 적용하니 Hand의 Layer를 다시금 “Weapon”으로 변경해준다.
'C# > UNITY_FPS 3D 서바이벌게임' 카테고리의 다른 글
Chapter 2-2. 무기 : 총 구현 2️⃣ (재장전, 정조준, 반동) (0) | 2024.03.11 |
---|---|
Chapter 2-1. 무기 : 총 구현 1️⃣ (애니메이션, 파티클, 오디오) (0) | 2024.03.11 |
Chapter 1-3. 캐릭터, 지형 : 지형 제작 (0) | 2024.03.11 |
Chapter 1-2. 캐릭터, 지형 : 달리기, 부드럽게 앉기, 점프 (0) | 2024.03.11 |
Chapter 1-1. 캐릭터, 지형 : 마우스, 키보드에 따른 이동과 회전, 1인칭 카메라 (0) | 2024.03.11 |