유니티 기초

SimpleRPG Test

다모아 2023. 8. 9. 16:01

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