ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • FPS 연습 04. Zombie 상태값 & Respawn
    연습 프로젝트/FPS 연습

    안녕하세요

    이번엔 일반 좀비로 만들었던 빠른 녀석의 상태 값 구현을 마무리해주고

     

    죽었을 시 일정 시간이 지난 후 되살아나는 Respawn 스크립트도 구현을 해보겠습니다.

    순서는 이렇습니다.

     

    1.Move (이동)

    2.Attack (공격)

    3.Damge (대미지)

    4.Die & Respawn (죽음 & 되살아남)


    자 그럼 바로 코드 내용부터 보겠습니다!


     

    Move함수입니다.

     

    notMovSpeed의 스피드였던 nav의 스피드를

    movSpeed로 바꿔 주었습니다.

     

    이후 거리를 체크하는 Distance함수를 사용해 transform(zombie)와 player의 위치를 계산해

     

    atckDis(공격 범위) 보다 클 경우의 조건문을 걸어주었습니다.

     

    (DIstance와 과정 및 결과가 똑같은 magnitude / 제곱 값을 리턴하는 sqrMagitude(연산 속도가 더 빠름)

    정확한 계산 / 크게 나 작은지만 판단

     

    전투 중인지 판단하는 inCombat 불 변수는 트루로 전투 중임을 알려 주었고.

     

    현재 적용된 목적지가 어디로 설정되어 있을지 몰라

    미끄러지듯이 움직이는 것을 방지하기 위하여

    isStopped를 true로 바꿔주어 멈춰주었습니다.

     

    ResetPath()로 목적지의 경로를 [초기화] 시켜 주었으며

     

    nav.stoppingDistance = atckDis 멈추는 거리는 (공격 범위) /

    nav.destination = player.position (목적지는 player로 향하게 해 주었습니다)

     

     

    atkDis(공격 범위) 보다 낮다면 현재 공격범위 안에 들어와 있는 것으로 간주합니다.

     

    zombieState = ZombieState.Attack 좀비의 상태를 Attack로 바꿔 주어 상태 값에 따른 Swicth 문에서의

    Attack() 함수를 실행하게 해 주었고


                curTime = atkDelay / 공격 딜레이를 담당할 curTime을 장전해 주었습니다.

    이렇게 해 놓으면 Attack 함수에서 나올 curTime += Time.deltaTime으로 1초씩 더해지는 curTime 변수를

    공격 가능할 값으로 바꿔주어 좀비가 플레이어에게 떨어졌을 시

    바로 공격을 가능하게 만들어 주었습니다.


                ani.SetTrigger("MoveToAttackDelay") : SetTrigger를 활용해 트랜지션의 방향을

    Move -> AttackDelay로 가게 해 주었습니다.

     

    Move에서 Attack 애니메이션으로 바로 가지 않는 이유는

    움직이다 공격으로 바로 전환 시 어색함을 연출할 수 있기 때문에

    중간 단계인 AttackDelay 애니메이션으로 가게 해 주어

    공격 준비 자세를 하게 만들었습니다.


    좀비의 상태가 Attack일시 실행되는 Attack() 함수입니다.

    우선 공격 시 좀비가 플레이어를 바라보게 하기 위해

    LookAt() 함수를 사용했습니다.

     

    LookAt함수는 트랜스폼을 회전시켜 지정한 타깃을 바라보게 하는 기능으로 보시면 될 텐데

     

    지정을 LookAt(player)로 player를 지정해주었기 때문에, 좀비의 상태가 Attack일 시 

     

    플레이어를 바라보며 회전을 하게 됩니다.

     

     

    if (Vector3.Distance(transform.position, player.position) < atckDis)

    를 한번 더 이용해, 좀비가 떨어져 있는 상태인지 공격 범위 안에 여전히 들어와 있는 상태인지 판단합니다.

     

    역시 Distance함수를 사용해 좀비의 위치와, 플레이어의 위치를 계산하여 atkDis(공격 범위) 안에 있을 시 실행되는 코드입니다.

     

    curTime += Time.deltaTime * 1;

     

    cirTime에 += 연산자와 Time.deltaTime을 사용하여 1초씩 누적시켜 주는 코드입니다.

     


    Time.deltaTime 에 1을 곱해준 이유는, 제가 프로젝트를 진행하면서 단순 Time.deltaTime를 사용해 봤었는데

     

    종종 지정된 값을 벗어나게 된 적이 있습니다.

     

    curTime += Time.deltaTime * 1 나 curTime += Time.deltaTime 는 같은 결과를 나타내는 코드이지만.

     

    curTime += Time.deltaTime 에 1을 곱해 줬을 시 지정된 값을 벗어나게 되는 현상이 없어졌었는데요,,

     

    이렇게 된 이유는 못 찾았지만 찾게 되면 꼭 추가를 하여 올리도록 하겠습니다.


    curTime이 정해진 값 atkDelay보다 크다면

     

    공격을 실행할 것이니 curTime = 0으로 초기화해 주고

     

    SetTrigger를 이용해 ("StartAttack") 애니메이션을 실행시켜 줍니다.

    이 애니메이션의 속도는 float 파라미터로 만든 Speed로 결정짓기에

     

    이 Speed값을 ani.SetFloat("Speed", Random.Range(1.0f, 2.0f))로 

     

    Random.Range를 사용하여 1.0f - 2.0f의 랜덤 값을 갖게 해주는 코드입니다.

     

    1 = 기본 재생 속도이며

    2 = 2배의 재생 속도입니다.

    반대로 0.5 = 말 그대로 0.5배의 재생 속도입니다.

     

    HitEnemy

    좀비의 Die상태와 Damaged상태는 

     

    좀비가 피격될 시 실행될 HitEnemy 함수에서 실행을 하였습니다.

     

    인자 값으로 int hitPower를 받게 되어 player의 공격력을 받을 것이며.

     

     

    좀비가 죽었을 시 실행이 될 경우를 방지하여 상태가 Die일 시 return으로 함수를 끝내게 해 주었습니다.

     

    우선 좀비가 피격이 되면 좀비의 hp_Now를 -= 연산자를 이용해 지정된 hitPower만큼 빼 줍니다

     

    이후

     nav.isStopped = true : 로 좀비의 nav를 멈춰 주고
            nav.ResetPath() : 목적지를 [초기화]를 해 주었는데

     

    아직 좀비의 Damage를 입는 애니메이션은 실행하진 않았지만,

    Damage의 애니메이션을 실행하면서 플레이어에게 다가오게 되므로

    미끄러지는 이동을 방지하기 위해 설정해 두었습니다.

     

    if - else문입니다.

     

    만일 hp_Now > 0로 hp가 0보다 크다면

    좀비의 상태를 Damaged로 변경시켜주며

    Damaged의 함수를 실행되게 해 주었고

     

    만일 hp_Now가 0보다 작다면

    죽었을 때를 판단해

    좀비의 상태를 Die로 변경시켜주어

    Die의 함수가 실행되게 해 주었습니다.


    Damaged를 입었을 때의 동작 구현부터 보겠습니다.

    StartCorutine 함수로 "DamagedProcess"를 실행을 시켜 주었으며

     

    코 루틴 함수인 DamagedProcess는 좀비의 상태를 Move로 변경시켜 줍니다.

    SetTrigger를 이용해 "AttackToMove"로 Move로 향하는 트랜지션의 조건이 성립되게 해 주었는데요

     

    아직 원하는 Damage의 애니메이션을 찾지 못해서 믹 사모 홈페이지를 이용해 animation을 찾고 있습니다.

    애니메이션을 찾은 뒤  yield return new WaitForSeconds를 이용해

    애니메이션의 지정 초만큼 지정해 주어 기다리게 해 주면

     

    Damage를 입었을 시 Damage 애니메이션의 실행이 끝난 후 다시 움직이게 될 것입니다.

     

    마지막으로 yield return null을 이용해 제어권을 넘겨주는 모습입니다.

     


    그렇다면 이제 좀비가 HitEnemy 함수에서 hp_Now가 0 이하일 시 죽음 상태로 간주하여 

    실행될 Die 함수를 보겠습니다.

    Die함수는 StopAllCoroutines를 이용해 실행 중인 모든 코 루틴을 [중지] 시켜 줍니다.

     

    StopAllCoroutines과는 다르게 StopCoroutines("string")으로 일정 코루틴 함수만 중지시킬 수 있습니다.

     

    마지막으로 

     

    StartCoroutine를 이용해 "DieProcess" 코 루틴 함수를 실행시켜 줍니다.

     

    SetTrigger를 이용해 "Die" 애니메이션을 실행되게 트랜지션의 조건을 성립하게 해 줍니다.

    Zombie의 Animator

    이후 yield return new WaitForSeconds(3f)를 이용하여 3f초만큼 Die애니메이션의 

    진행될 시간만큼 멈춰 줍니다.

     

    gameObject.SetActive를 false로 바꿔주어 비활성화를 해 주며

    ani.StopPlayBack()으로 실행 중인 애니메이션을 멈춰 줍니다.

     

    아래 전투 중임을 체크할 불 변수 inCombat은 false로 바꿔 전투 중이 아님으로 설정해 주고

    이동 중인지 확인할 변수 onMove를 false로 바꿔주어 이동 중이 아님을 설정해 주었습니다.


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

    좀비가 리스폰이 되는 함수를 실행시켜 주는 코드입니다.

    우선 리스폰 함수의 인자 값은 (좀비, 초기위치, 소멸 시간, 생성시간)으로 나누어집니다.

     

    소멸 시간과 생성 시간을 지정해 주기 위해

    float 값으로 변수를 선언해 주었으며

    인스펙터 창에서 제어가 가능하게 public으로 선언하였습니다.

     

    Respawn함수를 실행시키는 코드를 보겠습니다.

    타 매니저들에게 접근을 하려면 Manager(대장) 에게 먼저 접근을 시켜 주어

    타 매니저들에게 접근을 용이하게 만들어 줬습니다.

     

    매니저들을 싱글톤 패턴으로 인스턴스화 해서 접근해도 좋은 접근 방식이지만

     

    하나하나 늘어가다 보면 언젠가 코드 들은 쌓이게 되고

    싱글톤 인스턴스가 너무 많은 일을 하거나 많은 데이터를 공유시킬 경우

    다른 클래스의 인스턴스들 간에 결합도가 높아져 "개방 - 폐쇄 원칙"을 위배하게 된다고 합니다.

     

    따라서 수정도 어려워지고 테스트하기도 어려워지는 점 까지 존재하니, 

    싱글톤 패턴은 너무 남발하지 않고 지양할 필요가 있을 것 같습니다.

     

    하지만 저같이 많은 것을 배워야 하는 입장에서는 상당히 편리하다는... 적당히가 애매합니다....

     

    네 무튼.. 음 

     

    Manager 스크립트만 독단적으로 싱글톤 패턴을 사용하였습니다.

    public으로 타 매니저들을 받아와 주는 코드이며

    public ManagerSe managerSe
    public ManagerResoawn managerRespawn

     

    public static Manager instance = null로

    Manager를 인스턴스화 시켜주는 코드입니다.

     

    두 매니저가 현재 존재하는데 

    ManagerSe의 사운드 매니저부터 보겠습니다.

    아직 Player에 대한 사운드들 밖에 없습니다.

    AudioSource를 audio로 받아와 주는 모습이고.

    public AudioClip player_Step : 걸음 사운드
        public AudioClip player_Jump : 점프 사운드
        public AudioClip player_Rifle : 총 사운드
        public AudioClip player_Rifle_Spark : 총 스파크 사운드 

    입니다.

     

    Player_Step() 함수는 플레이어의 걸음에 맞춰 실행해 보기 위해 테스트 용도로 만들었던 함수인데

    괜찮게 잘 만든 것 같습니다. PlayOnShot함수로 player_Step을 실생 시키게 해 뒀으며

    그럼 정말 마지막으로 ManagerRespawn 스크립트를 보겠습니다.

    우선 Respawn(GameObject monster, Vector3 pos, float vanishTime, float respawnTime)부터 보겠습니다.

     

    Respawn함수의 인자 값은 (게임 오브젝트, 위치, 소멸 시간, 생성시간)입니다.

     

    이 입력받은 파라미터 값을

     

    StartCoroutine을 이용해 코 루틴 함수 RegenMonster의 인자값에 넣어 실행시켜 줍니다.

     

     

    코루틴 함수인 RegenMonster()의 인자 값도 똑같습니다. (게임 오브젝트, 위치, 소멸 시간, 생성시간)

     

    이 값에 저는 (zombie, 좀비 초기위치, 소멸시간, 생성시간)을 넣어 주었었습니다.

     

    RegenMonster의 게임 오브젝트 = 좀비입니다.

     

    첫 번째 인자 값인 monster를 GetComponent를 이용해 Zombie 스크립트를 받아 와 줍니다.

     

    1.vanishTime(소멸 시간)을 기다립니다.

    yield return new WaitForSecondsRealtime(vanishTime)

     

    2. 소멸 시간이 끝나면 SetActive(false)로 비활성화시켜 줍니다.
    monster.SetActive(false)

     

    3.respawnTime(생성시간)을 기다립니다.
    yield return new WaitForSecondsRealtime(respawnTime)

     

    4. 생성 시 간이 끝나면 monster(zombie)의 위치는 pos(초기위치)로 설정해 주고
    monster.transform.position = pos

    - SetActive(true)로 활성화를 시켜 줍니다.
    monster.SetActive(true)

     

    if (monster.tag == "Zombie") 문으로 태그가 Zombie인지를 판단해 줍니다.

    현재 만드는 Zombie 말고도 slowZombie, powerZombie 등 제작을 할 것이기에 태그를 검사하여 

    현재 어떤 좀비인지 확인을 합니다.

     

    만일 monster(zombie)의 tag가 "Zombie"가 맞다면

     

    받아온 zombie의 현재 체력이 최대 체력이 아니라면? 의 조건문을 걸어 주어 한번 더 확인을 해줬습니다.

     

    zombie.hp_Now = zombie.hp_Max : 현재 체력을 최대 체력으로 바꿔 줍니다.
    zombie.zombieState = Zombie.ZombieState.Idle : 좀비의 상태 값을 Idle(초기) 상태로 만들어줍니다.

     

    이렇게 공격을 맞거나 추적 범위에 들어오면 Player를 향해 빠른 속도로 달려오며

     

    죽었을 시 일정 시간이 지난 후 되살아나서 초기 상태로 돌아가는 Zombie가 완성이 되었습니다.

     

     

    00 : 28초

     

    좀비를 5 - 6마리 생성해서 영상으로 보겠습니다.

     

    다음엔 앞으로 엎드려 있는 좀비로, 다가오는 속도는 느리지만 공격력이 강한 좀비를 만들어 보겠습니다.

     


    추가적으로 아주 초보적인 실수인데,, 제가 클래스 명을 오타를 내어 변경한 곳이 있습니다.

    그 이후 생기진 않던 오류가 발생하여 한참을 찾았는데..

     

    우선 Manager에 붙은 Missing Message는 해결 나서 버그가 걸린 줄 알았습니다.

     

    근데 클래스 명과 스크립트의 이름이 달라서 발생한 문제로.. 정말 간단하지만 

     

    절대 잊지 말아야 할 부분인 것 같습니다.

    댓글

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