유니티 심화

HeroShooter - 몬스터를 발견하면 공격애니메이션 실행

다모아 2023. 8. 25. 18:20

 

PlayerController

onAttack 대리자만 추가해줘서 몬스터가 인식할 때 대리자를 Main쪽에 부를 수 있게 해주었다.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class PlayerController : MonoBehaviour
{
    //캐릭터 움직임 속도
    [SerializeField]
    private float moveSpeed = 5f;
    //애니메이션
    private Animator anim;
    //포탈태그했을 때
    public Action reachPortal;
    //문에 닿았을 때
    public Action reachDoor;
    //rayOffsetY
    private float rayOffsetY = 0.5f;
    //공격대리자
    public Action onAttack;
    void Start()
    {
        this.anim = this.GetComponent<Animator>();
    }

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

    }

    public void Move(Vector3 moveDir)
    {
        //달리는 애니메이션 실행
        this.PlayAnimation(EnumState.ePlayerState.RunF);
        //Mathf.Atan2와 Mathf.Rad2Deg를 이용해서 회전시키기
        float angle = Mathf.Atan2(moveDir.x, moveDir.z) * Mathf.Rad2Deg;
        //AngleAxis를 이용하기
        Quaternion q = Quaternion.AngleAxis(angle, Vector3.up);
        //캐릭터에 회전 적용
        this.transform.rotation = q;
        //캐릭터 움직이기
        this.transform.Translate(Vector3.forward * this.moveSpeed * Time.deltaTime);
    }
    public void PlayAnimation(EnumState.ePlayerState state)
    {
        this.anim.SetInteger("State", (int)state);
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.tag == "Door")
        {
            //대리자 사용해서 씬 전환하기
            this.reachDoor();
        }
        else if (collision.collider.tag == "Portal")
        {
            //대리자 사용해서 문 열고, 포탈 제거하기
            this.reachPortal();
        }
    }

    private void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(this.transform.position, 30f);
    }

    public void MonsterRay(Vector3 dir, MonsterController target)
    {
        //레이로 플레이어의 살짝 위에서 몬스터까지의 거리 찍기
        Ray ray = new Ray(this.transform.position + (Vector3.up * this.rayOffsetY), dir);
        //그려보기
        Debug.DrawRay(ray.origin, ray.direction * 100f, Color.green);
        //layerMask.NameToLayer로 레이어 구별해주기
        var layerMask = 1 << LayerMask.NameToLayer("Monster") | 1 << LayerMask.NameToLayer("Car") | 1 << LayerMask.NameToLayer("Wall");
        RaycastHit hit;
        if(Physics.Raycast(ray, out hit, 100f, layerMask))
        {
            Debug.Log(hit.collider.tag);

            if(hit.collider.CompareTag("Monster"))
            {
                if(target != null)
                {
                    target.HideIndicator();
                }
                var monsterController = hit.collider.gameObject.GetComponent<MonsterController>();
                monsterController.ShowIndicator();
                this.onAttack();
                target = monsterController;
            }
            else
            {
                MonsterController[] monsterContorllers = GameObject.FindObjectsOfType<MonsterController>();
                Debug.Log(monsterContorllers.Count());

                foreach (MonsterController monster in monsterContorllers)
                {
                    monster.HideIndicator();
                }
                target = null;
            }
        }
    }
}

 

Stage1SceneMain

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

public class Stage1SceneMain : MonoBehaviour
{
    //플레이어
    [SerializeField]
    private PlayerController playerController;
    //몬스터
    [SerializeField]
    private MonsterController target;
    //조이스틱
    [SerializeField]
    private CJoystick joystick;
    //왼쪽 문
    [SerializeField]
    private DoorController leftDoorController;
    //오른쪽 문
    [SerializeField]
    private DoorController rightDoorController;
    //페이드인
    [SerializeField]
    private Image dim;
    //NPC 텍스트
    [SerializeField]
    private Text text;
    //TargetQuad
    [SerializeField]
    private GameObject targetQuadGo;
    //반지름
    private float radius = 30f;
    //딕셔너리
    private Dictionary<GameObject, float> dic = new Dictionary<GameObject, float>();
    //캐릭터가 움직이는 좌표
    private Vector3 moveDir;
    // Start is called before the first frame update
    void Start()
    {
        //씬 전환시 페이드인
        this.StartCoroutine(this.CoFadeInAndSceneManager());
        //포탈에 닿았을 때
        this.playerController.reachPortal = () =>
        {
            //포탈 삭제하고
            //Destroy(this.portalGo.gameObject);
            //문 열기
            //this.leftDoorController.OpenDoor();
            //this.rightDoorController.OpenDoor();
        };

        //문에 닿았을 때
        this.playerController.reachDoor = () =>
        {
            //씬 전환하기
            //this.FadeOutAndSceneManager();
        };

        //공격실행
        this.playerController.onAttack = () => {
            if (moveDir == Vector3.zero)
            {
                this.playerController.PlayAnimation(EnumState.ePlayerState.Attack);
            }
        };
    }

    // Update is called once per frame
    void Update()
    {
        //수평, 수직 좌표 구하고 moveDir에 넣기
        float h = this.joystick.Horizontal;
        float v = this.joystick.Vertical;
        this.moveDir = new Vector3(h, 0, v);
        //moveDir이 Vector3.zero일 경우
        if (moveDir == Vector3.zero)
        {
            //정지 애니메이션 실행
            this.playerController.PlayAnimation(EnumState.ePlayerState.Idle);
        }
        else // (0,0,0)이 아닐 경우
        {
            //this.IsAttack = false;
            this.playerController.Move(moveDir);
        }
        //OverlapSphere로 몬스터 인식하기
        int layerMask = 1 << LayerMask.NameToLayer("Monster");
        Collider[] hitColliders = Physics.OverlapSphere(this.playerController.transform.position, this.radius, layerMask);

        this.dic.Clear();
        foreach (Collider cols in hitColliders)
        {
            //딕셔너리에 거리차이와 게임오브젝트 추가
            this.dic.Add(cols.gameObject, Vector3.Distance(cols.transform.position, this.playerController.transform.position));
        }

        //만약 dic.Count가 0보다 크다면
        if (dic.Count > 0)
        {
            //작은 값을 short_distance에 넣기
            float short_distance = this.dic.Values.Min();
            //몬스터한 키값 넣어주기
            var target = this.dic.FirstOrDefault(x => x.Value == short_distance).Key;
            //dir에 몬스터와 플레이어의 거리 빼준 값 넣기
            Vector3 dir = this.target.transform.position - this.playerController.transform.position;
            this.playerController.MonsterRay(dir, this.target);
        }
    }

    private IEnumerator CoFadeInAndSceneManager()
    {
        Color color = this.dim.color;
        while (true)
        {
            color.a -= 0.01f;
            this.dim.color = color;

            if (color.a <= 0)
            {
                break;
            }
            yield return null;
        }
    }
}

Start쪽에 공격애니메이션을 실행시켜주었다.


남은 할 일 :

LookAt으로 공격애니메이션할 때 가까운 몬스터 바라보기

카메라 버그 수정하기 [ z축으로 -6.8f 오면 멈추고 플레이어가 뒤로 와도 카메라가 이동안함 ]

Stage1Scene에 text추가해주기

Stage1Scene문 통과하면 다음챕터가기