주인공이 죽으면 PlayerDie()::PlayerCtrl 함수에서 씬뷰의 모든 몬스터들을 찾아 SendMessage를 이용 하나하나 메세지를 보냈다. 만일 적캐릭터가 엄청 많다면 그닥 효율적인 방법이 아니다.
void PlayerDie() {
//MONSTER 태그를 가진 모든 게임오브젝트를 찾아옴
GameObject[] monsters = GameObject.FindGameObjectsWithTag("MONSTER");
foreach(GameObject monster in monsters) { //모든 오브젝트를 순차적으로 불러옴
monster.SendMessage("OnPlayerDie", SendMessageOptions.DontRequireReceiver);
}
}
순차적 호출 방식을 이벤트 구동방식으로 변경하자. 주인공이 죽었다는 이벤트를 시스템이 통보해주는 방식이다.
델리게이트
델리게이트는 함수(메서드)를 참조하는 변수를 의미한다. C++의 함수 포인터와 같은 의미이다. 사용하기전 아래와 같이 델리게이션 타일을 선언후 델리게이트타입을 이용 변수를 만들고 함수와 연결후 사용해야 한다.
using UnityEngine;
public class DelegateDemo : MonoBehavior
{
//델리게이트 타입선언
delegate float SumHandler(float a, float b);
//델리게이트타입 델리게이트변수
SumHandler sumHandler;
//덧셈연산을 하는 함수
float sum(float a, float b) {
return a + b;
}
void Start() {
sumHandler = sum; //델리게이트변수에 함수를 연결
float sum = sumHandler(10.0f, 5.0f);
Dubug.Log($"Sum = {sum}"};
}
}
주인공의 사망 이벤트 처리
PlayerCtrl() 함수내 델리게이트와 이벤트를 선언한다.
public delegate void PlayerDieHandler(); //델리게이트타입 선언
public static event PlayerDieHandler OnPlayerDie; //델리게이트 변수선언
PlayerCtrl() 함수내 PlayerDie()함수 내용을 지우고 OnPlayerDie()함수로 이벤트를 콜한다
void PlayerDie() {
//MONSTER 태그를 가진 모든 게임오브젝트를 찾아옴
/*
GameObject[] monsters = GameObject.FindGameObjectsWithTag("MONSTER");
foreach(GameObject monster in monsters) { //모든 오브젝트를 순차적으로 불러옴
monster.SendMessage("OnPlayerDie", SendMessageOptions.DontRequireReceiver);
}
*/
OnPlayerDie(); //주인공 사망 이벤트 호출(발생)
}
이제 MonsterCtrl를 변경한다. 우선 애니메이터의 "Die" 파라미터 해시값을 추출해 놓는다.
몬스트의 생명치를 저장할 hp변수를 만들어 놓는다.
private readonly int hashDie = Animator.StringToHash("Die"); //파라미터해시값 추출
private int hp = 100; //몬스터 생명점수
이제 MonsterCtrl에서 발생하는 이벤트에 반응할 OnPlayerDie()함수를 Player.OnPlayerDie와 연결한다.
PlayerCtrl는 스크립트이름인데 선언없이 사용가능하다. 교재는 PlayerCtrl이다. 변경하려고 했는데 자잘한 에러때문에 그냥 쓴다.
이벤트 스크립트는 반드시 스크립트의 활성화 시점에 연결하고 비활성화될때 해제해야 한다.
MonsterCtrl Start()함수 앞에 OnEnable() OnDisable()함수를 추가한다.
private int hp = 20; //몬스터 생명점수
private void OnEnable() { //스크립트가 활성활 될때 콜백되는 함수
PlayerCtrl.OnPlayerDie += this.OnPlayerDie; //교재는 PlayerCtrl.
}
private void OnDisable() { //스크립트가 활성활 될때 콜백되는 함수
PlayerCtrl.OnPlayerDie -= this.OnPlayerDie; //교재는 PlayerCtrl.
}
몬스터의 사망처리
CheckMonsterState()에 state.DIE일 경우 코루틴을 빠져나가는 조건문을 추가한다.
IEnumerator CheckMonsterState() {
while(!isDie) {
yield return new WaitForSeconds(0.3f);
if(state == State.DIE) yield break; //몬스터 상태가 DIE면 코루틴 종료
OnCollisionEnter()함수에 충돌이 났을때 hp를 -10을 감소시키고 state를 DIE로 바꿔준다.
private void OnCollisionEnter(Collision coll) {
if (coll.collider.CompareTag("BULLET")) {
//중략
hp -= 10;
if(hp <= 0) state = State.DIE;
}
}
state가 DIE로 변경되면 MonsterAction()코루틴에서 "DIE" 트리거가 일어나고 Collider가 비활성화돼 죽은 몬스터에 총을 발사했을때 혈흔이 일어나지 않는다.
IEnumerator MonsterAction() {
while (!isDie) {
//중략
case State.DIE: //사망상태
isDie= true;
agent.isStopped = true; //추적 정지
anim.SetTrigger(hashDie); // 애니메이트 hasDie 트리거발생
GetComponent<CapsuleCollider>().enabled = false; //충돌컴포넌트 비활성화
break;
}
yield return new WaitForSeconds(0.3f);
}
}
Monster의 die 애니메이션 클립을 MonsterAnim 애니메이터뷰에 추가하고 Any State->Die State로 Transition을 만든다. 조건으로 사용할 파라미터는 Trigger타입으로 die를 추가하고 설정한다. Die State는 마지막 스테이트인 Exit스테이트로 연결하고 각 Transition의 연결조건은 다음과 같다.
실행해보면 죽은 몬스터가 떠 있다. 0.5 모델/Monster폴더의 Monster의 Die 애니메이션의 프리뷰를 봐도 떠 있다.
05.Models>Monster>Monster모델 원본을 눌러 인스펙터에서
Animation 탭을 눌러 die애니메이션을 골라 프리뷰를 봐도 공중에 떠 있다. 몬스터 아래 원이 피봇위치이다. 피봇위치는 Transform.position이다
아래쪽 Root Transform Position(Y)의 Bake Into Pose를 체크하던지 Based Upon값을 Feed로 설정하면 Pivot좌표값이 발위치로 조정된다.
실행하려면 방금 몬스터 모델 원형을 변경했기 때문에 Apply를 물어보면 적용시킨다. 실행해보면 잘된다.
전체파일을 원하시는 분은 출판사의 깃허브에서 다운받으시길.
https://github.com/IndieGameMaker/SpaceShooter2021/releases/tag/6%EC%9E%A5
'유니티게임강좌 > 적 캐릭터 제작' 카테고리의 다른 글
[Enemy제작] 몬스터 공격 중지 (0) | 2023.03.09 |
---|---|
[Enemy제작] 본 구조의 최적화 (0) | 2023.03.09 |
[Enemy제작] 특정 레이어 간의 충돌 감지 (0) | 2023.03.08 |
[Enemy제작] 적 캐릭터의 공격 능력 (0) | 2023.03.08 |
[Enemy제작] Player - 자동 회전 (0) | 2023.03.08 |