상태머신(FSM)
상태(State) : 객체의 행동
전이(Transition) : 상태의 이동
조건(Condition) : 전이하기 위한 조건
Enemy 상태머신
Enemy는 정지 상태로 시작하고 이동 조건이 성립하면 이동 상태로 전이한다.
이동중에 일정 범위 안에 목표물이 들어오면 공격상태로 전이한다.
공격을 계속하지 않고 일정 시간 대기를 한 후 다시 공격한다.
공격중에 목표물이 공격범위에서 벗어나면 다시 이동상태로 전이한다.
어떠한 상태에서든 피격을 당하면 죽음 상태로 전이한다.
변수
public float speed = 1;
public float findDistance = 5;
public float attackDistance = 1.5f;
enum State
{
Idle,
Move,
Attack,
Die,
}
State state;
GameObject target;
float currentTime;
float attackTime = 1;
enum : 열거형 자료형. 들어있는 항목에 순서대로 값을 부여한다.
Start에서 Player를 찾고,
Update에서는 상태만 계속 바뀌도록 해 준다.
// Start is called before the first frame update
void Start()
{
state = State.Idle;
target = GameObject.Find("Player");
}
// Update is called once per frame
void Update()
{
if (state == State.Idle)
{
UpdateIdle();
}
else if (state == State.Move)
{
UpdateMove();
}
else if (state == State.Attack)
{
UpdateState();
}
}
Enemy는 정지 상태로 시작하고 이동 조건이 성립하면 이동 상태로 전이한다.
private void UpdateIdle()
{
// 나와 target의 거리를 구해서
float distance = Vector3.Distance(transform.position, target.transform.position);
// 만약 그 거리가 감지거리보다 작으면
if (distance < findDistance)
{
// Move상태로 전이하고싶다.
state = State.Move;
}
이동중에 일정 범위 안에 목표물이 들어오면 공격상태로 전이한다.
private void UpdateMove()
{
// target방향으로 이동하다가
Vector3 dir = target.transform.position - transform.position;
dir.Normalize();
transform.position += dir * speed * Time.deltaTime;
// 나와 target의 거리를 구해서
float distance = Vector3.Distance(transform.position, target.transform.position);
// 만약 그 거리가 공격거리보다 작으면
if (distance < attackDistance)
{
// Attack으로 전이하고싶다.
state = State.Attack;
}
}
공격을 계속하지 않고 일정 시간 대기를 한 후 다시 공격한다.
공격중에 목표물이 공격범위에서 벗어나면 다시 이동상태로 전이한다.
private void UpdateAttack()
{
// 시간이 흐르다가
currentTime += Time.deltaTime;
// 만약 현재시간이 공격시간이 되면
if (currentTime > attackTime)
{
// 현재시간을 초기화하고
currentTime = 0;
// 플레이어를 공격하고
// target.AddDamage();
// 만약 target이 공격거리 밖에 있으면 Move상태로 전이하고싶다.
// 나와 target의 거리를 구해서
float distance = Vector3.Distance(transform.position, target.transform.position);
// 만약 그 거리가 공격거리보다 크다면
if (distance > attackDistance)
{
// Move상태로 전이하고싶다.
state = State.Move;
}
}
}
어떠한 상태에서든 피격을 당하면 죽음 상태로 전이한다.
public void AddDamage (int damage)
{
Destroy(gameObject);
}
AddDamage 함수를 불러오기 위해 Gun에 추가한다.
// 만약 hitInfo가 Enemy컴포넌트를 가지고 있다면
Enemy enemy = hitinfo.transform.GetComponent<Enemy>();
if (enemy != null)
{
// enemy의 AddDamage함수를 호출하고싶다.
enemy.AddDamage(1);
}
Enemy 전문
더보기
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// FSM으로 상태를 제어하고싶다.
// 정지, 이동, 공격, 죽음
public class Enemy : MonoBehaviour
{
public float speed = 1;
public float findDistance = 5;
public float attackDistance = 1.5f;
enum State
{
Idle,
Move,
Attack,
Die,
}
State state;
GameObject target;
float currentTime;
float attackTime = 1;
// Start is called before the first frame update
void Start()
{
state = State.Idle;
target = GameObject.Find("Player");
}
// Update is called once per frame
void Update()
{
if (state == State.Idle)
{
UpdateIdle();
}
else if (state == State.Move)
{
UpdateMove();
}
else if (state == State.Attack)
{
UpdateAttack();
}
}
private void UpdateIdle()
{
// 나와 target의 거리를 구해서
float distance = Vector3.Distance(transform.position, target.transform.position);
// 만약 그 거리가 감지거리보다 작으면
if (distance < findDistance)
{
// Move상태로 전이하고싶다.
state = State.Move;
}
}
private void UpdateMove()
{
// target방향으로 이동하다가
Vector3 dir = target.transform.position - transform.position;
dir.Normalize();
transform.position += dir * speed * Time.deltaTime;
// 나와 target의 거리를 구해서
float distance = Vector3.Distance(transform.position, target.transform.position);
// 만약 그 거리가 공격거리보다 작으면
if (distance < attackDistance)
{
// Attack으로 전이하고싶다.
state = State.Attack;
}
}
private void UpdateAttack()
{
// 시간이 흐르다가
currentTime += Time.deltaTime;
// 만약 현재시간이 공격시간이 되면
if (currentTime > attackTime)
{
// 현재시간을 초기화하고
currentTime = 0;
// 플레이어를 공격하고
// target.AddDamage();
// 만약 target이 공격거리 밖에 있으면 Move상태로 전이하고싶다.
// 나와 target의 거리를 구해서
float distance = Vector3.Distance(transform.position, target.transform.position);
// 만약 그 거리가 공격거리보다 크다면
if (distance > attackDistance)
{
// Move상태로 전이하고싶다.
state = State.Move;
}
}
}
public void AddDamage (int damage)
{
Destroy(gameObject);
}
}
Gun 전문
더보기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
// Ray를 이용해서 바라보고 닿은곳에 총을 쏘고싶다. (총알자국을 남기고싶다.)
public class Gun : MonoBehaviour
{
// 총알자국
public ParticleSystem bulletImpact;
public Text logText;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// 만약 마우스 왼쪽버튼을 누르면
if (Input.GetButtonDown("Fire1"))
{
// 시선을 만들고
Ray ray = new Ray(Camera.main.transform.position, Camera.main.transform.forward);
// 그 시선을 이용해서 바라봤는데 만약 닿은곳이 있다면?
RaycastHit hitinfo;
if (Physics.Raycast(ray, out hitinfo))
{
// 닿은곳에 총알자국을 가져다놓고싶다.
bulletImpact.transform.position = hitinfo.point;
// 총알자국 VFX를 재생하고싶다.
bulletImpact.Stop();
bulletImpact.Play();
// 총알자국의 방향을 닿은곳의 Normal방향으로 회전하고싶다.
// = 총알자국의 forward방향과 닿은곳의 Normal방향을 일치시키고 싶다.
bulletImpact.transform.forward = hitinfo.normal;
// 만약 hitInfo가 Enemy컴포넌트를 가지고 있다면
Enemy enemy = hitinfo.transform.GetComponent<Enemy>();
if (enemy != null)
{
// enemy의 AddDamage함수를 호출하고싶다.
enemy.AddDamage(1);
}
}
}
// 마우스 커서의 좌표를 월드 좌표계로 변환하고, 그 월드좌표를 기준으로 Ray를 생성한다.
Ray mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit mouseInfo;
// Ray를 쏴서 부딪힌 대상이 있다면
if (Physics.Raycast(mouseRay, out mouseInfo))
{
print("가리키는 대상: " + mouseInfo.transform.name);
logText.text = "가리키는 대상: " + mouseInfo.transform.name;
}
// Ray에 닿은 대상이 없다면
else
{
print("가리키는 대상 없음");
logText.text = "가리키는 대상 없음";
}
}
}
'공부 > Unity 기초' 카테고리의 다른 글
3D FPS - 7 / 피격 처리 (0) | 2022.05.19 |
---|---|
3D FPS - 6 / 위치 복귀 (0) | 2022.05.19 |
3D FPS - 4 / 가리키는 대상 표시 (0) | 2022.05.18 |
3D FPS - 3 / 무기 제작 (0) | 2022.05.18 |
3D FPS - 2 / 시점 변화 (0) | 2022.05.17 |