본문 바로가기

유니티/확장 프로젝트[3D]

3D 미니 프로젝트 2 - 5 ] 몬스터

지난 포스팅에서는 강화 시스템을 구현 해 보았다.

 

3D 미니 프로젝트 2 - 4 ] 강화 시스템

지난 포스팅에서는 골드메탈님의 강의 중 공격에 대해 정리 해 보았다. 3D 미니 프로젝트 2 - 3 ] 공격 지난 포스팅에서는 아이템 수집 및 장착을 구현하였다. 3D 미니 프로젝트 2 - 2 ] 아이템 수집

mini-noriter.tistory.com

 

이제 강화를 열심히 한 무기로 몬스터를 때려 잡을 수 있어야 하기에 몬스터와 보스에 대한 구현을 해 보도록 하겠다.

 

몬스터, 보스에 대한 내용은 골드메탈님의 강의와 에셋을 이용하여 제작하였다.


몬스터 생성

우선 받은 에셋에 있는 몬스터 Prefab을 들고 와 준다.

몬스터  A Type Prefab

이제 몬스터 로직을 짜기 위해 Enemy.cs를 만들어 주고 아래와 같이 작성을 한다.

 

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

public class Enemy : MonoBehaviour
{
    // 몬스터 타입 설정
    public enum Type { A,B,C,Boss }; // 변수의 종류를 만든다.
    public Type enemyType; // 적의 타입을 넣을 변수

    // 체력 정보
    public int maxHealth;
    public int cntHealth;

    // 데미지 관련
    public GameObject PosObj; // 데미지 생성 위치에 있는 빈 오브젝트
    GameObject Damage_Prefab; // 데미지 프리팹
    GameObject Damage; // 데미지

    Vector3 dmgPos; // 데미지 위치

    // 물리 관련
    protected Rigidbody rigid;
    public BoxCollider meleeArea; // 공격 범위를 담을 변수
    public BoxCollider boxCollider; // 겉면 collider?

    // 원거리 몬스터 전용
    public GameObject monsterMissile; // 몬스터 미사일 프리팹을 담을 변수

    // 상태 관련
    public bool isAttack; // 공격을 하고 있는가?
    public bool isDead; // 죽은 상태인가?

    // 겉보기
    protected MeshRenderer[] mat;

    // 추적 관련
    public bool isChase; // 추적이 가능한 상황!
    public Transform target; // 추적 대상
    protected NavMeshAgent navi; // UnityEngine.AI를 필수로 쓸 것

    //애니메이션
    protected Animator anim;

    // Start is called before the first frame update
    void Awake()
    {
        rigid = GetComponent<Rigidbody>();
        mat = GetComponentsInChildren<MeshRenderer>(); // material을 가져오는 방법!!
        navi = GetComponent<NavMeshAgent>();
        anim = GetComponentInChildren<Animator>();
        Invoke("ChaseStart", 2.0f);
        target = GameObject.FindGameObjectWithTag("Player").transform; // 플레이어를 추적하는 것이 default값

    }
    private void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (navi.enabled)
        {// navi가 활성화 되었을 때만 목표를 추적! (기존에는 목표만 잃어버리고 움직이기는 하기 때문에 정지까지 하는 것으로 해 준다!)
            navi.SetDestination(target.position);
            navi.isStopped = !isChase; // 추적을 하고 있지 않을때(false) 정지를 하고(!false = true), 추적을 할 때 멈추는 것을 멈추게(움직이게) 한다.
        }
            
    }

    private void FixedUpdate()
    {
        Targeting();
        FreezeVelocity(); // 회전 속도 0으로 설정!
    }

    void Targeting()
    {
        // 공격을 하기 위한 타겟 설정
        float targetRadius = 0f;
        float targetRange = 0f;

        if (!isDead && enemyType != Type.Boss) // 죽은 상태가 아니고, 보스가 아닐 때만 타겟팅을 실행
        {
            switch (enemyType)
            {
                case Type.A:
                    targetRadius = 1.5f;
                    targetRange = 3.0f;
                    break;
                case Type.B:
                    targetRadius = 1f; // 타겟을 찾을 두께 (티스토리 참고)
                    targetRange = 12.0f; // 플레이어 타겟팅 범위
                    break;
                case Type.C: // 원거리는 타겟팅이 넓고 정확해야 한다.
                    targetRadius = 0.5f;
                    targetRange = 25.0f; // 플레이어 타겟팅 범위
                    break;
            }
        }
        

        RaycastHit[] rayHits = Physics.SphereCastAll(transform.position, targetRadius, transform.forward, targetRange, LayerMask.GetMask("Player"));
        // 자신의 위치, 구체 반지름, 나아가는 방향(어느 방향으로 쏠 것인가?), 거리, 대상 레이어
        
        if(rayHits.Length > 0 && !isAttack)
        {
            // 플레이어가 몬스터의 레이더 망에 감지됨과 동시에 공격 중이 아니라면!
            StartCoroutine("Attack"); // 공격!

        }
            

    }

    IEnumerator Attack()
    {
        // 일반적인 몬스터는 잠시 정지 후, 공격하고 다시 쫓아가는 패턴으로!

        isChase = false;
        isAttack = true;
        anim.SetBool("isAttack", true);

        switch (enemyType)
        {
            case Type.A:
                yield return new WaitForSeconds(0.5f); // 애니메이션 동작동안 딜레이!

                meleeArea.enabled = true; // 그 뒤에 박스 활성화를 하여 공격!

                yield return new WaitForSeconds(0.3f); // 공격 박스가 활성화 된 시간

                meleeArea.enabled = false;

                yield return new WaitForSeconds(0.8f);
                break;
            case Type.B:
                yield return new WaitForSeconds(0.1f); // 선 딜레이
                rigid.AddForce(transform.forward * 20, ForceMode.Impulse); // 즉각적인 힘으로 돌격!
                meleeArea.enabled = true; // 돌격하는 동안 박스를 활성화!

                yield return new WaitForSeconds(0.5f); // 공격 박스가 활성화 된 시간
                rigid.velocity = Vector3.zero; // 일정 시간 돌격 후 멈춤!
                meleeArea.enabled = false;

                yield return new WaitForSeconds(2.0f); // 후 딜레이
                break;
            case Type.C: // 미사일을 만들어야 한다.
                yield return new WaitForSeconds(0.4f); // 선 딜레이

                GameObject instantBullet = Instantiate(monsterMissile, transform.position,transform.rotation); // 몬스터와 같은 위치에 미사일 생성
                Rigidbody rigidBullet = instantBullet.GetComponent<Rigidbody>();
                rigidBullet.velocity = transform.forward * 20; // 총알에 속도를 부여

                yield return new WaitForSeconds(2.0f); // 후 딜레이
                break;
        }


        isChase = true;
        isAttack = false;
        anim.SetBool("isAttack", false);

    }


    void ChaseStart()
    {
        isChase = true; // 추적을 가능하게 하고
        anim.SetBool("isWalk", true); // 애니메이션 상태를 변경!
    }

    void FreezeVelocity() // 플레이어와 충돌 시 날라가서 추적을 하지 못하는 상황 방지
    {
        if (isChase) // 추적중일 때만 제약!
        {
            rigid.velocity = Vector3.zero; // 속도 0
            rigid.angularVelocity = Vector3.zero; // 회전 속도 0
        }

    }

    private void OnTriggerEnter(Collider other)
    {
        if(other.tag == "Melee")
        {
            Damage_Prefab = Resources.Load("Prefabs/LobbyAndRPG/Damage") as GameObject; // 데미지 프리팹을 GameObject에 가져 온다.

            Weapon weapon = other.gameObject.GetComponent<Weapon>();
            cntHealth -= weapon.Damage;

            Damage = MonoBehaviour.Instantiate(Damage_Prefab);
            Damage.GetComponent<Damage>().damage = weapon.Damage; // 오브젝트 속 데미지 컴포넌트에 있는 데미지 변수 세팅
            Damage.transform.position = PosObj.transform.position;

            Vector3 reactVec = transform.position - other.transform.position;

            StartCoroutine(OnDamage(reactVec));

        }
        else if (other.tag == "Bullet")
        {
            Damage_Prefab = Resources.Load("Prefabs/LobbyAndRPG/Damage") as GameObject; // 데미지 프리팹을 GameObject에 가져 온다.

            Bullet bullet = other.gameObject.GetComponent<Bullet>();
            cntHealth -= bullet.damage;

            Damage = MonoBehaviour.Instantiate(Damage_Prefab);
            Damage.GetComponent<Damage>().damage = bullet.damage; // 오브젝트 속 데미지 컴포넌트에 있는 데미지 변수 세팅
            Damage.transform.position = PosObj.transform.position;

            Vector3 reactVec = transform.position - other.transform.position;

            Destroy(other.gameObject);

            StartCoroutine(OnDamage(reactVec));
        }

    }
    

    IEnumerator OnDamage(Vector3 reactVec) // 피격시 반응 설정
    {
        foreach (MeshRenderer mesh in mat)
        {
            mesh.material.color = Color.red;
        }
        yield return new WaitForSeconds(0.1f);

        if(cntHealth > 0)
        {
            foreach (MeshRenderer mesh in mat)
            {
                mesh.material.color = Color.white;
            }
        }
        else
        {
            foreach (MeshRenderer mesh in mat)
            {
                mesh.material.color = Color.gray;
            }

            gameObject.layer = 7; // rayCast에서와 달리 숫자로 그냥 적는다.
            isDead = true;
            isChase = false;
            if(enemyType == Type.A || enemyType == Type.B)
                meleeArea.enabled = false;
            navi.enabled = false;
            anim.SetTrigger("DoDie");

            reactVec = reactVec.normalized; // 몬스터가 죽을 때 팔짝 뛴 다음에 죽는 모습을 연출하기 위함
            reactVec += Vector3.up;

            rigid.AddForce(reactVec * 10, ForceMode.Impulse);

            

            Destroy(gameObject,2); // 2초 뒤에 Destroy!
        }

    }


}

위 코드는 3종류의 일반 몬스터에 적용된 완성 된 코드이다.

 

enum을 통하여 몬스터의 타입을 구분 한 다음, 각 타입별로 몬스터의 패턴을 다르게 만들어 주었다.

 

몬스터 공격 범위 설정

 

우선, 몬스터에 물리를 적용하기 위하여 RigidBody와 BoxCollider를 추가시켜 준다.

 

그리고, 근접 공격을 활성화 하기 위하여 자식 오브젝트로 빈 오브젝트 추가 후, BoxCollider를 Trigger 모드로 추가 해 주며, Layer 와 Tag 이름을 EnemyBullet으로 설정 해 준다.

 

그 다음, 아래와 같이 애니메이션을 에셋에서 가져 와 구성해 주며, 변환 조건에 트리거를 설정하여 넣어 준다.

 

기본 애니메이션 설정

 

 

타입 설정

 

여러 가지 몬스터를 하나의 코드를 통하여 로직을 적용 해 줄 것이기 때문에 코드 내부에서 몬스터의 타입을 설정 해 주어야 한다.

 

// 몬스터 타입 설정
public enum Type { A,B,C,Boss }; // 변수의 종류를 만든다.
public Type enemyType; // 적의 타입을 넣을 변수

enum을 통하여 A,B,C, 그리고 Boss 타입을 세팅 해 준다.

 


추적

 

그리고 몬스터는 플레이어를 추적할 수 있어야 하기에 유니티에서 추적을 위해 만들어 준 컴포넌트인 nav mesh를 넣어 준다.

Nav Nesh Agent

여기서 몬스터가 플레이어를 추적하는 속도, 방향전환 속도, 가속도 등을 설정할 수 있다.

 

위와 같이 설정 해 준 다음, 이렇게 하고, update에 아래 부분을 넣어 주게 되면

 

navi.SetDestination(target.position);

움직이지 않는다.

 

Nav는 Navigation을 통해 추적을 할 수 있는 땅을 설정 해 주어야 한다.

 

우선 추적을 할 수 있는 땅으로 만들기 위해 지형에 static 체크를 해 주어야만 그 위에 추적할 수 있는 길이 생기게 된다.

 

Navigation 설정하기

Window -> AI -> Navigation에 들어 간 다음

 

Bake

Bake를 눌러 구워주게 되면

 

추적할 수 있는 발판이 설정 된 모습

위 사진과 같이 타겟을 추적할 수 있는 범위가 생기게 된다.

 

Agent Radius를 통하여 네모 칸의 크기를 정할 수 있다.

 

이제, 실행을 해 보게 되면 플레이어를 추적하게 됨을 볼 수 있다.

 

위에 있는 코드에서는 단순 추적이 아닌 공격과 연계하여, 추적 여부를 변동할 수 있게 만들어 주었다. isChase라는 bool 변수를 통하여 해 주었는데, 이 것은 공격을 설명하면서 같이 이야기 하겠다.

 


공격

 

공격 부분 코루틴 코드이다.

IEnumerator Attack()
    {
        // 일반적인 몬스터는 잠시 정지 후, 공격하고 다시 쫓아가는 패턴으로!

        isChase = false;
        isAttack = true;
        anim.SetBool("isAttack", true);

        switch (enemyType)
        {
            case Type.A:
                yield return new WaitForSeconds(0.5f); // 애니메이션 동작동안 딜레이!

                meleeArea.enabled = true; // 그 뒤에 박스 활성화를 하여 공격!

                yield return new WaitForSeconds(0.3f); // 공격 박스가 활성화 된 시간

                meleeArea.enabled = false;

                yield return new WaitForSeconds(0.8f);
                break;
            case Type.B:
                yield return new WaitForSeconds(0.1f); // 선 딜레이
                rigid.AddForce(transform.forward * 20, ForceMode.Impulse); // 즉각적인 힘으로 돌격!
                meleeArea.enabled = true; // 돌격하는 동안 박스를 활성화!

                yield return new WaitForSeconds(0.5f); // 공격 박스가 활성화 된 시간
                rigid.velocity = Vector3.zero; // 일정 시간 돌격 후 멈춤!
                meleeArea.enabled = false;

                yield return new WaitForSeconds(2.0f); // 후 딜레이
                break;
            case Type.C: // 미사일을 만들어야 한다.
                yield return new WaitForSeconds(0.4f); // 선 딜레이

                GameObject instantBullet = Instantiate(monsterMissile, transform.position,transform.rotation); // 몬스터와 같은 위치에 미사일 생성
                Rigidbody rigidBullet = instantBullet.GetComponent<Rigidbody>();
                rigidBullet.velocity = transform.forward * 20; // 총알에 속도를 부여

                yield return new WaitForSeconds(2.0f); // 후 딜레이
                break;
        }


        isChase = true;
        isAttack = false;
        anim.SetBool("isAttack", false);

    }

 

공격하는 순간에는 공격 애니메이션이 나오고, 추적을 하면 안되기에 추적을 할 수 있음을 체크 해 주는 bool 변수인 isChase를 만들어 false로 만들어 준다.

 

앞서 추적 부분 코드에서 navi.isStopped 를 isChase에 not 연산자를 넣어 대입 해 주었다.

 

그 이유가 바로 공격 딜레이 때 추적을 하는 것을 방지하기 위함이다. (isStopped 는 true일 때 멈추는 것이기 때문에 추적을 멈추게 하기 위해서는 isChase가 false가 되어야 한다.)

 

그리고 몬스터 타입에 따라서 공격을 하는 방식을 바꾸어 주었다.

 

타입 A는 일반적으로 잠시 멈춰서 정면을 공격하는 몬스터, 타입 B는 일정 시간 빠르게 돌진하면서 공격하는 몬스터, 타입 C는 미사일을 소환하여 공격하는 몬스터이다.

 

공격 움짤

A에서는 딜레이에 맞춰서 근접공격의 BoxCollider를 활성화/비활성화 시켜주면 된다.

 

즉, 위 움짤에서는 몬스터가 앞으로 몸을 내지르는 순간에 Box Collider가 활성화 되는 것이다.

 

Box Collider는 몬스터 자식 오브젝트로 빈 오브젝트를 하나 만들어 준 다음, BoxCollider만 Trigger 형태로 넣어주면 된다.

 

그리고 그곳에 데미지 설정을 해 주기 위해 만들어 놓은 Bullet.cs 코드를 넣어 준다.

Bullet.cs

B는 일정시간동안 AddForce를 통하여 힘을 주어 돌진하게끔 해 주고, 돌진하는 동안 근접공격 BoxCollider를 활성화 시켜주면 된다.

 

BoxCollider는 A와 같게 설정 해 주면 된다.

 

C는 원거리 공격이기 때문에 따로 미사일 Prefab을 만들어 주어야 한다.

 

monster missile

에셋에 있는 미사일 Prefab을 받아 드래그 해 준다.

 

그 다음에, Prefab 자식 오브젝트에 있는 mesh object의 y좌표를 띄워 주고, 방향을 z축(파란 화살표)을 향하게 돌려 준다.

 

자식 오브젝트의 y축을 띄워 주는 이유는 미사일 좌표는 바닥에 붙어 있는데 미사일 자체는 떠 있게끔 해 주려고 하기 때문이다. (유도 미사일을 만들 때 이렇게 하지 않으면 오류가 생긴다.)

 

미사일 자식 Mesh Object

그리고 Mesh Object에는 미사일이 뱅글뱅글 돌게 해 주는 Missile.cs 코드를 넣어 준다.

 

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

public class Missile : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        transform.Rotate(Vector3.right * 3);
    }
}

 

그리고 미사일답게 Particle System도 추가 해 주고, 설정 해 준다.

 

Particle System 설정 모습

그리고 Mesh Object가 아닌, 미사일에는 물리적 판정이 나게 할 수 있는 BoxCollider를 설정 해 주고, Bullet.cs 코드를 넣어 데미지를 설정 해 준다.

 

Bullet 설정

그리고 Bullet 코드 중에서 플레이어가 쏘는 총알은 벽이나 바닥에 닿으면 사라지게 만들었던 것을 기억 할 것이다.

 

같은 코드이기에 미사일 역시 바닥에 닿으면 사라지게 될 것이다.

 

그런데 아까 근접 공격에도 Bullet.cs를 넣어 준 것을 기억할 것이다. 그렇다면 만약 몬스터가 바닥이나 벽에 공격을 하게 되면 근접 공격 범위 오브젝트가 사라지게 될 것이다.

 

그렇게 되서는 안되기에 코드 속에 상태 변수를 하나 만들어 주어 근접공격 여부를 구분 해 주었다

 

그리고 미사일이 사라지는 것 처럼 해당 객체가 사라지게 하는 조건에 근접공격이 아닌 경우를 And로 묶어 추가 하였다.

 

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

public class Bullet : MonoBehaviour
{
    public int damage;
    public bool isRock;
    public bool isMelee; // 벽이랑 바닥에 총알이 닿았을 때, Destroy를 실행하는데, 만약 근접 Collider랑 벽이랑 닿아서 없어지면 안된다. -> 근접 Collider 여부를 판단하기 위함!
    public void SetDamage(int setDmg)
    {
        this.damage = setDmg;
    }

    private void OnCollisionEnter(Collision collision)
    {
        if(collision.gameObject.tag == "base")
        {
            Destroy(gameObject, 3);
        }
        else if(collision.gameObject.tag == "Wall")
        {
            Destroy(gameObject);
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (!isMelee && (other.gameObject.tag == "base" || other.gameObject.tag == "Wall"))
        {
            // 근접공격 Collider가 아니면서, 벽 or 바닥에 닿으면
            Destroy(gameObject); // 제거
        }

    }


}

수정된 Bullet.cs 이다.


플레이어 피격 판정

 

이제 몬스터가 플레이어를 때렸을 때, 플레이어는 맞아야? 한다.

 

피격 판정을 만들어 보자

 

PlayerCode.cs 코드에 onTriggerEnter() 이벤트를 추가 해 준다.

 

private void OnTriggerEnter(Collider other)
{
    if(other.tag == "EnemyBullet")
    {
        if (!isDamage)
        {
            Bullet enemyBullet = other.GetComponent<Bullet>();
            playerHealth -= enemyBullet.damage;

            if (other.GetComponent<RigidBody>() != null) // 무적 시간 중에도 추가적으로 투사체를 맞게 되면 사라지게끔!
              Destroy(other.gameObject);

            StartCoroutine(OnDamage());
        }
    }
}
    
   
IEnumerator OnDamage()
{
    isDamage = true;
    foreach(MeshRenderer mesh in meshs)
    {
        mesh.material.color = Color.red;
    }

    yield return new WaitForSeconds(1.0f); // 무적 딜레이 1초!

    isDamage = false;
    foreach (MeshRenderer mesh in meshs)
    {
        mesh.material.color = Color.white;
    }

}

EnemyBullet 이라는 이름의 객체에 닿게 되면 데미지를 닳게 하고, (투사체라면 RigidBody가 포함되어 있기에) RigidBody가 있을 경우 해당 객체를 삭제 해 준다. (투사체는 1회용이기 때문!)

 

그리고 코루틴을 이용하여 OnDamage()에 넘어가서 1초 동안 데미지를 받는 동안 무적 상태로 만들어 준다. (isDamage = true)

(데미지 처리는 isDamage가 false일 때만 되기 때문에 사실상 무적이다)

 

그리고 플레이어 Mesh들을 다 불러 온 다음, 맞았다는 것을 표현하기 위해 빨간색으로 칠해 준다.

 

피격 상태

이렇게 설정 해 주면 위 사진과 같이 피격 시 플레이어 전체가 빨간색으로 변하게 된다.

 

돌격형 몬스터는 아래 움짤과 같이 되게 된다.

돌격형 몬스터
원거리 몬스터 장면


데미지 표현

몬스터를 때리면 데미지가 위에 표시되어야 한다.

 

데미지는 TextMeshPro 객체를 하나 만들어 데미지 모양을 설정 해 준다.

데미지

데미지의 특성은 데미지를 보여 준 다음, 얼마 있지 않다가 바로 사라지는 것이다.

 

따라서 이러한 특성을 코드로 작성해 보자.

 

Damage.cs

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

public class Damage : MonoBehaviour
{
    public float movingSpeed;
    public float alphaSpeed;
    public int damage;

    TextMeshPro dmgText;
    Color alpha; // 텍스트의 컬러 관련 정보를 얻을 수 있다.


    // Start is called before the first frame update
    void Start()
    {
        dmgText = GetComponent<TextMeshPro>();
        dmgText.text = damage.ToString();
        alpha = dmgText.color;
        Invoke("DestroyDmg", 2.0f); // 2초 뒤에 데미지가 사라지게!
        
    }

    // Update is called once per frame
    void Update()
    {
        transform.Translate(new Vector3(0, 1 * movingSpeed * Time.deltaTime, 0));
        alpha.a = Mathf.Lerp(alpha.a, 0, Time.deltaTime * alphaSpeed);
        dmgText.color = alpha;
    }

    void DestroyDmg()
    {
        Destroy(gameObject);
    }

}

시작하자 마자 Invoke을 통해 2초 뒤에 데미지가 사라지게 하고, 그 때 까지 Update에서 데미지의 투명도가 서서히 0으로 가게 만들었다.

 

Mathf.Lerp는 첫 두 매개변수의 사이값을 가져오게 만드는 함수이다.

즉, alpha.a(현재 투명도)와 0 사이의 값을 투명해지는 속도(alphaSpeed)로 사이 값을 가져오게 만드는 것이다. 

 

이렇게 만들어 준 다음, prefab으로 저장 해 준다.

 

다음으로는 몬스터 자식 오브젝트에 데미지 위치로 세팅 할 빈 오브젝트를 만들어 준다.

 

그리고 Enemy.cs 코드에 아래와 같이 Damage 부분을 넣어 준다.

private void OnTriggerEnter(Collider other)
    {
        if(other.tag == "Melee")
        {
            Damage_Prefab = Resources.Load("Prefabs/LobbyAndRPG/Damage") as GameObject; // 데미지 프리팹을 GameObject에 가져 온다.

            Weapon weapon = other.gameObject.GetComponent<Weapon>();
            cntHealth -= weapon.Damage;

            Damage = MonoBehaviour.Instantiate(Damage_Prefab);
            Damage.GetComponent<Damage>().damage = weapon.Damage; // 오브젝트 속 데미지 컴포넌트에 있는 데미지 변수 세팅
            Damage.transform.position = PosObj.transform.position;

            Vector3 reactVec = transform.position - other.transform.position;

            StartCoroutine(OnDamage(reactVec));

        }
        else if (other.tag == "Bullet")
        {
            Damage_Prefab = Resources.Load("Prefabs/LobbyAndRPG/Damage") as GameObject; // 데미지 프리팹을 GameObject에 가져 온다.

            Bullet bullet = other.gameObject.GetComponent<Bullet>();
            cntHealth -= bullet.damage;

            Damage = MonoBehaviour.Instantiate(Damage_Prefab);
            Damage.GetComponent<Damage>().damage = bullet.damage; // 오브젝트 속 데미지 컴포넌트에 있는 데미지 변수 세팅
            Damage.transform.position = PosObj.transform.position;

            Vector3 reactVec = transform.position - other.transform.position;

            Destroy(other.gameObject);

            StartCoroutine(OnDamage(reactVec));
        }

    }

우선 데미지 prefab을 Resource.Load를 통해서 불러 온다.

(Public으로 설정 한 다음 드래그 앤 드랍으로 객체를 설정해도 된다.)

 

그 다음에 데미지를 설정하여 데미지에 적용시킨 다음, Instantiate를 통해 prefab을 소환 해 준다.

 


몬스터를 잡는 모습

 

데미지 적용까지 끝난 모습이다.

몬스터를 잡는 움짤

다음 포스팅에서는 보스 구현에 대해 정리 해 보도록 하겠다.