EasyCastleUNITY

간단 RPG 2-1 (몬스터 피격 애니메이션) 본문

유니티 기초

간단 RPG 2-1 (몬스터 피격 애니메이션)

EasyCastleT 2023. 8. 8. 23:21

다시 확인해보니, 0.35초 쯤에서 실질적인 impact가 발생하여 조금 수정하였다.

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;
    }
}

실행결과
슬로우

'유니티 기초' 카테고리의 다른 글

간단 RPG Test2-1  (0) 2023.08.09
코드 베이스로 버튼 실행  (0) 2023.08.09
간단 RPG 2  (0) 2023.08.08
간단 RPG  (0) 2023.08.08
유니티 코루틴  (0) 2023.08.08