728x90
총을 쏘는 슈터 역할을 하는 PlayerShooter 스크립트를 만들자!
어떤 애니메이션을 사용하든 상관없이 캐릭터 손의 위치가 항상 손잡이에 위치하기 위해 애니메이터의 IK를 사용하자
- IK?? 이해를 위해선 FK 먼저 알아야 함.
캐릭터 애니메이션은 기본적으로 FK로 동작한다. 부모 조인트 -> 자식 조인트 순서로 움직임 적용함- FK(Forward Kinematics, 전진 운동학) : 대분류에서 소분류까지 순차적으로 힘이 연결되는 것
- IK(Inverse Kinematics, 역운동학) : 자식 조인트의 위치를 먼저 결정하고 부모 조인트가 거기에 맞춰서 변형
그래서 IK를 사용!
사용하려면 IK Pass 가 체크돼있어야 함
Animator에서 확인
PlayerShooter.cs 작성
using UnityEngine;
// 주어진 Gun 오브젝트를 쏘거나 재장전
// 알맞은 애니메이션을 재생하고 IK를 사용해 캐릭터 양손이 총에 위치하도록 조정
public class PlayerShooter : MonoBehaviour
{
public Gun gun; // 사용할 총
public Transform gunPivot; // 총 배치의 기준점
public Transform leftHandMount; // 총의 왼쪽 손잡이, 왼손이 위치할 지점
public Transform rightHandMount; // 총의 오른쪽 손잡이, 오른손이 위치할 지점
private PlayerInput playerInput; // 플레이어의 입력
private Animator playerAnimator; // 애니메이터 컴포넌트
private void Start() {
// 사용할 컴포넌트 가져오기
playerInput = GetComponent<PlayerInput>();
playerAnimator = GetComponent<Animator>();
}
private void OnEnable() {
// 슈터가 활성화될 때 총도 함께 활성화
gun.gameObject.SetActive(true);
}
private void OnDisable() {
// 슈터가 비활성화될 때 총도 함께 비활성화
gun.gameObject.SetActive(false);
}
private void Update() {
// 입력을 감지하고 총을 발사하거나 재장전
if (playerInput.fire) {
// 발사 입력 감지 시 총 발사
gun.Fire();
}
else if (playerInput.reload) {
// 재장전 입력 감지 시 재장전
if (gun.Reload()) {
// 재장전 성공 시에만 재장전 애니메이션 재생
playerAnimator.SetTrigger("Reload");
}
}
// 남은 탄알 UI 갱신
UpdateUI();
}
// 탄알 UI 갱신
private void UpdateUI() {
if (gun != null && UIManager.instance != null) {
// UI 매니저의 탄알 텍스트에 탄창의 탄알과 남은 전체 탄알 표시
UIManager.instance.UpdateAmmoText(gun.magAmmo, gun.ammoRemain);
}
}
// 애니메이터의 IK 갱신
private void OnAnimatorIK(int layerIndex) {
// 총의 기준점 gunPivot을 3D 모델의 오른쪽 팔꿈치 위치로 이동
gunPivot.position = playerAnimator.GetIKHintPosition(AvatarIKHint.RightElbow);
// IK를 사용하요 왼손의 위치와 회전을 총의 왼쪽 손잡이에 맞춤
playerAnimator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1.0f);
playerAnimator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 1.0f);
playerAnimator.SetIKPosition(AvatarIKGoal.LeftHand, leftHandMount.position);
playerAnimator.SetIKRotation(AvatarIKGoal.LeftHand, leftHandMount.rotation);
// IK를 사용하여 오른손의 위치와 회전을 총의 오른쪽 손잡이에 맞춤
playerAnimator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1.0f);
playerAnimator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1.0f);
playerAnimator.SetIKPosition(AvatarIKGoal.RightHand, rightHandMount.position);
playerAnimator.SetIKRotation(AvatarIKGoal.RightHand, rightHandMount.rotation);
}
}
- OnEnable() 메서드 : PlayerShooter 컴포넌트가 활성화될 때 자동으로 실행. 슈터가 활성화되면 총도 함께 활성화되어야 함!
- OnDisable() 메서드 : PlayerShooter 컴포넌트가 비활성화될 때 자동으로 실행. 슈터가 비활성화되면 총도 함께 비활성화되어야 함!
- IK 적용 : OnAnimatorIK() 메서드로 구현
- 가중치 : SetIKPositionWeight()와 SetIKRotationWeight()으로 결정
- 대상의 위치와 회전 : SetIKPosition()과 SetIKRotation()으로 결정
- 대상 : AvatarIKGoal 타입
Player Character에 PlayerShooter.cs 컴포넌트를 추가해주고, 할당해주자
- 인터페이스 : 어떤 메서드를 반드시 구현한다는 계약, 상속한 클래스는 반드시 메서드를 public으로 구현
- 스트립터블 오브젝트 : 데이터를 유니티 에셋 형태로 저장
- 코루틴 : 대기 시간
- 레이캐스트 : 광선을 쏴서 충돌하는 콜라이더가 있는지 검사하는 방법
- IK : 하위 조인트를 먼저 결정하고, 그것에 상위 조인트를 맞추는 애니메이팅 방식
생명체의 기반이 되는 LivingEntity 클래스를 만들자 (부모 클래스)
플레이어 캐릭터의 체력과 좀비 AI 체력을 구성할 것
- 다형성 (Polymorphism) : '여러 형태', 객체지향의 특징. C#에서 다형성은 자식 클래스 타입을 부모 클래스 타입으로 다룰 수 있게 함
- 상속 : 부모 클래스를 기반으로 자식 클래스를 만드는 방법
- FindObjectsOfType() 메서드 : 씬에서 명시한 타입의 모든 오브젝트를 찾아 배열로 반환
- 메서드에서도 다형성을 적용하여 같은 이름의 메서드가 서로 다른 방식으로 동작하게 할 수 있다!
그러한 방법 중 하나가 오버라이드(Override)
: 부모 클래스에서 작성한 메서드를 자식 클래스에서 재정의
※ 오버로딩 : 이름이 같은 메서드들인데, 매개변수의 유형과 개수를 다르게 하는 것
public class Monster : MonoBehavior {
public virtual void Attack() {
Debug.Log("공격!!");
}
}
public class Orc : Monster {
public override void Attack() {
base.Attack();
Debug.Log("가자!!!!");
}
}
- virtual 키워드로 지정된 메서드는 가상 메서드가 됨!
가상 메서드는 자식 클래스가 오버라이드할 수 있도록 허용된 메서드 - 자식 클래스는 override 키워드를 사용해 부모 클래스의 가상 메서드를 재정의할 수 있음
- base : 해당 메서드를 가지고 있는 윗 단계 부모 클래스를 지칭.
오버라이드되기 전의 원형 메서드로 접근할 수 있음
LivingEntity.cs 작성
using System;
using UnityEngine;
// 생명체로 동작할 게임 오브젝트들을 위한 뼈대를 제공
// 체력, 대미지 받아들이기, 사망 기능, 사망 이벤트를 제공
public class LivingEntity : MonoBehaviour, IDamageable
{
public float startingHealth = 100f; // 시작 체력
public float health { get; protected set; } // 현재 체력 (자동구현 프로퍼티)
public bool dead { get; protected set; } // 사망 상태
public event Action onDeath; // 사망 시 발동할 이벤트
// 생명체가 활성화될 때 상태를 리셋
protected virtual void OnEnable() {
// 사망하지 않은 상태로 시작
dead = false;
// 체력을 시작 체력으로 초기화
health = startingHealth;
}
// 대미지를 입는 기능
public virtual void OnDamage(float damage, Vector3 hitPoint, Vector3 hitNormal) {
// 대미지만큼 체력 감소
health -= damage;
// 체력이 0 이하 && 아직 죽지 않았다면 사망 처리 실행
if (health <= 0 && !dead) {
Die();
}
}
// 체력을 회복하는 기능
public virtual void RestoreHealth(float newHealth) {
if (dead) {
// 이미 사망한 경우 체력을 회복할 수 없음
return;
}
// 체력 추가
health += newHealth;
}
// 사망 처리
public virtual void Die() {
// OnDeath 이벤트에 등록된 메서드가 있다면 실행
if (onDeath != null) {
onDeath();
}
// 사망 상태를 참으로 변경
dead = true;
}
}
- public float health { get; protected set; } , public bool dead { get; protected set; }
- 자동구현 프로퍼티!
- protected 접근 한정자 : 값의 변화는 나와 나를 상속받는 자식만 가능
- public event Action onDeath;
- event : 구독!!!
- 이벤트는 연쇄 동작을 이끌어내는 사건임
- 이벤트 자체는 어떤 일을 실행하지 않지만 이벤트가 발생하면 이벤트를 구독하는 처리들이 연쇄적으로 실행됨
- 이벤트를 사용하면 어떤 클래스에서 특정 사건이 일어났을 때 다른 클래스에서 그것을 감지하고 관련된 처리를 실행할 수 있음
- 이벤트를 구현할 때는 이벤트와 이벤트에 관심이 있는 이벤트 리스너(Event Listener)로 오브젝트를 구분함
- Action 타입 : 입력과 출력이 없는 메서드를 가리킬 수 있는 델리게이트(Delegete)
※ 델리게이트 : 대리자. 메서드를 값으로 할당받을 수 있는 타입- Action 타입의 변수에는 입력과 출력이 없는 메서드를 등록할 수 있음 (void)
- += 을 사용해 메서드를 등록할 수 있음. 등록할 메서드 끝에는 괄호없이 이름만 명시!
괄호를 붙이면 메서드를 등록하는 것이 아니라 실행하고 그 반환값을 할당하는 것이 됨
- 델리게이트 타입의 변수는 event 키워드를 붙여 선언.
어떤 델리게이트 변수를 event로 선언하면 클래스 외부에서는 해당 델리게이트를 실행할 수 없게 됨
- event : 구독!!!
체력 UI를 구현해보자
Hierarchy 뷰에서 UI - Slider 추가
- Canvas
- Canvas - Render Mode
- Screen Space - Overlay : 게임화면과 UI화면이 별개라서 따로 작업
- Screen Space - Camera : 카메라가 기준이 되는 카메라 시야에 맞춰서 게임과 캔버스가 배치돼서 작업됨
- World Space : 해당 UI를 인게임 요소로 만듦
- 일반적으로 VR 개발하면 UI를 World Space로 해줘야 함 (양안 처리를 위해서임)
- Canvas Scaler
- 단위당 레퍼런스 픽셀(Reference Pixels Per Unit)
- UI 스프라이트의 픽셀 크기와 게임 월드의 유닛 크기가 대응되는 비율을 결정
- 이 값은 UI의 스프라이트 화질에 영향을 줌
- 단위당 레퍼런스 픽셀(Reference Pixels Per Unit)
- Canvas - Render Mode
Canvas를 Player Character 자식으로 넣어 줌
Canvas의 Rect Transform 변경
Alt를 누르고 Canvas 클릭하면 자식 오브젝트까지 다 열림
Handle Slide Area(슬라이더의 손잡이임) 삭제해 줌
Canvas 안에 있는 거 전부 클릭해서 Anchor Presets
alt + shift 눌러서 full stretch
Slider 이름을 Health Slider로 변경
Interactable : 반응형!
색상을 변경해주자
'Hello, World! > Unity' 카테고리의 다른 글
유니티 3D 좀비 서바이벌 게임 만들자 (16) (0) | 2022.06.01 |
---|---|
유니티 3D 좀비 서바이벌 게임 만들자 (15) (0) | 2022.06.01 |
유니티에서 VSCode를 쓰자 (0) | 2022.05.30 |
유니티 파티클 시스템 (골드메탈님 따라하기) (0) | 2022.05.18 |
유니티 3D 좀비 서바이벌 게임 만들자 (13) (0) | 2022.04.20 |
댓글