ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 팀업 프로젝트 03. Enemy 상태구현 및 넉백
    팀업 프로젝트/Night Raid

    수정된 맵 사진입니다. 마을과 Boss zone을 나누어 마을에서부터 일반 몬스터를 잡으며 아이템을 얻고 보스를 소탕하는 동작 구현으로 설계를 하였습니다.

     

    Terrain인 필드는 경로가 너무 길다고 판단하여 맵을 Teerain 컴포넌트를 이용해 수정하고 새로 배치를 해 주었습니다.

     

    Update문

    Enemy의 상태인 Return, Damaged, Die 상태를 보겠습니다.

    Damaged와 Die 상태는 매 프레임마다 실행되는것이 아닌 조건이 성립할 때 한 번씩 실행되기에, Update문에서 함수 실행을 하지 않았습니다.

    여기서도 거리를 체크 하기 위해 Vector3.Distance를 사용하였습니다.


    거리를 체크하는 방법을 몇가지 더 찾아보니

    Vector3 에는

    1. Distance

    2. magnitude

    그리고 처음 보는 3.sqrMagnitude 가 있었습니다.

     

    검색을 통해 알아본 사실은, 1-2 번은 정확한 거리를 계산하고

    3 번은 계산된 거리의 제곱값을 리턴하므로, 3번의 경우 루트 연산을 하지 않아 연산속도가 더 빠르다고 합니다.

     

    정확한 거리를 계산 할때는 1-2번을 이용하면 되고, 단순 거리가 작거나 큰지 비교만 하고 싶으면 3번을 이용하면 된다라고 하며, 1번의 경우는 편의 룰 위해서 제공하는 것뿐 1-2번의 결과 및 계산 방식은 동일하다고 합니다.

     

    책에서 본 습관적인 코드인 Distance에서 다른 거리 방식들의 예도 찾아 보며 좀 더 알아보야 하겠습니다.


    사용 한 코드는 Distance 함수를 사용하였고, Return 함수가 실행 될 시 Enemy의 위치와 초기 위치를 계산해

    0.1f 보다 크다면 Enemy의 위치를 nav로 이용해 목적지를 초기 위치로 정해주었으며,

    멈추는 거리인 stoppingDistance 는 0으로 하여 거리를 두고 멈추는 현상을 없앴습니다.

     

    else문 에서의 만일 0.1f의 거리 안에 포함이 되어있다면, 

    isStopped로 네비를 멈춰 주었고,

    ResetPath() 로 초기화시켜 주었습니다.

     

    inCombat : 전투중임을 알리는 불 변수 = false

    onMove : 움직이고 있음을 알리는 불 변수 = false

    마지막으로 Enemy의 상태는 Idle로 하여 

    animator에서의 동작을 Idle 상태가 되게 해 주었습니다.

    이전에 보았던 OnEnable 에서 실행되는 코 루틴 함수입니다.

    if (! inCombat)의 조건에 부합하여

    다시 Move_NonCombat() 함수를 실행시켜 주며

    Enemy는 다시 자유롭게 필드를 돌아다니게 됩니다.

    Damaged

    Enemy의 상태가 Damged일 때 실행되는 Damaged의 함수에선 StartCorutine으로 DamagedProcess를 실행시킵니다.

    추후 이뤄질 사운드 작업의 중복 방지를 위해 isHit의 불 변수를 사용해 중복을 막아 주었고

    yield return new WaitForSeconds를 이용해 0.1f 초의 대기시간을 갖게 해 주었습니다.

    이 외에도 yield return의 종류는 여러 가지 다양하게 있었기에 많이 사용해 보며 제 것으로 만들어야 겠습니다.

    아직 제 역량에서 사용할 수 있는 것은 

    yield return null : 다음 프레임까지 대기

    yield return new WaitForSeconds : 지정한 초만큼 대기

    이 두 가지 정도 일 것 같습니다.

     

    마지막으로 Die() 함수로 Enemy가 죽었을 시 발생될 함수입니다.

    Enemy가 죽었는지의 판단은 Enemy가 공격을 받았을 시 hp > 0 상태이면 hp가 소모가 되고

    hp가 <= 0 일시엔 죽었으므로 판단하여 Enemy의 상태를 Die상태로 바꿔주게 할 예정입니다.


    Player와 맞춰 봐야 하는 경우가 있기에

    Update에서 만든 함수 조건문들로 Test를 하고 있습니다.

    GetKeyDown으로 눌렀을 시 한 번만 실행되게 하였으며

    GetKey : 누르고 있을 경우 True

    GetKeyUp : 눌렀다 손이 떼어질 때 True

    의 Input 함수도 존재합니다.


    다시 DIe 함수로 넘어와서 Manager.instance.mangerSE 와 같은 싱글톤 패턴 접근 방식으로 오디오 소스를 재생하였고

    즉시 사라지지 않도록 코 루틴 함수에서 yield return new WatiForSeconds를 사용해 5f초의 시간을 주게 하여 enemy가 죽었을 시 발생하는 animation 재생 시간도 함께 기다리게 했습니다.

     

    5f초의 시간이 지나면 SetActive를 false로 바꿔주어 오브젝트가 비활성화가 되며

    StopAllCoroutines()로 모든 코루틴 함수를 중지시킵니다.

    -StopCoroutine(함수명)을 사용하면 원하는 함수만 중지시킬 수 있습니다.-

     

    StopPlayback을 사용하여 진행되고 있는 애니메이션도 종료시켜주었고

    inCombat과 onMove도 false로 바꿔주어 전투 중임과 움직임 중을 모두 해제시켰습니다.

     

    Manager.instance.manager_Enemy.Respawn(gameObject, orizinPos, vanishTime, respawnTime)

    의 부분이 Enemy가 리스폰되는 부분입니다.

     

    gameObject : 이 스크립트가 포함된 오브젝트 (Enemy)

    orizinPos : Enemy의 초기 위치

    vanishTime : 소멸 시간

    respawnTime : 리스폰 시간

    의 파라미터들을 Respawn에 주었고

    manager_Enemy에 있는 Respawn함수를 보겠습니다.

    처음에 있는 Respawn 함수를 보자면 GameObject, Vector3, float, float의 4가지 값을 받아

    StartCorutine으로 실행시키는 RegenMonster함수에 연결시켜 줍니다.

     

    이렇게 받은 Ienumrator RegenMonster는

    Enemy enemy = monster.GetComponent <Enemy>()로 Enemy 스크립트를 변수에 담아 주고

     

    vanishTime : 소멸 시간 / 만큼 기다렸다가 monster(Enemy)의 SetActive는 비활성화해 줍니다.

     

    respawnTime : 리스폰 시간 / 만큼 기다렸다가, monster(Enemy)의 위치는 초기 위치로 정해 주고

    다시 SetActive를 활성화로 사용해 리스폰이 되는 것처럼 연출을 하였습니다.

     

    if문의 조건문에선, 일반 몬스터의 종류를 생각해 tag 검사를 해 주었고, tag = "Enemy"라면

    Enemy의 현재 hp를 최대 hp로 바꿔주어 완전한 부활을 만들었습니다.

     

    마지막으로 상태는 Idle로 바꿔주어 초기에 생성된 Eenmy와 같은 상황으로 만들어 주었습니다.

    Enemy의 인스펙터 창에 있는 VanishTime과 RespawnTime 입니다.


    마지막으로 player와 hit모션과 attack 작업을 하던 도중 밋밋함을 느꼈고

    각 기능에 넉백 기능과 스턴 기능을 넣어보면 어떨까 해서 만들어 보게 되었습니다.

     

    player의 공격 기능은 총 3가지로 

    제가 생각하기에는 Enemy의 공격받는 모션도 총 3가지로 

    넉백, 스턴, 대미지가 필요했습니다.

     

    우선 Enemy가 넉백이 되는 과정에서 역동적인 동작을 연출시켜 주고 싶어 좋은 방법이 없을까 생각을 하던 도중

    Rigidbody를 사용해 AddForce를 이용한 화살 쏘는 영상이 생각났습니다.

     

    Rigidbody를 Enemy의 컴포넌트에 추가해 바로 가져왔고.

    첫 번째 공격을 공격과 대미지를 받는 동작을 하는 함수인 HitEnemyN_D를 만들었습니다.

    Enemy의 상태가 Die 이거나( || ) Return 상태이면 함수를 실행하지 않고 바로 Return을 하여 벗어나게 했습니다.

    Enemy가 죽었을 때나, Return 중이면 동작하지 않도록 첫 번째 조건문을 걸어주었습니다.

     

    두 번째 조건문은 isHit인 불 변수로 인하여 연속 공격에 대비하여 중복되는 점을 막아 주었었습니다.

    이걸 이용하여 hit 타격을 입었을 시에는 현재 hp에서 받아온 플레이어의 공격 hitPower 만큼 체력을 빼 주었고

    미끄러짐을 방지하여 Enemy의 nav는 isStop으로 멈춰 주고 ResetPath로 초기화시켜 주었습니다.

     

    세 번째 조건문인 Enemy의 체력이 > 0 일시

    enemy의 상태를 Damaged 상태로 바꿔 주며 

    rb.AddForce(Camera.main.transform.forward * backPower, ForceMode.Impulse)로 넉백을 실행하였고,

     

    마지막 else문일 시는 Enemy의 체력이 <= 0으로 판단하여

    Enemy의 상태를 Die로 바꿔주었습니다.

     

    rb.AddForce(Camera.main.transform.forward * backPower, ForceMode.Impulse)

    코드를 사용한 이유는, 좀 더 역동적인 동작을 연출해 주고 싶어서입니다.

     

    AddForce의 종류에는 4가지가 있는 것을 알고 있는데

     

    ForceMode.Force : 연속적인 힘 & 무게를 적용

    ForceMode.Acceleration : 연속적인 힘 & 무게를 무시

     

    ForceMode.Impulse : 순간적인 힘 & 무게를 적용

    ForceMode.VelocityChange : 순간적인 힘 & 무게를 무시

     

    로 그중 ForceMode.Impulse : 순간적인 힘 & 무게를 적용을 사용하여

    투척 무기에 사용되던 방식을 가져와 사용해 봤습니다.

     

     

     

    추가 적으로 인벤토리의 Item을 오브젝트 풀 방식으로 구현을 해 보았습니다.

    Enemy가 죽었을 시 발생하는 Respawn함수 부분입니다. 아이템이 확정적으로 나오는 건 파밍의 재미의 요소를 떨어트린다고 생각하여  Random.Range(1,11)을 사용하여 1-10까지의 숫자를 int num에 대입하고

    if문의 조건에 num < 6을 넣어 50%의 확률을 나타내게 하였습니다.

    gold.transform.position = enemy.transform.position
    gold.SetActive(true)의 동작 구현은 꺼내온 오브젝트 풀 방식의 함수에서 return 된 아이템을 활성화시키는

    동작 구현 코드입니다.

    마지막 오브젝트 풀 방식을 담당할 ManagerObj 스크립트입니다.

     

    gold_Pool : 아이템들의 풀입니다. ( 현재 아이템들을 담고 있는 부모 오브젝트입니다.)

    list_Gold : 아이템들입니다. ( 생성시킬 아이템들의 List입니다.)

    prefab_Gold : 아이템들의 프리 팹입니다. (아이템이 모두 활성화되어있다면, 생성됩니다.)

     

    AddObj의 아이템을 만들어 주는 함수에서는 prefab, pool, list 들을 받아 오고

    Instantiate를 이용해 pool의 위치에 prefab을 만들어 줍니다.

    이렇게 만들어준 prefab은 list에 add로 추가시키며, 반환을 해주는 코드.

    마지막으로 반환을 해주는 코드입니다.

    for문을 통하여 현재 가지고 있는 List를 검색하여 주고 list들의 인덱스 들 중 activeSelf를 사용해 비활성화가 되어 있다면 list [i]를 반환해 줍니다.

     

    만일 위가 return이 실행이 안되고 내려와 if(list == list_Gold)가 실행이 된다면 현재 아이템이 모두 활성화되어있는 상태로 AddObj를 통하여 새로운 프리 팹을 만들어 줍니다.

    댓글

김효겸 / Tel. 010-7735-0580 / E-mail. dollzzang2@hanmail.net