본문 바로가기
공부/Unity 기초

3D FPS - 5 / 상태머신 설계

by svcbn 2022. 5. 18.

상태머신(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