유니티 기초

[합치기] 이동, 공격, 피격

다모아 2023. 8. 11. 18:05

 

처음에 this.targetPosition을 넣어놓지 않아서 Hero캐릭터가 몬스터를 중간에 누르면 가던 와중에 공격을 해서 왜 그런지 몰랐고 머리 아팠는데 저거를 넣어놓지 않아서 그랬던 것이었다.

 

공격을 한번만 해서 왜 그러지.. 했는데 Move가 끝나고 target이 있으면 공격하는 대리자를 입력하나만 해놔서 움직이지 않으면 공격도 안했었다.

 

그래서 거리에 들어왔을 때 공격 메서드를 넣어줘서 움직이지 않아도 누르면 공격하게 되었다.

 

float distance부터 아래 4줄을 if (distance <= 2f 랑 0.1f) 안에다 넣어놨었는데 밖에다 놔둬도 break문으로 나오게하면 되서 불필요한 코드를 안쓸 수 있었다.

 

GameEnums

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Real
{
    public class GameEnums
    {
        public enum eHeroType
        {
            Idle, Run, Attack
        }

        public enum eMonsterType
        {
            Idle, Hit, Die
        }
    }
}

 

HeroController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

namespace Real
{
    public class HeroController : MonoBehaviour
    {
        [SerializeField]
        private float radius = 1f;
        private float moveSpeed = 3f;
        private float impact = 0.39984f;

        private MonsterController target;

        private GameEnums.eHeroType state;

        private Vector3 targetPosition;

        private Coroutine moveRoutine;
        private Coroutine attackRoutine;

        private Animator anim;

        public Action<MonsterController> onMoveComplete;
        public float Radius
        {
            get
            {
                return this.radius;
            }
        }

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

        //몬스터타겟이 들어오면 거기로 움직이기
        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;

            if(this.moveRoutine != null)
            {
                this.StopCoroutine(this.moveRoutine);
            }
            this.moveRoutine = this.StartCoroutine(this.CoMove());
        }

        //코루틴으로 움직이기
        private IEnumerator CoMove()
        {
            while (true)
            {
                float distance = Vector3.Distance(this.transform.position, this.targetPosition);
                this.transform.LookAt(this.targetPosition);
                this.PlayAnimation(GameEnums.eHeroType.Run);
                this.transform.Translate(Vector3.forward * this.moveSpeed * Time.deltaTime);

                //몬스터 타겟이 있을경우
                if (this.target != null)
                {
                    if (distance <= 2f)
                    {
                        break;
                    }
                }
                else //몬스터 타겟이 없을경우
                {
                    //거리가 0.1f라면
                    if (distance <= 0.1f)
                    {
                        break;
                    }
                }
                yield return null;
            }
            this.PlayAnimation(GameEnums.eHeroType.Idle);
            this.onMoveComplete(this.target);
        }

        //공격
        public void Attack(MonsterController target)
        {
            if (this.attackRoutine != null)
            {
                this.StopCoroutine(this.attackRoutine);
            }
            this.attackRoutine = this.StartCoroutine(this.CoAttack());

        }

        //코루틴 공격
        private IEnumerator CoAttack()
        {
            this.PlayAnimation(GameEnums.eHeroType.Attack);

            yield return new WaitForSeconds(this.impact);
            Debug.Log("impact");

            AnimatorStateInfo animStateInfo = this.anim.GetCurrentAnimatorStateInfo(0);
            yield return new WaitForSeconds(animStateInfo.length);

            this.PlayAnimation(GameEnums.eHeroType.Idle);

        }

        //애니메이션 멤버메서드
        private void PlayAnimation(GameEnums.eHeroType state)
        {
            if(this.state != state)
            {
                this.state = state;
                this.anim.SetInteger("State", (int)state);
            }
        }

        private void OnDrawGizmos()
        {
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, 1, 40);
        }
    }
}

 

Real_SimpleRpgSceneMain

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static UnityEngine.GraphicsBuffer;

namespace Real
{
    public class Real_SimpleRpgSceneMain : MonoBehaviour
    {
        [SerializeField]
        private HeroController heroController;
        private MonsterController monsterController;
        // Start is called before the first frame update
        void Start()
        {
            this.heroController.onMoveComplete = (target) => {
                //타겟이 있다면
                if (target != null)
                {
                    this.heroController.transform.LookAt(target.transform);
                    this.heroController.Attack(target);
                    this.monsterController.HitDamage();
                }
            };
        }

        // Update is called once per frame
        void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 2f);

                RaycastHit hit;
                if(Physics.Raycast(ray, out hit, 100f)) {
                    //몬스터라면
                    if (hit.collider.tag == "Monster")
                    {
                        MonsterController monsterController = hit.collider.gameObject.GetComponent<MonsterController>();
                        this.monsterController = monsterController;
                        //두 원점사이의 거리
                        float distance = Vector3.Distance(this.heroController.transform.position, hit.collider.gameObject.transform.position);
                        //반지름의 합
                        float sumRadius = this.heroController.Radius + this.monsterController.Radius;
                        Debug.LogFormat("distance, sumRadius: {0}, {1}", distance, sumRadius);
                        Debug.LogFormat("hit.collider.tag : {0}", hit.collider.tag);
                        //distance <= sumRadius 가 true라면
                        if (distance <= sumRadius)
                        {
                            this.heroController.Attack(this.monsterController);
                            this.monsterController.HitDamage();
                        }
                        else 
                        {
                            this.heroController.Move(this.monsterController);
                        }
                    }
                    //땅이라면
                    else if (hit.collider.tag == "Ground")
                    {
                        Debug.LogFormat("hit.collider.tag : {0}", hit.collider.tag);
                        this.heroController.Move(hit.point);
                    }
                }
            }
        }
    }
}

 

MonsterController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Real
{
    public class MonsterController : MonoBehaviour
    {
        [SerializeField]
        private float radius = 1f;
        private float impact = 0.39984f;

        private Coroutine hitRoutine;

        private Animator anim;

        private GameEnums.eMonsterType state;

        public float Radius
        {
            get
            {
                return this.radius;
            }
        }

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

        public void HitDamage()
        {
            if (this.hitRoutine != null)
            {
                this.StopCoroutine(this.hitRoutine);
            }
            this.hitRoutine = this.StartCoroutine(this.CoWaitCompleteHitDamage());
        }

        private IEnumerator CoWaitCompleteHitDamage()
        {
            yield return null;

            yield return new WaitForSeconds(this.impact);
            this.PlayAnimation(GameEnums.eMonsterType.Hit);

            AnimatorStateInfo animStateInfo = this.anim.GetCurrentAnimatorStateInfo(0);
            yield return new WaitForSeconds(animStateInfo.length - this.impact * 2);
            this.PlayAnimation(GameEnums.eMonsterType.Idle);

        }
        public void PlayAnimation(GameEnums.eMonsterType state)
        {
            if (this.state != null)
            {
                this.state = state;
                this.anim.SetInteger("State", (int)state);
            }
        }
        private void OnDrawGizmos()
        {
            GizmosExtensions.DrawWireArc(this.transform.position, this.transform.forward, 360, 1, 40);
        }
    }
}