using System.Collections;
using UnityEngine;
public class Boss : EnemyBase
{
public GameObject bossBulletPrefab;
public Transform firePos;
public float fireForce = 10f;
public int bossHP;
protected Animator anim;
public BossState bossState = BossState.Idle;
public float detectionRadius = 50.0f;
public LayerMask playerLayer;
private GameObject detectedPlayer = null;
public float attackRange = 5.0f;
public float moveSpeed = 1.0f;
private float curruntMoveSpeed;
public int magicCoolTime;
public float rotationSpeed = 5.0f;
public SkinnedMeshRenderer skinnedMeshRenderer;
public Material objectMaterial;
public Material redMaterial;
private bool isHit = false;
public enum BossState
{
Idle,
Walk,
Attack,
MagicAttack,
Die
}
void Start()
{
curruntMoveSpeed = moveSpeed;
anim = GetComponent<Animator>();
targetPoint.SetActive(false);
magicCoolTime = 0;
StartCoroutine("MagicCoolTime");
StartCoroutine("EnemyAnimation");
}
protected override void Update()
{
//base.Update();
PlayerDetect();
if(bossHP <= 0)
{
bossState = BossState.Die;
}
else
{
if (bossState == BossState.Walk && detectedPlayer != null)
{
float distanceToPlayer = Vector3.Distance(transform.position, detectedPlayer.transform.position);
MoveTowardsPlayer(distanceToPlayer);
}
}
}
IEnumerator MagicCoolTime()
{
magicCoolTime = 0;
while (true)
{
yield return new WaitForSeconds(1);
magicCoolTime++;
// magicCoolTime이 3초 이상이고, 플레이어가 감지되었을 때 MagicAttack 상태로 변경
if (magicCoolTime >= 3 && detectedPlayer != null)
{
bossState = BossState.MagicAttack;
}
}
}
void PlayerDetect()
{
if (GameManager.Instance.PlayerHP <= 0)
{
detectedPlayer = null;
bossState = BossState.Idle;
return;
}
Collider[] detectedPlayers = Physics.OverlapSphere(transform.position, detectionRadius, playerLayer);
if (detectedPlayers.Length > 0)
{
detectedPlayer = detectedPlayers[0].gameObject;
float distanceToPlayer = Vector3.Distance(transform.position, detectedPlayer.transform.position);
if (distanceToPlayer > attackRange)
{
// 플레이어가 감지되었지만 magicCoolTime 조건에 따라 MagicAttack 상태가 이미 설정되었는지 확인
if (bossState != BossState.MagicAttack)
{
bossState = BossState.Walk;
}
}
else
{
bossState = BossState.Attack;
}
}
else
{
detectedPlayer = null;
bossState = BossState.Idle;
}
}
void MoveTowardsPlayer(float distanceToPlayer)
{
Vector3 directionToPlayer = (detectedPlayer.transform.position - transform.position).normalized;
directionToPlayer.y = 0;
transform.position += directionToPlayer * curruntMoveSpeed * Time.deltaTime;
RotateTowardsPlayer();
}
void MagicAttack()
{
Debug.Log("MagicAttack 호출");
FireAtPlayer();
}
public void FireAtPlayer() //애니메이션 이벤트
{
// 적의 중앙을 향한 방향 계산
Vector3 playerCenter = detectedPlayer.transform.position + new Vector3(0, detectedPlayer.GetComponent<Collider>().bounds.extents.y, 0); //player의 position + Enemy의 콜라이더 높이 절반
Vector3 fireDirection = (playerCenter - firePos.position).normalized;
// 총알 방향을 설정
Quaternion fireRotation = Quaternion.LookRotation(fireDirection);
fireRotation *= Quaternion.Euler(90, 0, 0); // X축으로 90도 회전을 추가
GameObject bossBullet = Instantiate(bossBulletPrefab, firePos.position, fireRotation);
Rigidbody rb = bossBullet.GetComponent<Rigidbody>();
if (rb != null)
{
rb.AddForce(fireDirection * fireForce, ForceMode.Impulse);
}
}
void PlayerAttackDamage()
{
float distanceToPlayer = Vector3.Distance(transform.position, detectedPlayer.transform.position);
if (distanceToPlayer <= attackRange)
{
GameManager.Instance.PlayerHP--;
Debug.LogFormat("PlayerHP : {0}", GameManager.Instance.PlayerHP);
}
}
void RotateTowardsPlayer()
{
if (detectedPlayer != null)
{
Vector3 lookDirection = (detectedPlayer.transform.position - transform.position).normalized;
lookDirection.y = 0;
Quaternion lookRotation = Quaternion.LookRotation(lookDirection);
transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * rotationSpeed);
}
}
private IEnumerator GetHit()
{
skinnedMeshRenderer.material = redMaterial;
yield return new WaitForSeconds(0.1f);
skinnedMeshRenderer.material = objectMaterial;
isHit = false;
}
protected override void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Bullet"))
{
bossHP--;
Destroy(other.gameObject);
if (bossHP <= 0)
{
bossState = BossState.Die;
}
else
{
StartCoroutine("GetHit");
}
}
}
private void OnDrawGizmos()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(this.transform.position, detectionRadius);
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(this.transform.position, attackRange);
}
protected override IEnumerator EnemyAnimation()
{
while (true)
{
switch (bossState)
{
case BossState.Idle:
curruntMoveSpeed = 0;
anim.SetInteger("state", 0);
break;
case BossState.Walk:
curruntMoveSpeed = moveSpeed;
anim.SetInteger("state", 1);
break;
case BossState.Attack:
anim.SetInteger("state", 2);
curruntMoveSpeed = 0;
RotateTowardsPlayer();
yield return new WaitUntil(() => anim.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f && anim.GetCurrentAnimatorStateInfo(0).IsName("Attack"));
// 애니메이션이 완료된 후 Player의 위치를 확인
if (detectedPlayer != null)
{
float distanceToPlayer = Vector3.Distance(transform.position, detectedPlayer.transform.position);
// Player가 공격 범위 밖으로 이동했다면 상태를 Walk로 변경
bossState = distanceToPlayer > attackRange ? BossState.Walk : BossState.Attack;
}
else
{
bossState = BossState.Walk;
//anim.SetInteger("state", 1);
}
break;
case BossState.MagicAttack:
StopCoroutine("MagicCoolTime");
anim.SetInteger("state", 3);
curruntMoveSpeed = 0;
yield return new WaitUntil(() => anim.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f && anim.GetCurrentAnimatorStateInfo(0).IsName("MagicAttack"));
bossState = BossState.Walk;
StartCoroutine("MagicCoolTime");
//anim.SetInteger("state", 1);
break;
case BossState.Die:
curruntMoveSpeed = 0;
anim.SetInteger("state", 4);
Destroy(this.GetComponent<BoxCollider>());
targetPoint.SetActive(false);
yield return new WaitUntil(() => anim.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f && anim.GetCurrentAnimatorStateInfo(0).IsName("Die"));
yield return new WaitForSeconds(1);
Destroy(this.gameObject);
yield break; // Boss가 사망한 경우 코루틴 종료
}
yield return null;
}
}
}
Boss.cs
using System.Collections;
using UnityEngine;
public abstract class EnemyBase : MonoBehaviour
{
public GameObject targetPoint;
public int enemyHP;
protected Animator animator;
public EnemyState enemyState = EnemyState.Idle;
public enum EnemyState
{
Idle,
Walk,
GetHit,
Die,
Attack
}
protected virtual void Start()
{
animator = GetComponent<Animator>();
targetPoint.SetActive(false);
StartCoroutine(EnemyAnimation());
}
protected virtual void Update()
{
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
if (stateInfo.IsName("GetHit") && stateInfo.normalizedTime >= 1.0f)
{
enemyState = EnemyState.Idle;
}
}
protected virtual void OnTriggerEnter(Collider other)
{
if (animator == null) return;
if (other.gameObject.CompareTag("Bullet"))
{
enemyHP--;
Destroy(other.gameObject);
if (enemyHP <= 0)
{
enemyState = EnemyState.Die;
}
else
{
if (enemyState != EnemyState.GetHit)
{
enemyState = EnemyState.GetHit;
animator.Play("GetHit", -1, 0f);
}
}
}
}
protected abstract IEnumerator EnemyAnimation();
}
EnemyBase.cs
반응형
'산대특 > 게임 플랫폼 응용 프로그래밍' 카테고리의 다른 글
탱크 시즈모드 Splash 공격 구현하기 (0) | 2024.02.29 |
---|---|
탱크 공격 과 폭발 이펙트 + 적 삭제 구현하기 (0) | 2024.02.29 |
탱크 이동과 모드 전환 구현하기 (0) | 2024.02.29 |
[HeroShooter -> Zombero] Stage4 NavMesh 경로 개선 (0) | 2024.02.27 |
[Zombero] 시작 스테이지 (Start) (0) | 2024.02.27 |