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(큐브 프리팹) 생성 구현
-> 바닥: 노란색 , 몬스터: 빨강색
'KDT > 유니티 기초' 카테고리의 다른 글
[Apple Catch] High Score 구현 (0) | 2023.08.07 |
---|---|
[Apple Catch] Apple Catch 제작 (0) | 2023.08.07 |
[UnityChan] 캐릭터 화면 클릭 시 해당 위치로 이동하기 (0) | 2023.08.04 |
[밤 송이 던지기] (0) | 2023.08.04 |
[Pixel Sword] 캐릭터 동작과 애니메이션 구현 + 캐릭터 위의 땅 충돌시 오류 수정 (0) | 2023.08.04 |