ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 팀업 프로젝트 02. Enemy의 상태 구현 및 Time.deltaTime
    팀업 프로젝트/Night Raid
     

    주기적으로 Enemy의 상태를 체크하기 위한 Update 함수에서의 siwtch문입니다.

     

    우선 Slider를 사용하려면 네임스페이스 using UnityEngine.UI를 선언해 주어야 합니다.

     

    Enemy의 체력을 나타내 주는 slider를 주기적으로 확인하기 위하여 Update 함수에서 Slider를 담은 변수 hp_bar의 value 값을 현재 hp / 최대 hp로 나누어 0.0f - 1.0f의 value 값을 만들게 하였습니다.


     

    Enemy의 Move함수입니다.

    첫 번째 if문에서 Vector.Distance 함수를 사용하여 Enemy의 위치화 초기 위치의 거리가 최대 움직일 수 있는 moveDis보다 크다면, Return(복귀) 상태로 변경하게 해 주었고,

     

    else if문에서는 최대 움직일 수 있는 거리를 벗어나지 않았다는 조건 하에 이루어 지기에, Enemy의 위치와 player의 거리가 atckDis 보다 크다면 Idle에서 넘어온 상태인 추적 상태인 것으로 판단했고,

    첫 번째로 사운드 효과를 주었습니다.

     

    불 변수 isSound로 Update에서 실행되는 함수기에 한 번만 실행하도록 중복 방지를 해 주었고,

    static 싱글톤 패턴을 이용한 하나의 Manager의 함수에서 managerSe를 호출하여 PlayOnShot 함수로 재생시켜 주었습니다.


     

    우선 싱글톤 패턴으로 만든 Manager의 스크립트입니다.

    public statuc Manager instance = null로 인스턴스화를 시켰고

    초기에 한 번만 실행되는 Awake 함수에서 instance가 this (이 스크립트)가 아니라면

    this로 지정해 주었습니다.

     

    이렇게 싱글톤 패턴으로 정해주면 다른 클래스에서 접근하기가 용이해지는 장점이 있습니다.

     

    또한 부 각 담당의 부 매니저들을 스크립트에 선언해 주어 각 담당 manager 들에게 접근하는 것을 용이하게 만들어 주었습니다.

    Move 함수의 PlayOneShot 함수를 사용할 때도 이러한 방식으로 사용한 것입니다.

    각 담당을 하는 manager 중 하나인 ManagerSE의 스크립트입니다.

    ManagerSE에 Audiosource를 추가하여 담아 주었고, 각 재생할 스크립트를 public으로 선언하여 인스펙터 창에서 바로 담을 수 있게 하였습니다.

     

    Hierarchy 창 에 있는 대장 Manager 오브젝트와 각 담당을 맡은 부 Manger입니다.

    Manager의 인스펙터 창과

    ManagerSe의 인스펙터 창입니다.

     

    여기서 알게 된 점이 있는데, 우선

    첫 번째

    static을 많이 사용하게 되면 관리가 쉽다는 장점이 존재하지만, 대표적으로 메모리 낭비가 있다고 합니다.

    static은 컴파일 타임에서 메모리에 잡히고 Application이 끝나기 전까지 유지가 된다고 합니다.

    즉 프로그램 시작과 동시에 메모리에 잡히게 되어 사용량을 늘리게 되고 메모리 낭비가 된다고 합니다.

     

    두 번째

    Audio를 재생하기 위해선 AudioClip ( 재생 파일 ) 하고 AudioSource (오디오 플레이어) 두 가지 오브젝트가 필요한데, 단순히 사운드를 재생할 땐 전 영상을 통해 공부한 대로 PlayOneShot만 사용했었습니다.

    우연히 오디오 소스의 함수인 Play 함수를 사용되게 되는 계기가 있었는데,

    Play 함수는 중첩이 되면 가장 마지막만 출력이 되고,

    PlayOnShot 함수는 중첩이 되어도 동시에 출력이 가능하다는 점을 알게 되었습니다.

     

    그 외에도 오디오 소스의 함수는 상당히 많은 기능들이 있었는데,

    볼륨을 ( 0.0f - 1.0f )로 조절하는 Volume 함수

    오디오를 멈춰주는 Stop 함수

    사운드의 반복 여부를 체크하는 loof 함수

    등 아는 것이 힘이다라고, 알 면 알수록 다양한 동작 구현이 가능해질 것 같습니다.

     

    ----------------------------------------------------------------------------

     

    Move 함수의 두 번째 조건문인 else if 문을 좀 더 살펴보겠습니다.

    inCombat = true;

    전투 중을 확인하는 불 변수를 true로 바꿔주어 코 루틴 함수에서의 Move_NonCombat 함수를 막아주었고

     

    nav.isStopped = true;

    nav.ResetPath();

    Enemy의 Move함수는 Update함수에서 실행되기에 매 프레임마다 목적지의 위치를 향해 체크하면서 이동하기에, 공격 모션 중에 플레이어가 다른 곳으로 이동하여 Enemy가 미끄러지듯이 이동해 버릴 우려가 있기에

    isStopped로 nav의 움직임을 멈춰 주었고,

    ResetPath 함수로 경로를 초기화시켜 주었습니다.

     

    nav.stoppingDistance = atckDis;

    이후 다시 내비게이션으로 접근하는 최소 거리를 공격 가능 거리로 설정을 해 주었고

    nav.destination = player.position;

    player를 향해 목적지를 정해 주었습니다.

     

    ↓↓↓↓↓

     
     

    Move 함수의 모든 조건이 아닐 시 들어올 수 있는 else문입니다.

    상태를 Attack으로 변경되게 하였고, 애니메이터의 Trigger는 바로 Attack 상태가 아닌 AttackDelay 상태로 가게 하여 Attack 후의 Delay 시간 동안 어색함이 없도록

    공격 대기 모션으로 가게 했습니다.


    Attack 함수


     

    Enemy가 Attack 상태 일시 스위치 문에서 실행되는 Attack 함수입니다.

    Attack 상태가 되면 transform (Enemy)의 오브젝트의 transform을 회전시키는

    LookAt 함수로 player를 바라보게 해 주었고,

    첫 번째 조건문인 if문으로 Enemy와 플레이어의 거리가 공격 범위 안에 있다면

    공격을 실행되는 함수를 구현했습니다.

     

    curTime에 += 연산자를 사용하여 Time.deltaTime * 1을 누적시켜 주었는데,

    처음 Time.deltaTime을 접했을 시에는, 이해가 잘 가지 않아 각 컴퓨터마다 사양이 다른데 이를 곱해줌으로써 원하는 값만큼 똑같이 움직이게 해 준다. 정도로만 생각하고 습관적으로 사용했습니다.

     

    -----Time.deltaTime-----

    이번 기회에 Time.deltaTime을 좀 더 자세히 찾아보았습니다.

     

    Time.deltaTime은 "한 프레임당 실행 시간"을 뜻하며

    Time.deltaTime을 사용을

    매 프레임마다 호출되어 사용하는 Update 함수에 구현을 할 시

    FPS가 큰 컴퓨터는 이동거리가 좀 더 많을 것이며

    FPS가 작은 컴퓨터는 이동거리가 적게 되게 됩니다.

    이러한 요소를 해결할 수 있는 것이 바로 Time.deltaTime이라고 합니다.

     

    단순히 보자면 초당 프레임 시간인(FPS가 다를 수 있기 때문에

    프레임 단위로 처리되는 처리 속도를 동일하게 맞춰주기 위해 사용된다 이고,

     

    조금만 더 나아가 보자면, Time 클래스에 있는 deltaTime 변수는

    현재 프레임과 이전 프레임간의 시간차를 가지고 있으며

    30 FPS라면 1(초)/30(프레임)이고 * 30(속도)를 곱해주면 1 프레임당 1 만큼 이동하게 되고.

    60 FPS라면 1(초)/60(프레임)이고 * 30(속도)를 곱해주면 1 프레임당 0.5만큼 이동하게 된다

     

    30 프레임 = 30

    60 프레임 = 30

    으로 결괏값은 동일해지지만, 한 프레임에 더 멀리 나가는 30 FPS 컴퓨터는 렉이 걸리는 것처럼 뚝뚝 끊기는 현상이 나타날 수 있다는 점입니다.

    -----Time.deltaTime-----

     

    얘기가 길어졌는데, 위의 내용에서 curTime에 += 연산자를 사용하여 Time.deltaTimer를 곱해준 것은

    1초씩 누적되어진다는 것을 알 수 있습니다.

     

    이 curTime이 atkDelay (4) 초 보다 커지면, Attack Animation을 실행하게 해 놨고

    atkStep은 단순 int 값을 담을 변수로 Random.Range 함수를 통해 0-1의 값을 랜덤으로 받게 했습니다.

     

    또한 ani.SetFlot("Speed", Random.Range(1.0f,2.0f)) 를 통하여 스피드 값을 1,0f-2.0f로 랜덤으로 받게 하였고,

    랜덤으로 받은 atkStep값을 switch로 나누어 공격 패턴을 랜덤으로 받게끔 하였습니다.

    단조로운 공격 패턴을 조금이나마 보완하고자 스피드와 공격 값을 랜덤으로 받았습니다.

     

     
     

    그리고 마지막 else문에서는 공격 범위가 아니므로 Enemy의 상태를 Move로 바꿔 주었고,

    조건문의 진입 상태 및 예기치 못한 상황을 막아주기 위해 curTime은 0으로 초기화해주었습니다.

    이후 SetTrigger를 이용해 Animator의 트랜지션 방향이 Move로 전환되도록 하였습니다.

     

    Attack함수의 마지막으로 Attack, Attack02의 Animation인 Events 값에 Function으로 함수를 추가시켜 동작 모션에 맞게 공격 프리 팹을 켜 주었고, 항상 콜라이더를 켜 두면 의도치 않은 상황에 감지가 될 수 있기 때문에, 공격 모션에만 콜라이더를 켜고 끄게끔 만들었습니다.

     

    Attack02 Animation의 Events에 Add Event를 추가시킨 모습입니다.

    공격 모션에 맞게 SetActive함수를 활용해 콜라이더가 담긴 오브젝트를 활성화/비활성화를 구현해주는 함수입니다. 활성화/비활성화 가 되면서 추가되어 있는 콜라이더도 같이 활성화/비활성화 가 되어 지기에

    타이밍에 맞춰 켜고 꺼 주는 기능을 추가하게 됐습니다.

     

     

    이로써 findDis에 들어오면 Player를 추적하는 Move 함수의 구현과 Attack 함수의 공격 구현 기능을 player와 맞춰보며 성공적으로 마무리를 했습니다.

    추가적으로 맵은 무료 에셋으로 다운로드하였는데, 이동 경로가 너무 커서 맵의 배치의 수정하여 바꿔야

    겠습니다.

    댓글

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