MonsterController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
namespace Test2
{
public class MonsterController : MonoBehaviour
{
public enum eState
{
Idle, GetHit = 2
}
[SerializeField]
public float radius = 1f;
private Animator anim;
private eState state;
private float hitTime = 0.43316f;
public Action onHit;
public float Radius
{
get
{
return this.radius;
}
}
// Start is called before the first frame update
void Start()
{
this.anim = GetComponent<Animator>();
}
public void HitDamage()
{
this.StartCoroutine(this.CoWaitForCompleteHitDamageAnimation());
}
private IEnumerator CoWaitForCompleteHitDamageAnimation()
{
yield return null; // 다음 프레임 시작
Debug.Log("피격 애니메이션이 끝날때까지 기다림");
AnimatorStateInfo animStateInfo = this.anim.GetCurrentAnimatorStateInfo(0);
bool IsHitState = animStateInfo.IsName("GetHit");
Debug.LogFormat("IsHitState: {0}", IsHitState);
if (IsHitState)
{
Debug.LogFormat("animStateInfo.length: {0}", animStateInfo.length);
}
else
{
Debug.Log("Hit상태가 아닙니다.");
}
yield return new WaitForSeconds(this.hitTime);
this.PlayAnimation(eState.GetHit);
//대리자 호출
this.onHit();
yield return new WaitForSeconds(animStateInfo.length - (this.hitTime * 2));
this.PlayAnimation(eState.Idle);
}
private void PlayAnimation(eState state)
{
this.anim.SetInteger("State", (int)state);
}
private void OnDrawGizmos()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(this.transform.position, this.radius);
}
}
}
HeroController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Test2
{
public class HeroController : MonoBehaviour
{
public enum eState
{
Idle, Attack
}
private Animator anim;
[SerializeField]
private float radius = 1f;
private eState state;
private float impactTime = 0.39984f;
private MonsterController target;
public float Radius
{
get
{
return this.radius;
}
}
// Start is called before the first frame update
void Start()
{
this.anim = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
}
public void Attack(MonsterController target)
{
this.target = target;
//공격 애니메이션을 1회 실행
//애니메이터 컴포넌트가 필요함
//this.anim.SetInteger("State", (int)eState.Attack); // 정수 -> enum
this.PlayAnimation(eState.Attack);
}
private void PlayAnimation(eState state)
{
//중복막기
if(this.state != state)
{
//this.state : 현재, state : 미래
Debug.LogFormat("{0} -> {1}", this.state, state);
this.state = state;
this.anim.SetInteger("State", (int)state);
switch(state)
{
case eState.Attack:
this.StartCoroutine(this.CoWaitForCompleteAttackAnimation());
break;
}
}
else
{
Debug.LogFormat("{0}은 현재상태와 같은 State입니다.", state);
}
}
//코루틴
//IEnumerator 반환타입을 갖는 1개 이상의 yield return을 포함하는 함수
//Update와 동일하게 동작 가능
private IEnumerator CoWaitForCompleteAttackAnimation()
{
yield return null; //다음 프레임의 시작
Debug.Log("공격 애니메이션이 끝날때까지 기다림");
//layerIndex : 0
AnimatorStateInfo animStateInfo = this.anim.GetCurrentAnimatorStateInfo(0);
bool isAttackState = animStateInfo.IsName("Attack01");
Debug.LogFormat("IsAttackState: {0}", isAttackState);
if (isAttackState)
{
Debug.LogFormat("animStateInfo.length: {0}", animStateInfo.length);
}
else
{
Debug.Log("Attack State가 아닙니다.");
}
//this.WaitForCompleteAnimation(0.833f);
yield return new WaitForSeconds(this.impactTime);
Debug.Log("Impact");
yield return new WaitForSeconds(animStateInfo.length - this.impactTime);
//Idle 애니메이션을 함
this.PlayAnimation(eState.Idle);
}
private void OnDrawGizmos()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(this.transform.position, this.radius);
}
}
}
Test_PlayerAttackSceneMain
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Test2
{
public class Test_PlayerAttackSceneMain : MonoBehaviour
{
[SerializeField]
private Button btnAttack;
[SerializeField]
private HeroController heroController;
[SerializeField]
private MonsterController monsterController;
[SerializeField]
private GameObject hitFxPrefab;
// Start is called before the first frame update
void Start()
{
//이벤트 등록
this.monsterController.onHit = () => {
Debug.Log("이펙트 생성");
Vector3 offset = new Vector3(0, 0.5f, 0);
Vector3 tpos = this.monsterController.transform.position + offset;
Debug.LogFormat("생성위치: {0}", tpos);
//프리팹 인스턴스 생성
GameObject fxGo = Instantiate(this.hitFxPrefab);
//위치 설정
fxGo.transform.position = tpos;
//파티클 실행
fxGo.GetComponent<ParticleSystem>().Play();
};
//이벤트 등록
//익명 (람다)
this.btnAttack.onClick.AddListener(() =>{
//사거리 체크
//두점과의 거리, 두 오브젝트의 radius의 합
//B-A : C (방향벡터), magnitude (벡터의 길이)
//MonsterController컴포넌트가 붙어있는 게임오브젝트의 Transform 컴포넌트의 Position속성 (Vector3)
//Vector3 b = monsterController.gameObject.transform.position;
//Vector3 b = monsterController.gameObject.GetComponent<Transform>().position;
Vector3 b = this.monsterController.transform.position;
Vector3 a = this.heroController.transform.position;
Vector3 c = b - a; //방향벡터 [정규화되지 않은] , 방향과 크기 가지고있음, 크기 : 길이
// --------------> (방향 -> , 크기 --------------)
float distance = c.magnitude;
Debug.LogFormat("distance: {0}", distance);
Debug.DrawLine(a, b, Color.red, 5f);
//단위벡터 (길이가 1인 벡터)
//Vector3 normal = c.normalized; // 방향 의미
//시작위치, 방향, 몇초, 색, 화살표타입
//DrawArrow.ForDebug(a, c, 5f, Color.red, ArrowType.Solid);
//DrawArrow.ForDebug(a, normal, 5f, Color.red, ArrowType.Solid);
//Vector3.Distance
//두 컴포넌트의 radius (반지름)의 합
float sumRadius = this.heroController.Radius + this.monsterController.Radius;
Debug.LogFormat("radius: {0}", sumRadius);
Debug.LogFormat("{0}, {1}", distance, sumRadius);
Debug.LogFormat("IsWithinRange: {0}", this.IsWithinRange(distance, sumRadius));
if(this.IsWithinRange(distance, sumRadius))
{
//HeroController에게 공격을 명령
this.heroController.Attack(this.monsterController);
this.monsterController.HitDamage();
}
});
}
//사거리 체크
private bool IsWithinRange(float distance, float radius)
{
//distance > radius; // false
return radius >= distance; // true
}
public void TestAttack()
{
Debug.Log("공격");
}
}
}
ParticleDestroyer
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ParticleDestroyer : MonoBehaviour
{
private ParticleSystem ps;
private float elapsedTime = 0f;
// Start is called before the first frame update
void Start()
{
this.ps = GetComponent<ParticleSystem>();
this.StartCoroutine(this.CoWaitForPlayFaterDestroy());
//this.ps.duration이후 씬에서 제거
//Destroy(this.gameObject);
}
//IEnumerator 반환하는 함수, 1개 이상의 yield return을 포함하는 함수
private IEnumerator CoWaitForPlayFaterDestroy()
{
//프레임경과 기다렸다가
yield return new WaitForSeconds(this.ps.main.duration);
//파괴
Destroy(this.gameObject);
}
// Update is called once per frame
void Update()
{
this.elapsedTime += Time.deltaTime;
if(this.elapsedTime >= this.ps.main.duration)
{
Destroy(this.gameObject);
}
}
}
MonsterController 부분에서 코루틴으로 피격 애니메이션 소환하는게 어려웠다.
애니메이션에서도 보면 Idle로 거북이가 돌아오는게 뭔가 비정상적인데 animStateInfo.length - this.hitTime 하면 될까 싶었지만 그렇게 해도 느리길래 this.hitTime * 2 를 해줬더니 매끄럽게 GetHit에서 Idle 애니메이션으로 잘 이동했다.
'유니티 기초' 카테고리의 다른 글
몬스터 동적으로 데이터관리 생성 (0) | 2023.08.10 |
---|---|
Real SimpleRPG (0) | 2023.08.09 |
SimpleRPG (0) | 2023.08.09 |
이펙트 (0) | 2023.08.08 |
몬스터 클릭해서 공격 (0) | 2023.08.08 |