using System;
using System.Collections;
using System.Threading;
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; // 방향을 변경할 시간
[SerializeField]
private GameObject hitFxPrefab;
public Action onHit;
private bool isBeingDamaged = false;
private enum MonsterState
{
Idle,
Damaged,
run
}
private MonsterState currentState = MonsterState.Idle;
private void Start()
{
animator = GetComponent<Animator>();
player = GameObject.FindWithTag("Player");
agent = GetComponent<NavMeshAgent>();
StartCoroutine(MonsterBehavior());
}
private IEnumerator TransitionToIdle()
{
// "Damaged"는 피격 애니메이션의 이름입니다. Animator에서 사용하는 정확한 이름을 입력하십시오.
yield return StartCoroutine(WaitForAnimation("GetHit", () =>
{
this.animator.SetInteger("state", 0);
isBeingDamaged = false;
}));
}
//private IEnumerator MonsterBehavior()
//{
// float distanceToPlayer = Vector3.Distance(player.transform.position, transform.position);
// //if (distanceToPlayer <= attackRange) //monster state= 0: Idle, 1: Damaged, 2: run
// //{
// // isAttacking = true;
// // //animator.SetInteger("state", 1); // attack 애니메이션 재생
// // LookAtPlayer(); // 플레이어를 바라봄
// // agent.SetDestination(transform.position); // 몬스터는 현재 위치에 머무름
// //}
// //else
// if (distanceToPlayer <= detectRange)
// {
// animator.SetInteger("state", 0);
// LookAtPlayer();
// //agent.SetDestination(player.transform.position);
// agent.SetDestination(transform.position);
// }
// //else
// //{
// // animator.SetInteger("state", 2);
// // if (timeToChangeDirection <= 0)
// // {
// // MoveRandomly();
// // timeToChangeDirection = UnityEngine.Random.Range(1, 5);
// // yield return new WaitForSeconds(timeToChangeDirection);
// // }
// // timeToChangeDirection -= Time.deltaTime;
// //}
// yield return null;
//}
private IEnumerator MonsterBehavior()
{
while (true)
{
if (!isBeingDamaged)
{
float distanceToPlayer = Vector3.Distance(player.transform.position, transform.position);
if (distanceToPlayer <= detectRange)
{
animator.SetInteger("state", 0);
LookAtPlayer();
agent.SetDestination(transform.position);
}
}
yield return null;
}
}
public void OnDamaged()
{
isBeingDamaged = true;
currentState = MonsterState.Damaged;
animator.SetInteger("state", 1);
if (hitFxPrefab)
{
Instantiate(hitFxPrefab, transform.position + new Vector3(0, 1f, 0), Quaternion.identity);
}
StartCoroutine(TransitionToIdle());
}
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 = UnityEngine.Random.insideUnitSphere * wanderRadius; // 무작위 방향 계산
randomDirection += transform.position; // 현재 위치에 더함
NavMeshHit hit;
NavMesh.SamplePosition(randomDirection, out hit, wanderRadius, 1); // 무작위 위치 계산
return hit.position; // 무작위 위치를 반환
}
public void HitDamage()
{
Debug.Log("피해 애니메이션을 실행합니다.");
if (this.onHit != null)
this.onHit();
this.animator.SetInteger("state", 1);
//StartCoroutine(TransitionToIdle());
}
private IEnumerator WaitForAnimation(string animationName, Action callback)
{
bool isAnimationRunning = true;
// 현재 애니메이터의 상태를 얻습니다.
while (isAnimationRunning)
{
if (animator.GetCurrentAnimatorStateInfo(0).IsName(animationName))
{
if (animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0f)
{
isAnimationRunning = false;
}
}
yield return null;
}
callback();
}
}
using System;
using System.Collections;
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 MonsterController monster;
private bool isParticlePlaying = false;
[SerializeField]
private GameObject hitFxPrefab;
private void Awake()
{
camera = Camera.main; // 메인 카메라
}
private void Start()
{
animator = GetComponent<Animator>();
StartCoroutine(DogBehavior());
}
private IEnumerator DogBehavior()
{
while (true)
{
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")
{
Debug.Log("hit Monaster to ray!");
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);
// onHit 설정
monster = monsterTarget.GetComponent<MonsterController>();
Debug.Log(monster.gameObject.name);
//monster.onHit = () => {
// Debug.Log("이펙트 생성");
// Vector3 offset = new Vector3(0, 0.5f, 0);
// Vector3 tpos = monster.transform.position + offset;
// Debug.LogFormat("생성위치: {0}", tpos);
// // 프리팹 인스턴스(복사본) 생성
// GameObject fxGo = Instantiate(this.hitFxPrefab);
// // 위치 설정
// fxGo.transform.position = tpos;
// // 파티클 실행
// fxGo.GetComponent<ParticleSystem>().Play();
//};
}
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(); // 움직이는 함수 호출
yield return null;
}
}
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);
}
public void TriggerAttack()
{
Debug.Log("TriggerAttack Start!");
if (monsterTarget != null)
{
MonsterController monsterController = monsterTarget.GetComponent<MonsterController>();
Debug.Log(monsterController);
Debug.Log("monsterController Attached!");
if (monsterController != null)
{
Debug.Log("monsterCOntroller != null!");
monsterController.OnDamaged();
}
}
}
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);
//monster.HitDamage();
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;
}
}
}
반응형
'KDT > 유니티 기초' 카테고리의 다른 글
[SimpleRPG] 보스 씬 전환 + 보스 탐지 범위와 공격 범위 구현 (0) | 2023.08.14 |
---|---|
[SimpleRPG] 몬스터 클릭시 사망, 사망+이동 애니메이션, 아이템 드롭과 장착 (0) | 2023.08.10 |
[SimpleRPG] 플레이어 이동과 공격 + 몬스터 공격시 피격 구현 (0) | 2023.08.09 |
[Apple Catch] High Score 구현 (0) | 2023.08.07 |
[Apple Catch] Apple Catch 제작 (0) | 2023.08.07 |