using UnityEngine;
public class EnemyFSM : MonoBehaviour
{
private enum EnemyState { Idle, Move, Attack, Return, Damaged, Die }
private EnemyState m_State;
private Transform player;
private CharacterController cc;
public float findDistance = 8f;
public float attackDistance = 3f;
public float moveSpeed = 5f;
private float currentTime = 0f;
private float attackDelay = 2f;
public int attackPower = 3;
public int hp = 15;
private Vector3 originPos;
public float moveDistance = 20f;
void Start()
{
m_State = EnemyState.Idle;
player = GameObject.Find("Player").transform;
cc = GetComponent<CharacterController>();
originPos = transform.position;
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
}
void Update()
{
switch (m_State)
{
case EnemyState.Idle:
Idle();
break;
case EnemyState.Move:
Move();
break;
case EnemyState.Attack:
Attack();
break;
case EnemyState.Return:
Return();
break;
case EnemyState.Damaged:
// Damaged();
break;
case EnemyState.Die:
// Die();
break;
}
}
private void Idle()
{
if (Vector3.Distance(transform.position, player.position) < findDistance)
{
m_State = EnemyState.Move;
Debug.Log("상태 전환 : Idle -> Move");
}
}
private void Move()
{
if (Vector3.Distance(transform.position, originPos) > moveDistance)
{
m_State = EnemyState.Return;
Debug.Log("상태 전환 : Move -> Return");
}
else if (Vector3.Distance(transform.position, player.position) > attackDistance)
{
Vector3 dir = (player.position - transform.position).normalized; // 플레이어 쪽으로 가는 방향 구하기
cc.Move(dir * moveSpeed * Time.deltaTime);
}
else
{
currentTime = attackDelay;
m_State = EnemyState.Attack;
Debug.Log("상태 전환 : Move -> Attack");
}
}
private void Attack()
{
if (Vector3.Distance(transform.position, player.position) < attackDistance)
{
currentTime += Time.deltaTime;
if (currentTime > attackDelay)
{
currentTime = 0f;
player.GetComponent<FPSPlayerMove>().DamageAction(attackPower);
Debug.Log("공격!");
}
}
else
{
currentTime = 0f;
m_State = EnemyState.Move;
Debug.Log("상태 전환 : Attack -> Move");
}
}
private void Return()
{
if (Vector3.Distance(transform.position, originPos) > 0.1f)
{
Vector3 dir = (originPos - transform.position).normalized;
cc.Move(dir * moveSpeed * Time.deltaTime);
}
else
{
transform.position = originPos;
hp = 15;
m_State = EnemyState.Idle;
Debug.Log("상태 전환 : Return -> Idle");
}
}
public void HitEnemy(int hitPower)
{
if (m_State == EnemyState.Damaged || m_State == EnemyState.Die || m_State == EnemyState.Return)
return;
hp -= hitPower;
if (hp > 0)
{
m_State = EnemyState.Damaged;
Debug.Log("상태 전환 : Any State -> Damaged");
Damaged();
}
else
{
m_State = EnemyState.Die;
Debug.Log("상태 전환 : Any State -> Die");
Die();
}
}
private void Damaged()
{
StartCoroutine(DamageProcess());
}
IEnumerator DamageProcess()
{
yield return new WaitForSeconds(0.5f);
m_State = EnemyState.Move;
Debug.Log("상태 전환 : Damged -> Move");
}
private void Die()
{
StopAllCoroutines();
StartCoroutine(DieProcess());
}
IEnumerator DieProcess()
{
cc.enabled = false;
yield return new WaitForSeconds(2f);
Debug.Log("Enemy 소멸");
Destroy(gameObject);
}
}
public class FPSPlayerMove : MonoBehaviour
{
public int hp = 20;
public void DamageAction(int damage)
{
hp -= damage;
}
}
using UnityEngine;
public class FPSPlayerFire : MonoBehaviour
{
public int weaponPower = 5;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (Physics.Raycast(ray, out hitInfo))
{
if (hitInfo.transform.gameObject.layer == LayerMask.NameToLayer("Enemy")) // Enemy 감지
{
EnemyFSM eFSM = hitInfo.transform.GetComponent<EnemyFSM>();
eFSM.HitEnemy(weaponPower);
}
else // Enemy가 아닌 경우 피격 이펙트 플레이
{
bulletEffect.transform.position = hitInfo.point; // 피격 이펙트가 부딪힌 대상의 위치로 이동
bulletEffect.transform.forward = hitInfo.normal;
ps.Play(); // 피격 이펙트 플레이
}
}
}
}
}
2. 체력바
using UnityEngine.UI;
public class FPSPlayerMove : MonoBehaviour
{
private int maxHp = 20;
public Slider hpSlider;
public void DamageAction(int damage)
{
hp -= damage;
hpSlider.value = (float)hp / (float)maxHp;
if (hp > 0)
{
StartCoroutine(PlayHitEffect());
}
}
}
using UnityEngine.UI;
public class EnemyFSM : MonoBehaviour
{
public int maxHp = 15;
public Slider hpSlider;
void Update()
{
hpSlider.value = (float)hp / (float)maxHp;
}
}
using UnityEngine;
public class Billboard : MonoBehaviour
{
public Transform target;
void Update()
{
transform.forward = Camera.main.transform.forward;
}
}
3. 피격 이펙트
using System.Collections;
public class FPSPlayerMove : MonoBehaviour
{
public GameObject hitEffect;
public void DamageAction(int damage)
{
if (hp > 0)
{
StartCoroutine(PlayHitEffect());
}
}
IEnumerator PlayHitEffect()
{
Debug.Log("히트이펙트");
hitEffect.SetActive(true);
yield return new WaitForSeconds(0.3f);
hitEffect.SetActive(false);
}
}
4. 게임 매니저
using System.Collections;
using TMPro;
using UnityEngine;
public class FPSGameManager : Singleton<FPSGameManager>
{
public enum GameState { Ready, Run, GameOver }
public GameState gState;
public GameObject gameLabel;
private TextMeshProUGUI gameText;
private FPSPlayerMove player;
void Start()
{
gState = GameState.Ready;
gameText = gameLabel.GetComponent<TextMeshProUGUI>();
gameText.text = "Ready...";
gameText.color = new Color32(255, 185, 0, 255);
player = GameObject.Find("Player").GetComponent<FPSPlayerMove>();
StartCoroutine(ReadyToStart());
}
void Update()
{
if (player.hp <= 0)
{
gameLabel.SetActive(true);
gameText.text = "Game Over";
gameText.color = new Color32(255, 0, 0, 255);
gState = GameState.GameOver;
}
}
IEnumerator ReadyToStart()
{
yield return new WaitForSeconds(2f);
gameText.text = "Go!";
yield return new WaitForSeconds(0.5f);
gameLabel.SetActive(false);
gState = GameState.Run;
}
}
public class FPSPlayerMove : MonoBehaviour
{
void Update()
{
if (FPSGameManager.Instance.gState != FPSGameManager.GameState.Run)
return;
}
}
// PlayerFire, PlayerRotate, CamRotate에도 조건문 추가해주기