using System;
using UnityEngine;

public class DogController : MonoBehaviour
{
    public float speed = 5.0f; // 캐릭터의 이동 속도
    public float attackRange = 2.0f; // 캐릭터의 공격 범위
    private Animator animator;
    private Camera camera;
    private bool isMove; // 캐릭터가 움직이는지 여부
    private bool isAttacking; // 캐릭터가 공격 중인지 여부
    private Vector3 destination; // 캐릭터의 목적지
    private Transform monsterTarget; // 공격 대상인 몬스터

    public GameObject groundMarkerPrefab; // 지상 마커 프리팹
    public GameObject monsterMarkerPrefab; // 몬스터 마커 프리팹
    private GameObject currentMarker; // 현재 생성된 마커
    public float markerHeight = 1.5f; // 마커 높이

    private void Awake()
    {
        camera = Camera.main; // 메인 카메라
    }

    private void Start()
    {
        animator = GetComponent<Animator>(); 
    }

    private void Update()
    {
        // 우클릭을 하면
        if (Input.GetMouseButton(1))
        {
            RaycastHit hit;
            // 마우스 위치에서 카메라 방향으로 레이캐스트를 발사
            if (Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit))
            {
                // 기존의 마커가 있다면 삭제
                if (currentMarker != null)
                {
                    Destroy(currentMarker);
                }

                // 몬스터에게 레이가 닿았다면
                if (hit.collider.tag == "Monster")
                {
                    monsterTarget = hit.transform;
                    SetDestination(hit.point); // 몬스터 위치로 목적지 설정
                    // 몬스터 마커 생성
                    currentMarker = Instantiate(monsterMarkerPrefab);
                    // 마커를 몬스터 위에 위치시킴
                    currentMarker.transform.position = monsterTarget.position + Vector3.up
                        * (monsterTarget.GetComponent<Collider>().bounds.extents.y
                        + currentMarker.transform.localScale.y + markerHeight);
                }
                else // 지면에 레이가 닿았다면
                {
                    monsterTarget = null;
                    SetDestination(hit.point); // 지면의 위치로 목적지 설정
                    // 지면 마커 생성
                    currentMarker = Instantiate(groundMarkerPrefab);
                    // 마커를 지면 위에 위치시킴
                    currentMarker.transform.position = hit.point + Vector3.up
                        * (currentMarker.transform.localScale.y + markerHeight);
                }
            }
        }

        // 공격 대상이 있다면
        if (monsterTarget != null)
        {
            // 몬스터의 위치로 목적지 재설정
            SetDestination(monsterTarget.position);
            // 마커를 몬스터의 위치로 재설정
            currentMarker.transform.position = monsterTarget.position + Vector3.up
                * (monsterTarget.GetComponent<Collider>().bounds.extents.y
                + currentMarker.transform.localScale.y + markerHeight);
        }

        Move(); // 움직이는 함수 호출
    }

    private void SetDestination(Vector3 dest)  // player state= 0:idle, 1:run,  2: attack
    {
        // 목적지를 설정하고, run 상태로 설정. 공격은 false로
        destination = new Vector3(dest.x, transform.position.y, dest.z);
        isMove = true;
        isAttacking = false;
        animator.SetInteger("state", 1);
    }

    private void Move()
    {
        // run 상태일 때
        if (isMove)
        {
            // 공격 대상이 있고 공격 범위 안에 있다면
            if (monsterTarget != null && Vector3.Distance(monsterTarget.position, transform.position) <= attackRange)
            {
                // 이동 중단
                isMove = false;
                // attack 시작
                isAttacking = true;
                animator.SetInteger("state", 2);
                return;
            }
            else if (Vector3.Distance(destination, transform.position) <= 0.1f)
            {
                // 목적지에 도착
                isMove = false;
                animator.SetInteger("state", 0);
                return;
            }

            // 목적지 방향으로 이동
            Vector3 dir = (destination - transform.position).normalized;
            transform.forward = dir;
            transform.position += dir * Time.deltaTime * speed;
        }
    }
}
using UnityEngine;
using UnityEngine.AI;

public class MonsterController : MonoBehaviour
{
    public float detectRange = 5.0f; // 플레이어 탐지 범위
    public float attackRange = 1.0f; // 공격 범위
    public float wanderRadius = 10f;  // 무작위 이동 범위  
    private Animator animator; 
    private GameObject player; // 플레이어 오브젝트
    private bool isAttacking; // 공격 중인지 여부
    private NavMeshAgent agent;  
    private float timeToChangeDirection = 0; // 방향을 변경할 시간

    private void Start()
    {
        animator = GetComponent<Animator>();
        player = GameObject.FindWithTag("Player"); // "Player" 태그가 있는 오브젝트를 찾아서 저장
        agent = GetComponent<NavMeshAgent>();  
    }

    private void Update()
    {
        float distanceToPlayer = Vector3.Distance(player.transform.position, transform.position); // 플레이어와의 거리

        // 플레이어가 공격 범위 안에 있다면
        if (distanceToPlayer <= attackRange)     //monster state= 0: idle, 1: attack, 3: run
        {
            isAttacking = true;
            animator.SetInteger("state", 1); // attack 애니메이션 재생
            LookAtPlayer(); // 플레이어를 바라봄
            agent.SetDestination(transform.position); // 몬스터는 현재 위치에 머무름
        }
        // 플레이어가 탐지 범위 안에 있다면
        else if (distanceToPlayer <= detectRange)
        {
            isAttacking = false;
            animator.SetInteger("state", 0); // idle 애니메이션 재생
            LookAtPlayer(); // 플레이어를 바라봄
            agent.SetDestination(player.transform.position); // 플레이어에게 이동
        }
        // 그 외의 경우
        else
        {
            isAttacking = false;
            animator.SetInteger("state", 3); // run 애니메이션 재생
            if (timeToChangeDirection <= 0)
            {
                MoveRandomly(); // 무작위로 이동
                timeToChangeDirection = Random.Range(1, 5); // 1 ~ 5 초 뒤에 다시 방향을 변경
            }
            timeToChangeDirection -= Time.deltaTime; // 시간 감소
        }
    }

    private void LookAtPlayer()
    {
        Vector3 direction = (player.transform.position - transform.position).normalized; // 플레이어 방향 계산
        Quaternion lookRotation = Quaternion.LookRotation(new Vector3(direction.x, 0, direction.z)); // 회전값
        transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * 5f); //회전
    }

    private void MoveRandomly()
    {
        Vector3 newPos = GetRandomPosition(); // 새로운 위치를 무작위로 가져옴
        agent.SetDestination(newPos); // 새 위치로 이동을 시작
    }

    private Vector3 GetRandomPosition()
    {
        Vector3 randomDirection = Random.insideUnitSphere * wanderRadius; // 무작위 방향 계산
        randomDirection += transform.position; // 현재 위치에 더함
        NavMeshHit hit;
        NavMesh.SamplePosition(randomDirection, out hit, wanderRadius, 1); // 무작위 위치 계산
        return hit.position; // 무작위 위치를 반환
    }
}

<구현 순서>

1. 캐릭터의 RaycastHit를 통해서 바닥을 클릭시 해당 위치로 이동 구현

2. 몬스터 클릭시 몬스터 객체의 위치 정보를 RaycastHit를 통해 위치정보를 받아 몬스터의 위치로 이동 구현

3. 캐릭터와 몬스터의 애니메이션 설정 및 캐릭터의 공격 범위와 몬스터의 탐지/공격 범위 설정 구현

   ->몬스터의 탐지 범위 안으로 캐릭터가 들어오면 캐릭터를 쫒아가며, 공격범위로 들어오면 정지 후 공격 실행

   ->몬스터 탐지 밖으로 벗어나면 몬스터가 다시 랜덤한 위치로 이동

4. 캐릭터와 몬스터가 만나서 공격시 위치 겹침 문제 해결

   -> 공격 범위내에 들어오면 현재 좌표 설정 후 이동 정지 및 공격 실행

5. 바닥 클릭과 몬스터 클릭시 그 위치에 Marker(큐브 프리팹) 생성 구현

   -> 바닥: 노란색 , 몬스터: 빨강색

 

+ Recent posts