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

3D FPS - 7 / 피격 처리

by svcbn 2022. 5. 19.

ImageHit, 화면을 뒤덮는 UI를 생성하여 Enemy에게 공격 당했을 때 깜빡이게 한다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


// 태어날 때 ImageHit를 보이지 않게 하고싶다.
// 적이 플레이어를 공격했을 때 ImageHit를 깜빡이게 하고싶다.

public class HitManager : MonoBehaviour
{
    public static HitManager instance;

    private void Awake()
    {
        instance = this;
    }

    public GameObject imageHit;

    // Start is called before the first frame update
    void Start()
    {
        // 태어날 때 imageHit 를 보이지 않게 하고싶다.
        imageHit.SetActive(false);

    }

    public void Hit()
    {
        // 깜빡거리고싶다.
        StartCoroutine("IeHit");
        

    }

    IEnumerator IeHit()
    {
        // imgaHit를 보이게 하고 싶다
        imageHit.SetActive(true);

        // 0.1초 후에
        yield return new WaitForSeconds(0.1f);

        // imageHit를 안 보이게 하고 싶다.
        imageHit.SetActive(false);

    }

    // Update is called once per frame
    void Update()
    {
     
    }
}

 

코루틴(Coroutine) : IEnumerator

루틴이란 함수와 같은 개념

함께 동작하는 함수. 즉 "동시에 실행되는 함수" 라는 의미를 가지고 있다.

여러개의 진입점을 가진다.

 

 

Singleton 패턴

단 하나의 객체(인스턴스)를 생성해서 사용하는 디자인 패턴

 

 

 

 

 

 

 

미리 선언해둔 Hit함수를 Enemy에서 호출하고, 플레이어hp에서 AddDamage를 호출한다.

 if (currentTime > attackTime)
        {
            // 현재시간을 초기화하고
            currentTime = 0;

            

            // 만약 target이 공격거리 밖에 있으면 Move상태로 전이하고싶다.
            // 나와 target의 거리를 구해서
            float distance = Vector3.Distance(transform.position, target.transform.position);

            // 만약 그 거리가 공격거리보다 크다면
            if (distance > attackDistance)
            {
                // Move상태로 전이하고싶다.
                state = State.Move;

            }

            // 공격 성공
            else
            {
                // 플레이어를 공격하고
                php.AddDamage();
                // HitManager의 Hit 함수를 호출하고싶다.
                HitManager.instance.Hit();

            }


        }

 

 

 

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;
    public float traceDistance = 15;

    
    enum State
    {
        Idle,
        Move,
        Attack,
        Die,
        Return
    }

    State state;
    PlayerHP php;

    GameObject target;

    float currentTime;
    float attackTime = 1;

    Vector3 originPosition;

    // Start is called before the first frame update
    void Start()
    {
        state = State.Idle;

        target = GameObject.Find("Player");

        php = target.GetComponent<PlayerHP>();

        // 최초 위치를 originPosition 변수에 저장한다.
        originPosition = transform.position;


    }

    // 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();
        }

        else if (state == State.Return)
        {
            UpdateReturn();
        }
    }

    
    private void UpdateIdle()
    {
        // 나와 target의 거리를 구해서
        float distance = Vector3.Distance(transform.position, target.transform.position);

        // 만약 그 거리가 감지거리보다 작으면
        if (distance < findDistance)
        {
            // Move상태로 전이하고싶다.
            state = State.Move;

        }

    }

    private void UpdateMove()
    {
        // 현재 위치가 최대 추적 가능 거리를 넘어갔는지 여부를 판단한다.
        float trace = Vector3.Distance(originPosition, transform.position);

        // 만일, 최대 추적 가능 거리를 넘어갔다면 상태를 복귀 상태로 전환한다.
        if (trace > traceDistance)
        {
            state = State.Return;
        }


        // 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이 공격거리 밖에 있으면 Move상태로 전이하고싶다.
            // 나와 target의 거리를 구해서
            float distance = Vector3.Distance(transform.position, target.transform.position);

            // 만약 그 거리가 공격거리보다 크다면
            if (distance > attackDistance)
            {
                // Move상태로 전이하고싶다.
                state = State.Move;

            }

            // 공격 성공
            else
            {
                // 플레이어를 공격하고
                php.AddDamage();
                // HitManager의 Hit 함수를 호출하고싶다.
                HitManager.instance.Hit();

            }


        }

    }

    
    public void AddDamage (int damage)
    {
        Destroy(gameObject);

    }

    void UpdateReturn()
    {
        // 만일, 나의 현재 위치가 originPosition 위치에서 10센티미터 이상이라면
        Vector3 dir = originPosition - transform.position;

        if (dir.magnitude > 0.1f)
        {
            // originPosition 방향으로 이동한다.
            transform.position += dir.normalized * speed * Time.deltaTime;
        }


        // 그렇지 않다면, 나의 위치를 originPosition으로 설정한다.
        else
        {
            transform.position = originPosition;

            // 상태를 다시 대기 상태로 전환한다.
            state = State.Idle;

        }

        // 나와 target의 거리를 구해서
        float distance = Vector3.Distance(transform.position, target.transform.position);

        // 복귀 중 감지거리 안으로 Player가 들어오면
        if (distance < findDistance)
        {
            // 상태를 Move 상태로 전이한다.
            state = State.Move;
        }

        
    }
}

 

 

 

 

 

 

AddDamage 함수를 만들어줄 PlayerHP를 UI의 Slider로 만든다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

// 태어날때 체력을 최대체력으로 하고싶다.
// 적이 플레이어를 공격할 때 체력을 감소하고싶다.
// 체력이 변경되면 UI에도 반영하고싶다.

public class PlayerHP : MonoBehaviour
{
    int hp;
    public int maxHP = 3;
    public Slider sliderHP;


    // property
    public int HP
    {
        get { return hp; }
        set
        {
            hp = value;
            sliderHP.value = value;
        }
    }
   

    public void AddDamage()
    {
        HP -= 1; 
    }
    
    // Start is called before the first frame update
    void Start()
    {
        sliderHP.maxValue = maxHP;
        HP = maxHP;
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}