몬스터 컨트롤러
- AI 에이전트에 따라 움직임
- 상태에 따라 애니메이션 동작하기
- 총알에 맞았을때 HP감소
- 사망했을때 블러드 효과 발생시키기
- 플레이어가 사망했을때 이벤트 동작 실행
Tag설정 / Layer 설정
Monster : Monster / MonsterBody
Monster Lwrist .Rwrist : Punch / MonsterPunch
Edit ->> Project Settings >>충돌 설정 해제하기
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class MonsterCtrl : MonoBehaviour
{
public enum State
{
IDLE, TRACE, ATTACK, DIE //ctrl +shift+ U 대문자로 변경
}
public State state = State.IDLE;
public float traceDist = 10f; //추적사정거리
public float attackDist = 2f;
public bool isDie = false;
private GameObject bloodEffect;
private int hp=100;
private Transform playerTrans;
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 void OnEnable()
{
//이벤트 리스너를 등록 한다
PlayerCtrl.OnPlayerDie += this.OnPlayerDie;
}
void Start()
{
agent = GetComponent<NavMeshAgent>();
//플레이어를 찾는다 (태그로)
playerTrans = GameObject.FindWithTag("Player").transform;
//이동한다
agent.SetDestination(playerTrans.position);
anim = GetComponent<Animator>();
//프리팹을 로드한다
this.bloodEffect = Resources.Load<GameObject>("BloodSprayEffect");
//몬스터의 상태를 체크 하는 코루틴
StartCoroutine(CheckMonsterState());
//상태에 따라서 몬스터의 행동을 수행 하는 코루틴
StartCoroutine(MonsterAction());
}
IEnumerator MonsterAction()
{
while (!this.isDie) //죽지 않았을 경우만
{
switch (this.state)
{
case State.IDLE:
agent.isStopped = true; //추적을 중지
this.anim.SetBool(hashTrace, false);
break;
case State.TRACE:
//추적대상 좌표로 이동한다
agent.SetDestination(this.playerTrans.position);
this.anim.SetBool(hashTrace, true);
this.anim.SetBool(hashAttack, false);
agent.isStopped = false; //추적을 재게
break;
case State.ATTACK:
this.anim.SetBool(hashAttack, true);
break;
case State.DIE:
this.isDie = true;
agent.isStopped = true;
anim.SetTrigger(hashDie);
GetComponent<CapsuleCollider>().enabled = false;
break;
}
yield return new WaitForSeconds(0.3f); //0.3초 주기로 행동을 함
}
}
IEnumerator CheckMonsterState()
{
while (!this.isDie) //죽지 않았을 경우
{
yield return new WaitForSeconds(0.3f);
if(state ==State.DIE)
{
yield break; // 코루틴 즉시 종료
}
//거리 측정
float distance = Vector3.Distance(transform.position, playerTrans.position);
if(distance<=attackDist) //공격 사거리에 들어왔다면
{
state = State.ATTACK;
}
else if(distance<=traceDist) //시야에 들어왔다면
{
state = State.TRACE;
}
else //공격 사거리에도 안들어오고 시야에도 없다
{
state=State.IDLE;
}
}
}
private void OnCollisionEnter(Collision collision)
{
//총알에 충돌했다면
if (collision.collider.CompareTag("Bullet") ){
Destroy(collision.gameObject); //총알 제거
anim.SetTrigger(hashHit);
//충돌 지점을 가져옴
ContactPoint cp = collision.GetContact(0);
Vector3 pos = cp.point;
//충돌지점의 법선벡터
Quaternion rot= Quaternion.LookRotation(-cp.normal);
//이펙트 생성
this.ShowBloodEffect(pos,rot);
hp -= 10;
if (hp <= 0)
{
state = State.DIE;
}
}
}
private void ShowBloodEffect(Vector3 pos, Quaternion rot)
{
GameObject blood = Instantiate(this.bloodEffect,pos,rot,this.transform);
Destroy(blood,1f );
}
private void OnDrawGizmos()
{
if(state==State.TRACE)
{
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(this.transform.position, traceDist);
}
if (state == State.ATTACK)
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(this.transform.position, attackDist);
}
}
public void OnPlayerDie()
{
this.StopAllCoroutines();
agent.isStopped = true;
anim.SetTrigger(hashPlayerDie);
anim.SetFloat(hashSpeed, UnityEngine.Random.Range(0.8f, 1.2f)); ;
}
private void OnDisable()
{
PlayerCtrl.OnPlayerDie -= this.OnPlayerDie;
}
}
플레이어 수정
- 플레이어가 사망했을때
- 대리자 호출
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCtrl : MonoBehaviour
{
public float movespeed=1f;
public float turnspeed=1f;
private Animation anim;
private readonly float inithp = 100f;
public float currenthp;
public delegate void PlayerDieHandler();
public static event PlayerDieHandler OnPlayerDie;
private void Awake()
{
//첫번째 호출
}
private void OnEnable()
{
//두번째 호출
}
IEnumerator Start()
{
//코루틴으로 호출 가능
// IEnumerator Start()
anim = GetComponent<Animation>();
anim.Play("Idle");
this.turnspeed = 0;
yield return new WaitForSeconds(0.3f);
this.turnspeed = 10f;
currenthp = inithp;
}
private void FixedUpdate()
{
//일정간격으로 호출
}
void Update()
{
//transform.Translate(Vector3.forward * speed * Time.deltaTime); //로컬좌표 기준으로,정면으로 이동
//transform.Translate(Vector3.forward * 0.01f,Space.World); //월드좌표 기준으로,회전한 상태로 이동
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 dir = new Vector3(h,0, v);
transform.Translate(dir.normalized * movespeed * Time.deltaTime);
float r = Input.GetAxis("Mouse X"); //회전방향
transform.Rotate(Vector3.up * turnspeed * r);
PlayerAnim( h, v);
}
private void LateUpdate()
{
}
private void PlayerAnim(float h, float v)
{
if (v >= 0.1f)
{
this.anim.CrossFade("RunF", 0.25f);
}
else if(v<= -0.1f)
{
this.anim.CrossFade("RunB", 0.25f);
}
else if (h >= 0.1f)
{
this.anim.CrossFade("RunR", 0.25f);
}
else if (h <= -0.1f)
{
this.anim.CrossFade("RunL", 0.25f);
}
else
{
this.anim.CrossFade("Idle", 0.25f);
}
}
private void OnTriggerEnter(Collider other)
{
if(currenthp>0 && other.CompareTag("Punch"))
{
this.currenthp -= 10f;
Debug.LogFormat("HP : {0}/{1}" ,currenthp,inithp);
}
if (currenthp <= 0)
{
PlayerDie();
this.anim.CrossFade("Die", 0.25f);
}
}
private void PlayerDie()
{
Debug.Log("플레이어 다이");
/* GameObject[] monsters = GameObject.FindGameObjectsWithTag("Monster");
foreach (GameObject monster in monsters)
{
monster.SendMessage("OnPlayerDie",SendMessageOptions.DontRequireReceiver);
}*/
PlayerCtrl.OnPlayerDie(); //대리자 호출
}
}
'게임개발 > 게임엔진 응용 프로그래밍' 카테고리의 다른 글
절대강좌 유니티 (2) 총 발사 (0) | 2024.03.13 |
---|---|
절대강좌 유니티 (1) 플레이어와 카메라의 이동 (0) | 2024.03.12 |