위키북스 출판사 이재현 저자님의 '절대강좌! 유니티' 책을 참고하여 필기한 내용입니다.
사용자 정의 이벤트 - Delegate
주인공이 사망하면 모든적이 공격을 중지할 수 있게 foreac문을 이용하여 순차적으로 공격 중지 함수를 호출
적 캐릭터가 많다면 SendMessage는 좋은 방법이 아님
순차적인 호출 방식을 이벤트 구동(Event Drivern) 방식으로 변경
: 특정한 조건을 만족하면 자동으로 알려주는 메세지를 의미 , 순차적으로 호출하는 방식보다 효율적임
델리게이트
함수를 참조하는 변수를 의미, 함수를 저장할 수 있는 일종의 변수 ( C++의 포인터)
아래의 코드는 예제이므로 작성하지 않아도 된다.
using UnityEngine;
public class DelegateDemo : MonoBehaviour
{
// 델리게이트 선언
delegate float SumHadler(float a, float b);
// 델리게이트 타입의 변수 선언
SumHadler sumHadler;
// 덧셈 연산을 하는 함수
float Sum(float a, float b)
{
return a + b;
}
void Start()
{
// 델리게이트 변수에 함수(메서드) 연결(할당)
sumHadler = Sum;
// 델리게이트 실행
float sum = sumHadler(10.0f, 5.0f);
// 결괏값 출력
Debug.Log($"Sum = {sum}");
}
}
델리게이트의 선언을 보면 float 타입이고 float 타입의 매개변수 두개를 갖는 함수로 지정하였다.
delegate float SumHadler(float a, float b);
해당 델리게이트 타입의 변수를 선언한다.
SumHadler sumHadler;
Start 함수에서 sumHandler에 Sum 함수를 저장함, 이때 sumHandler를 호출하면 Sum함수도 호출
void Start()
{
// 델리게이트 변수에 함수(메서드) 연결(할당)
sumHadler = Sum;
// 델리게이트 실행
float sum = sumHadler(10.0f, 5.0f);
// 결괏값 출력
Debug.Log($"Sum = {sum}");
}
주인공의 사망 이벤트 처리
playerCtrl 스크립트를 아래와 같이 수정한다. 기존에 작성되어 있단 PlayerDie()함수는 주석처리하고 추가한다.
using System.Collections;
using UnityEngine;
public class PlayerCtrl : MonoBehaviour
{
[SerializeField]
private Transform tr;
// 이동 속력 변수
public float moveSpeed = 10.0f;
// 회전 속도 변수
public float turnSpeed = 80.0f;
// Animation 컴포넌트를 저장할 변수
private Animation anim;
// 초기 생명 값
private readonly float initHP = 100.0f;
// 현재 생명 값
public float currHp;
// 델리게이트 선언
public delegate void PlayerDieHandler();
// 이벤트 선언
public static event PlayerDieHandler OnPlayerDie;
...생략
// Player 사망 처리
void PlayerDie()
{
Debug.Log("Player Die !");
//// MONSTER 태그를 가진 모든 게임오브젝트를 찾아옴
//GameObject[] monsters = GameObject.FindGameObjectsWithTag("MONSTER");
//// 모든 몬스터의 OnPlayerDie 함수를 순차적으로 호출
//foreach(GameObject monster in monsters)
//{
// monster.SendMessage("OnPlayerDie", SendMessageOptions.DontRequireReceiver);
//}
// 주인공 사망 이벤트 호출(발생)
OnPlayerDie();
}
}
이벤트를 정의 하려면 델리게이트를 이용해 이벤트 함수의 원형을 선언해야됨
이벤트 함수는 언제 호출될지 모르기 때문에 정적변수 선언해야됨 Static
// 델리게이트 선언
public delegate void PlayerDieHandler();
// 이벤트 선언
public static event PlayerDieHandler OnPlayerDie;
OnPlayerDie는 이벤트 명이라고 하지만 변수의 일종이며 PlayerDieHandler는 OnPlayerDie 변수의 타입
MonsterCtrl 스크립트에 함수 추가한다.
// 스크립트가 활성화될 때마다 호출되는 함수
private void OnEnable()
{
// 이벤트 발생 시 수행할 함수 연결
PlayerCtrl.OnPlayerDie += this.OnPlayerDie;
}
// 스크립트가 비활성화될 때마다 호출되는 함수
private void OnDisable()
{
// 기존에 연결된 함수 해제
PlayerCtrl.OnPlayerDie -= this.OnPlayerDie;
OnEnable() 과 OnDisable() 함수는 스크립트가 활성화되거나 비활성화될 때 수행되는 함수로 이벤트의 결과 해지는 반드시 이 함수에서 처리
- 이벤트 연결: (이벤트가 선언된 클래스명).(이벤트명) += (이벤트 발생 시 호출할 함수)
- 이벤트 해지 : (이벤트가 선언된 클래스명).(이벤트명) -= (이벤트 발생 시 호출할 함수)
몬스터의 사망 처리
몬스터가 죽는 사망 애니메이션을 완성하기
using System.Collections;
using UnityEngine;
using UnityEngine.AI; // 내비게이션 기능을 사용하기 위해 추가해야 하는 네임 스페이스
public class MonsterCtrl : MonoBehaviour
{
// 몬스터 상태의 정보
public enum State
{
IDLE,
TRACE,
ATTACK,
DIE
}
// 몬스터 현재 상태
public State state = State.IDLE;
// 추적 사정거리
public float traceDist = 10.0f;
// 공격 사정거리
public float attackDist = 2.0f;
// 몬스터의 사망 여부
public bool isDie = false;
// 컴포넌트의 캐시를 처리할 변수
private Transform monsterTr;
private Transform playerTr;
private NavMeshAgent agent;
private Animator anim;
private readonly int hashTrace = Animator.StringToHash("IsTrace");
private readonly int hashAttack = Animator.StringToHash("IsAttack");
private readonly int hashHit = Animator.StringToHash("Hit");
private readonly int hashPlayerDie = Animator.StringToHash("PlayerDie");
private readonly int hashSpeed = Animator.StringToHash("Speed");
private readonly int hashDie = Animator.StringToHash("Die");
// 혈흔 효과 프리팹
private GameObject bloodEffect;
// 몬스터 생명 변수
private int hp = 100;
// 스크립트가 활성화될 때마다 호출되는 함수
private void OnEnable()
{
...생략
}
// 스크립트가 비활성화될 때마다 호출되는 함수
private void OnDisable()
{
...생략
}
private void Start()
{
...생략
}
// 일정한 간격을 몬스터의 행동 상태를 체크
IEnumerator CheckMonsterState()
{
while(!isDie)
{
// 0.3초 동안 대기하는 동안 제어권을 메세지 루프에 양보
yield return new WaitForSeconds(0.3f);
// 몬스터의 상태가 DIE일 때 코루틴을 종료
if (state == State.DIE) yield break;
// 몬스터와 주인공 캐릭터 사이의 거리 측정
float distance = Vector3.Distance(playerTr.position, monsterTr.position);
// 공격 사정거리 범위로 들어왔는지 확인
if (distance <= attackDist)
{
state = State.ATTACK;
}
//추적 사정거리 범위로 들어왔는지 확인
else if (distance <= traceDist)
{
state = State.TRACE;
}
else{
state = State.IDLE;
}
}
}
IEnumerator MonstorAction()
{
while(!isDie)
{
switch(state){
// Idle 상태
case State.IDLE:
// 추적 중지
agent.isStopped = true;
// Animator의 IsTrace 변수를 false로 설정
anim.SetBool(hashTrace, false);
break;
// 추적 상태
case State.TRACE:
// 추적 대상의 좌표로 이동 시작
agent.SetDestination(playerTr.position);
agent.isStopped = false;
// Animator의 IsTrace 변수를 true로 설정
anim.SetBool(hashTrace, true);
// Animator의 IsAttack 변수를 false로 설정
anim.SetBool(hashAttack, false);
break;
// 공격 상태
case State.ATTACK:
// Animator의 IsAttack 변수를 true로 설정
anim.SetBool(hashAttack, true);
break;
// 사망
case State.DIE:
isDie = true;
//추적 정지
agent.isStopped = true;
//사망 애니메이션 실행
anim.SetTrigger(hashDie);
// 몬스터의 Collider 컴포넌트 비활성화
GetComponent<CapsuleCollider>().enabled = false;
break;
}
yield return new WaitForSeconds(0.3f);
}
}
private void OnCollisionEnter(Collision coll)
{
if(coll.collider.CompareTag("BULLET"))
{
// 충돌한 총알을 삭제
Destroy(coll.gameObject);
// 피격 애니메이션 실행
anim.SetTrigger(hashHit);
//총알의 충돌 지점
Vector3 pos = coll.GetContact(0).point;
// 총알의 충돌 지점의 법선 벡터
Quaternion rot = Quaternion.LookRotation(-coll.GetContact(0).normal);
// 혈흔 효과를 생성하는 함수 호출
ShowBloodEffect(pos, rot);
// 몬스터의 hp 차감
hp -= 10;
if(hp <= 0)
{
state = State.DIE;
}
}
}
void ShowBloodEffect(Vector3 pos, Quaternion rot)
{
...생략
}
void OnPlayerDie()
{
...생략
}
private void OnDrawGizmos()
{
..생략
}
}
몬스터의 생명변수를 선언하고 초깃값은 100으로 설정
// 몬스터 생명 변수
private int hp = 100;
CheckMonsterState 코루틴 함수에서 몬스터의 상태가 DIE이면 상태를 체크하는 로직을 수행하지 않고 yield break를 사용해 종료
// 몬스터의 상태가 DIE일 때 코루틴을 종료
if (state == State.DIE) yield break;
Transition은 위와 같이 연결하고 속성도 설정해준다.
Root Transform Position
실행하면 몬스터가 공중에서 죽는 모습을 확인할 수 있다.
Monster 모델의 Animation에서 Root Transform Position(Y)의 Bake Into Pose를 체크한다.
'Unity 강의 > Unity Course(2) - 절대강좌! 유니티' 카테고리의 다른 글
[Unity Course 2] 07. 유니티 UI 시스템 2 (0) | 2024.06.22 |
---|---|
[Unity Course 2] 07. 유니티 UI 시스템 1 (0) | 2024.06.22 |
[Unity Course 2] 06. 적 캐릭터 제작 5 (0) | 2024.06.22 |
[Unity Course 2] 06. 적 캐릭터 제작 4 (0) | 2024.06.21 |
[Unity Course 2] 06. 적 캐릭터 제작 3 (0) | 2024.06.21 |