유니티 기초

유닛 움직여서 공격 / 몬스터 피격 / 사망

송현호 2023. 8. 9. 18:17
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace GameMain
{
    public class PlayerAttackSceneMain : MonoBehaviour
    {

        [SerializeField]
        private Button btnAttack;
        [SerializeField]
        private HeroController heroController;
        [SerializeField]
        private MonsterController monsterController;
        [SerializeField]
        private GameObject hitFxPrefab;
        [SerializeField]
        private GameObject grave;


        // Start is called before the first frame update
        void Start()
        {
            this.heroController.onMoveComplete = (target) =>
            {
                Debug.LogFormat("<color=cyan>이동을 완료 했습니다. : {0}</color>", target);
                //타겟이 있다면 공격 애니메이션 실행
                if (target != null)
                {
                    this.heroController.Attack(target);
                }
            };

            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.monsterController.onDie = () =>
            {
                GameObject grave = Instantiate(this.grave);
                grave.transform.position = monsterController.transform.position;

            };

            this.btnAttack.onClick.AddListener(() => {
                Vector3 a = heroController.gameObject.transform.position;
                Vector3 b = monsterController.gameObject.transform.position;
                Vector3 c = b - a;
                //시작위치, 방향
                float distance = c.magnitude;
                float radius = this.heroController.Radius + this.monsterController.Radius;
                Debug.LogFormat("radius: {0}", radius);
                Debug.LogFormat("IsWithinRange: <color=lime>{0}</color>", this.IsWithinRange(distance, radius));
                if (this.IsWithinRange(distance, radius))
                {
                    this.heroController.Attack(this.monsterController);
                }

            });
        }

        private bool IsWithinRange(float distance, float radius)
        {
            return radius > distance;
        }
        // Update is called once per frame
        void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                float maxDistance = 100f;
                Debug.DrawRay(ray.origin, ray.direction * maxDistance, Color.red, 3f);

                //Debug.LogFormat("-> {0}", this.transform.position == null);
                //if (this.transform.position == null)
                //{ 

                //}

                //this.transform.position = null;


                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("{0}, {1}", distance, sumRadius);

                        //사거리 안에 들어옴 
                        if (distance <= sumRadius)
                        {
                            //공격 
                        }
                        else
                        {
                            //이동 
                            this.heroController.Move(monsterController);
                            //this.heroController.Move(hit.point);
                        }

                    }
                    else if (hit.collider.tag == "Ground")
                    {
                        this.heroController.Move(hit.point);
                    }
                }
            }
        }

    }
}
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using System;

namespace GameMain
{
    public class MonsterController : MonoBehaviour
    {
        [SerializeField]
        private float radius = 1f;
        private float hp = 40;
        public float Radius
        {
            get { return this.radius; }

        }
        private Animator anim;

        public Action onHit;
        public Action onDie;
        private enum eState
        {
            Idle, Hit, Die
        }
        eState state;
        // Start is called before the first frame update
        void Start()
        {
            this.anim = this.GetComponent<Animator>();
        }

        // Update is called once per frame
        void Update()
        {

        }

        public void HitDamage(int atk)
        {
            this.hp -= atk;

            if (this.hp <= 0)
            {
                this.StartCoroutine(this.WaitForCompleteDieAnimation());
            }
            else
            {
                Debug.Log("피해를 입었습니다");
                this.onHit();
                this.anim.SetInteger("State", (int)eState.Hit);
            }
        }

        private IEnumerator WaitForCompleteDieAnimation()
        {
            this.anim.SetInteger("State", (int)eState.Die);
            yield return new WaitForSeconds(1.66f);
            this.onDie();
            Destroy(this.gameObject);

        }
        private void OnDrawGizmos()
        {
            Gizmos.color = Color.yellow;
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, this.radius);
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace GameMain
{
    public class HeroController : MonoBehaviour
    {
        public enum eState
        {
            Idle, Attack, Attack2
        }
        private Vector3 targetPosition;
        private MonsterController target;
        public System.Action<MonsterController> onMoveComplete;
        private Coroutine moveRoutine;
        [SerializeField]
        private eState state;
        private float radius = 1f;
        public float impactTime = 0.4f;
        [SerializeField]
        private MonsterController monsterController;
        private Coroutine attackRoutine;
        private int atk = 10;
        public float Radius
        {
            get { return this.radius; }

        }

        private Animator anim;
        // Start is called before the first frame update
        void Start()
        {
            this.anim = this.GetComponent<Animator>();
        }

        // Update is called once per frame 
        void Update()
        {
        }

        public void Move(MonsterController target)
        {
            this.target = target;
            this.targetPosition = this.target.gameObject.transform.position;
            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");


            if (this.moveRoutine != null)
            {
                //이미 코루틴이 실행중이다 -> 중지 
                this.StopCoroutine(this.moveRoutine);
            }
            this.moveRoutine = this.StartCoroutine(this.CoMove());
        }

        private IEnumerator CoMove()
        {
            while (true)
            {
                //방향을 바라봄 
                this.transform.LookAt(this.targetPosition);
                //이미 바라봤으니깐 정면으로 이동 (relateTo : Self/지역좌표)
                this.transform.Translate(Vector3.forward * 1f * Time.deltaTime);
                //목표지점과 나와의 거리를 잼 
                float distance = Vector3.Distance(this.transform.position, this.targetPosition);
                //Debug.Log(distance);
                //0이 될수 없다 가까워질때 도착한것으로 판단 하자 

                //타겟이 있을경우 
                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);
        }

        public void Attack(MonsterController target)
        {
            this.transform.LookAt(target.transform.position);
            this.PlayAnimation(eState.Attack);
        }

        private void PlayAnimation(eState state)
        {
            if (this.state != state)
            {
                Debug.LogFormat("{0} => {1}", this.state, state);
                this.state = state;
                this.anim.SetInteger("State", (int)state);

                switch (state)
                {
                    case eState.Attack:
                        if (this.attackRoutine != null)
                        {
                            this.StopCoroutine(this.attackRoutine);
                        }
                        this.attackRoutine = this.StartCoroutine(WaitForCompleteAttackAnimation());
                        // this.WaitForCompleteAnimation(0.833f);
                        break;
                }
            }
            else
            {
                if (this.state == eState.Attack)
                {
                    this.state = state;
                    this.anim.SetInteger("State", (int)eState.Attack2);

                }
            }
        }

        private IEnumerator WaitForCompleteAttackAnimation()
        {
            yield return null;

            Debug.Log("공격 애니메이션이 끝날때까지 기다림");
            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.LogFormat("Attack State가 아닙니다");
            }

            yield return new WaitForSeconds(this.impactTime);

            monsterController.HitDamage(this.atk);

            yield return new WaitForSeconds(animStateInfo.length - this.impactTime);
            this.PlayAnimation(eState.Idle);
        }

        private void OnDrawGizmos()
        {
            Gizmos.color = Color.yellow;
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, this.radius);
        }
    }
}