주인공 캐릭터(Player)가 쏜 총알이 몬스터에 명중하면 몬스터가 피를 흘리는 혈흔 효과(Blood Effect)를 추가해보자. 혈은 효과는 파티클로 구현하며 프로젝트뷰의 03.Prefabs/EffecExamples/Blood/Prefabs폴더에 있는 BloodSprayeffect를 사용한다. 버전의 차이인지 발견하지 못해서 아래 EffectExamples > Goop Effects > Prefabs> GoopSpray를 사용하였다.
Resources 폴더
지금까지는 프리팹을 사용하기 위해 public GameObject xxx를 선언하고 인스펙터에서 연결해놓고 사용하였다. 이번에는 스크립트에서 런모드(동적)으로 로드하는 방법을 사용한다. 이방법을 쓰기 위해서는 프리팹이나 에셋이 Resouces 폴더안에 있어야 한다.
Assets root에 Resources 폴더를 하나 만들자. BloodSprayeffect나 GoopSpray를 끌어다 놓는다. 이 Resources폴더는 빌드할때 포함되므로 필요한 것들만 놓기 바랍니다.
MonsterCtrl 스크립트에 다음과 같이 추가한다. Goopspray효과는 피튀는게 좀 작아서 localScale을 변경해 크게했다
- blood.transform.localScale = new Vector3(5, 5, 5); //효과가 작아서 확대했다.
효과의 인스턴스를 만드는데 인수가 4개이다 마지막 monsterTr의 Child로 만들겠다는 뜻이다.
- GameObject blood = Instantiate<GameObject>(bloodEffect, pos, rot, monsterTr);
이렇게 child로 만들면 Monster가 총알을 맞은뒤 이동해도 혈흔이 따라다닌다.
private GameObject bloodEffect;
void Start(){
bloodEffect = Resources.Load<GameObject>("GoopSpray");
}
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);
}
}
void ShowBloodEffect(Vector3 pos, Quaternion rot) {
GameObject blood = Instantiate<GameObject>(bloodEffect, pos, rot, monsterTr);
blood.transform.localScale = new Vector3(5, 5, 5); //효과가 작아서 확대했다.
Destroy(blood, 1.0f);
}
MonsterCtrl 전체코드
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.AI; //for Navigation
public class MonsterCtrl : MonoBehaviour
{
//몬스트 상태
public enum State { //인스펙터뷰에 콤보박스로 표시됨
IDLE, PATROL, TRACE, ATTACK, DIE ,DANCE
}
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;
public readonly int hashTrace = Animator.StringToHash("IsTrace");
public readonly int hashAttack = Animator.StringToHash("IsAttack");
private readonly int hashHit = Animator.StringToHash("Hit");
private GameObject bloodEffect;
void Start()
{
monsterTr= GetComponent<Transform>();
playerTr = GameObject.FindWithTag("PLAYER").GetComponent<Transform>();
agent = GetComponent<NavMeshAgent>();
anim = GetComponent<Animator>();
bloodEffect = Resources.Load<GameObject>("GoopSpray");
StartCoroutine(CheckMonsterState());
StartCoroutine(MonsterAction());
}
//일정한 간격으로 몬스터의 행동 상태를 체크
IEnumerator CheckMonsterState() {
while(!isDie) {
yield return new WaitForSeconds(0.3f);
//Player와 Monster간의 거리측정 스칼라값
float distance = Vector3.Distance(playerTr.position, monsterTr.position);
if(distance <= attackDist) {
state = State.ATTACK; //Attack로 상태변경
} else if(distance <= traceDist) {
state = State.TRACE; //Trace로 상태변경
} else {
state = State.IDLE; //Idle로 상태변경
}
}
}
//몬스터의 상태에 따라 몬스터의 동작을 수행
IEnumerator MonsterAction() {
while (!isDie) {
switch(state) {
case State.IDLE:
agent.isStopped= true; //추적을 중지
anim.SetBool(hashTrace, false);
break;
case State.TRACE:
agent.SetDestination(playerTr.position); //목표설정
agent.isStopped = false; //추적을 재개
anim.SetBool(hashTrace, true);
anim.SetBool(hashAttack, false); //공격파라메터 변경
break;
case State.ATTACK: //공격상태
anim.SetBool(hashAttack, true); //공격파라메터 변경
break;
case State.DIE: //사망상태
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);
}
}
void ShowBloodEffect(Vector3 pos, Quaternion rot) {
GameObject blood = Instantiate<GameObject>(bloodEffect, pos, rot, monsterTr);
blood.transform.localScale = new Vector3(5, 5, 5); //효과가 작아서 확대했다.
Destroy(blood, 1.0f);
}
private void OnDrawGizmos() { //상태에 따라 기즈모 색깔 변경
if(state == State.TRACE) {
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(transform.position, traceDist);
}
if(state == State.ATTACK) {
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, attackDist);
}
}
}
'유니티게임강좌 > 적 캐릭터 제작' 카테고리의 다른 글
[Enemy제작] 적 캐릭터의 공격 능력 (0) | 2023.03.08 |
---|---|
[Enemy제작] Player - 자동 회전 (0) | 2023.03.08 |
[Enemy제작] 유한 상태 머신 구현 (0) | 2023.03.05 |
[Enemy제작] 내비게이션 - 적 캐릭터의 순찰 및 추적 (1) | 2023.03.04 |
[Enemy제작] 유한 상태 머신의 정의 (2) | 2023.03.04 |