유니티 기초
간단 RPG 2-1 (몬스터 피격 애니메이션)
EasyCastleT
2023. 8. 8. 23:21
Test_PlayerControlSceneMain
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Test;
public class Test_PlayerControlSceneMain : MonoBehaviour
{
[SerializeField]
private HeroController heroController;
// Start is called before the first frame update
void Start()
{
this.heroController.onMoveComplete = (target) =>{
Debug.Log("<color=cyan>이동을 완료 했습니다.</color>");
//타겟이 있다면 공격 애니메이션 실행
if(target != null){
this.heroController.Attack(target);
}
};
}
// Update is called once per frame
void Update()
{
//화면을 클릭하면 클릭한 위치로 Hero가 이동
if (Input.GetMouseButtonDown(0))
{
//Ray를 만든다
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float maxDistance = 1000f;
//화면에 출력
Debug.DrawRay(ray.origin, ray.direction * maxDistance, Color.red, 2f);
//충돌검사
RaycastHit hit;
if (Physics.Raycast(ray, out hit, maxDistance))
{
//클릭한 오브젝트가 몬스터라면
if(hit.collider.tag == "Monster")
{
//거리를 구한다
float distance =Vector3.Distance(this.heroController.gameObject.transform.position,
hit.collider.gameObject.transform.position);
MonsterController monsterController =
hit.collider.gameObject.GetComponent<MonsterController>();
//각 반지름 더한거와 비교
float sumRadius = this.heroController.radius + monsterController.radius;
Debug.LogFormat("<color=green>distance:{0}, sumRadius:{1}</color>",
distance, sumRadius);
//사거리 안에 들어옴
if(distance <= sumRadius)
{
//공격
}
else
{
//이동
this.heroController.Move(monsterController);
}
}
else if(hit.collider.tag =="Ground")
{
//충돌정보가 hit 변수에 담김
Debug.Log(hit.point); //월드 상의 충돌 지점 위치
//Hero Gameobject 이동
this.heroController.Move(hit.point);
}
}
}
}
}
HeroController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Test
{
public class HeroController : MonoBehaviour
{
private Vector3 targetPosition; //타겟이 되는 위치
private Coroutine moveRoutine; //전에 실행하던 코루틴을 저장하는 변수
private Animator anim; //Hero의 Animator
public System.Action<MonsterController> onMoveComplete;
[SerializeField]
public float radius=1.0f; //Hero 사거리
private MonsterController target; //target이 된 몬스터의 MonsterController 컴포넌트
// Start is called before the first frame update
void Start()
{
this.anim = this.GetComponent<Animator>();
}
public void Move(MonsterController target)
{
this.target = target;
this.targetPosition = this.target.gameObject.transform.position;
this.anim.SetInteger("State", 1);
if (this.moveRoutine != null)
{
//이미 코루틴이 실행중이다 -> 중지
this.StopCoroutine(this.moveRoutine);
}
this.moveRoutine = this.StartCoroutine(this.CoMove());
}
public void Move(Vector3 targetPosition)
{
//타겟을 지움
this.target = null;
//이동할 목표 지점을 저장
this.targetPosition = targetPosition;
Debug.Log("Move");
//이동 애니메이션 실행
this.anim.SetInteger("State", 1);
if (this.moveRoutine != null)
{
//이미 코루틴이 실행중이다 -> 중지
this.StopCoroutine(this.moveRoutine);
}
this.moveRoutine = StartCoroutine(this.CoMove());
}
private IEnumerator CoMove()
{
while (true)
//무한 반복이 되어 유니티가 멈출 수도 있음
//그러므로 yield return 필수
{
//방향을 바라봄
this.transform.LookAt(targetPosition);
//이미 바라봤으니깐 정면으로 이동 (relateTo: Self/지역좌표)
//방향 * 속도 * 시간
this.transform.Translate(Vector3.forward * 2f * Time.deltaTime);
//목표지점과 나 사이의 거리를 계산, 즉 1프레임 마다 거리를 계산
float distance = Vector3.Distance(this.transform.position, this.targetPosition);
//타겟이 있을 경우
if (this.target != null)
{
if(distance <= (1f + 1f))
{
break;
}
}
else
{
if (distance <= 0.1f)
{
//도착
break;
}
}
yield return null; //다음 프레임 시작
}
Debug.Log("<color=yellow>도착!</color>");
this.anim.SetInteger("State", 0);
//대리자 호출
this.onMoveComplete(this.target);
}
private void OnDrawGizmos()
{
GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, 1, 40);
}
public void Attack(MonsterController target)
{
this.anim.SetInteger("State", 2);
//코루틴 함수 호출
this.StartCoroutine(this.CoAttack());
}
private IEnumerator CoAttack()
{
//Attack 애니메이션의 0.35초에서 Impact가 발생
yield return new WaitForSeconds(0.35f); // 0.38초 이후
//밑에 주석문과 같은 효과
//float elapsedTime = 0;
//while (true) {
// elapsedTime += Time.deltaTime;
// if (elapsedTime > 0.1f) {
// break;
// }
// yield return null;
//}
Debug.Log("<color=red>Impact!!!!!!!!!!</color>");
this.target.GetHit();
//0.83 - 0.38 => 0.83은 Hero의 Attack 애니메이션의 길이
yield return new WaitForSeconds(0.48f);
Debug.Log("Attack 애니메이션 종료");
this.anim.SetInteger("State", 0); //기본동작으로 전환
}
}
}
MonsterController
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonsterController : MonoBehaviour
{
public float radius = 1;
private Animator anim;
public int hp;
// Start is called before the first frame update
void Start()
{
this.anim = this.GetComponent<Animator>();
this.hp = 3;
}
// Update is called once per frame
void Update()
{
if (this.hp <= 0)
{
this.hp = 0;
this.anim.SetInteger("MonsterState", 1); //사망
}
else
{
this.anim.SetInteger("MonsterState", 0); //대기
}
}
//이벤트 함수
private void OnDrawGizmos()
{
GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, 1, 40);
}
public void GetHit()
{
this.StartCoroutine(this.CoGetHit());
}
//코루틴
private IEnumerator CoGetHit()
{
//GetHit 애니메이션의 0.14초에서 피격이 발생
yield return new WaitForSeconds(0.01f);
Debug.Log("<color=red>GetHit!!!!!!!!!!</color>");
this.anim.SetInteger("MonsterState", 2);
this.hp--;
Debug.LogFormat("<color=yellow>monster.hp: {0}</color>" , this.hp);
// 0.6 - 0.14 => 0.6은 GetHit 애니메이션의 길이
yield return new WaitForSeconds(0.59f);
Debug.Log("GetHit 애니메이션 종료");
this.anim.SetInteger("MonsterState", 0); //기본동작으로 전환
}
public void SetHp(int hp)
{
this.hp = hp;
}
public int GetHp()
{
return this.hp;
}
public Animator GetAnim()
{
return this.anim;
}
}