전체 페이지뷰

2017년 8월 4일 금요일

Unity Tutorial: Survival Shooter, part 5

Spawning Enemies



적의 캐릭터에는 두 가지가 더 있습니다. ZomBear와 Hellephant인데 이 프리팹이 Asset>_Complete-Game>Prefabs에 있습니다만 이 영상은 2014년에 만들어진 것이고 그때와는 유니티가 많이 달라졌습니다. 영상과 환경이 다르므로 모델을 가지고서 처음부터 만들어도 되겠습니다만 시간 낭비라 생각되어 위의 폴더에 있는 프리팹을 수정해서 사용하겠습니다. 제 마음대로 하는 것이라 맞을지 모르겠네요.

ZomBear는 Zombunny와 모양은 다르나 굉장히 유사합니다. 많은 수정을 거치지 않고도 사용이 가능합니다. 먼저 위의 폴더의 프리팹을 드래그하여 Hierarchy에 옮겨 씬에 배치합니다.


그냥 이렇게 끌어다 놓으면 오리진 위치로 가기 때문에 플레이어 캐릭터와 겹쳐 조작하기가 어렵습니다. Position은 3, 0, 0 정도로 옮겨서 보기 쉽게 해 놓고, 실행해 보니 당연하게도 NullReferenceException이 납니다. 먼저 EnemyAnimatorController로 되어있는 에니메이터 컨트롤러를 우리가 만든 EnemyAC로 바꾸어주고, 등록되어 있는 네 개의 스크립트와 Audio Source를 Remove Component로 다 지웁니다.


그리고 스크립트와 오디오 소스를 우리의 것으로 다 다시 등록하려 합니다.

먼저 Add Component>Audio>Audio Source를 하고 AudioClip은 ZomBear Hurt로 정하고 Play On Awake는 체크 해제합니다.


그리고 역시 Add Component>Scripts를 선택하고, Enemy Movement, Enemy Attack, Enemy Health의 세 가지를 모두 추가합니다.


이 시점에서 플레이 해보면 어떠한 에러도 없이 작동되는 것을 알 수 있습니다. 이제 이 완성된 ZomBear 오브젝트를 원래의 Assets>_Complete-Game>Prefabs가 아니라 우리의 Zombunny를 저장했던 Assets>Prefabs 폴더로 끌어놓아 다시 프리팹화하고 Hierarchy에서는 지웁니다.

다음은 Hellephant 차례인데 이 캐릭터는 Zombunny, ZomBear와 조금 다릅니다. 일단 형태가 다르고, HP 데미지 등이 다릅니다만 취하는 행동은 Move, Idle, Death로 같습니다. 물론 그 때 사용되는 에니메이션도 다르지요. 이것들을 콘트롤 하기 위해 Zombunny때 했던 것처럼 완전히 새로 에니메이션 컨트롤러를 만들수도 있지만, 여기서는 보다 간편하게 Animator Override Controller라는 것을 사용해 보기로 합니다.

Project 창에서 Animations 폴더를 선택하고 Create>Animator Override Controller를 선택한 후 이름을 HellephantAOC라고 하겠습니다.


그리고 Hellephant의 인스펙터창에서 오버라이드할 컨트롤러를 드래그하거나 옆의 동그라미를 눌러 선택해 넣습니다. 여기서는 EnemyAC가 되겠지요.


그러면 각 상태가 나타나고 상태에 맞는 동영상을 에니메이션을 선택할 수 있게 됩니다. 헬리펀트의 동영상은 Models>Characters에 있는 Hellephant 모델을 펼쳐 보면 나옵니다. 각각 연결해 줍니다. 이렇게 해서 헬리펀트용의 에니메이터 컨트롤러가 만들어 졌습니다.

이제 다시 ZomBear 때처럼 _Complete-Game>Prefabs 폴더에서 Hellephant 프리팹을 Hierarchy창에 끌어놓고 위치를 3, 0, 0으로 해 줍니다. 에니메이터 컨트롤러를 새로 만든 HellephantAOC로 정해주고 Audio Source와 스크립트들을 지웁니다.


그리고 Add Component>Audio>Audio Source를 추가하고 AudioClip을 Hellephant Hurt로 정한 후 Play On Awake는 체크 해제 합니다.

Add Component>Scripts 하고 역시 Enemy Health, Enemy Attack, Enemy Movement의 세 스크립트를 추가하고 플레이 해봅니다. 그리고, Enemy Health의 Starting Health를 300, Score Value를 50, Death Clip은 Hellephant Death로 변경하고, Enemy attack의 Attack Damage는 30으로 변경합니다.



문제 없이 동작하는게 확인 되면 역시 Assets>Prefabs 폴더로 드래그하여 프리팹화하고 Hierarchy에서는 지웁니다.

이제 세 종류의 적이 모두 준비되었습니다. 이 적들을 생성해 줄 매니저가 필요합니다.

Hierarchy 창에서 Create>Create Empty 하여 빈 오브젝트를 하나 만들고 EnemyManager라는 이름을 붙여줍니다. Reset하여 오리진 위치로 가게 하고 이미 Scripts>Managers 폴더에 작성되어져 있는 EnemyManger라는 스크립트를 이 빈 오브젝트에 연결합니다.

using UnityEngine;
public class EnemyManager : MonoBehaviour
{
    public PlayerHealth playerHealth;
    public GameObject enemy;
    public float spawnTime = 3f;
    public Transform[] spawnPoints;
    void Start ()
    {
        InvokeRepeating ("Spawn", spawnTime, spawnTime);
    }
    void Spawn ()
    {
        if(playerHealth.currentHealth <= 0f)
        {
            return;
        }
        int spawnPointIndex = Random.Range (0, spawnPoints.Length);
        Instantiate (enemy, spawnPoints[spawnPointIndex].position, spawnPoints[spawnPointIndex].rotation);
    }
}
cs


에디터로 열어서 확인해 보면 간단한 코드로 구성되어 있습니다. 몇 개의 퍼블릭 변수가 보이는데 이것은 여러 개의 적들이 동시에 사용하는 코드이므로 에디터에서 드래그하여 연결해줄 것입니다. Transform[]이라는 배열이 보이는데 이 예제에서는 인스턴스 당 하나의 스폰 포인트만 사용할 것입니다(스폰포인트는 나중에 따로 만듭니다). 만약 여러 개의 스폰포인트를 만들고 싶다면 저것을 사용해서 추가로 만들어 볼 수 있을 것입니다.

게임이 시작되면 Start()내에서 InvokeRepeating()이라는 메소드를 사용해 "Spawn"이라는 메소드를 spawnTime부터 spawnTime 간격으로 호출합니다.

Spawn()에서는 먼저 플레이어의 체력을 체크하여 0 이하면 더 이상 스폰하지 않고, 0 이상이면 spawnPoints라는 배열 중 랜덤으로 하나를 골라 적을 인스턴스화합니다.

스크립트를 저장하고 유니티로 돌아갑시다.

이제는 스폰포인트를 만들어줘야 합니다. 먼저 Zombunny용 스폰포인트를 만듭시다. Hierarchy 창에서 Create>Create Empty하여 빈 오브젝트를 만들고 이름을 ZombunnySpawnPoint라고 짓습니다. 우리는 이 포인트를 좀버니가 나올 곳으로 가져다 놓을 것입니다.

우선 이것은 빈 오브젝트이므로 보기 쉽게 하기 위해 인스펙터 창 옆의 칼라큐브를 클릭하여 색을 파란색으로 지정합니다.


그러면 씬뷰에서 파란색으로 이름이 표시된 것이 보입니다. 그리고 좀버니의 트랜스폼을 그림과 같이 지정하여 스포닝포인트를 정확히 잡아줍니다.



다음은 ZomBear차례입니다. 새로 만들어도 되겠지만 이번에는 복제하여 사용하려고 합니다. ZombunnySpawnPoint에 우클릭하고 Duplicate를 선택하여 하나를 복제합니다. 이름을 ZombearSpawnPoint로 바꾸고 또 아래 그림처럼 위치와 로테이션을 설정합니다.


Hellephant 스폰포인트도 같은 방법으로 아래 그림 위치에 설정합니다.



스폰포인트 설정은 끝났고 이제 다시 Hierarchy에서 Enemy Manager를 선택합니다. 인스펙터창에서 Player Health 프로퍼티에는 Hierarchy의 Player를 드래그하여 연결하고, Enemy에는 Assets>Prefabs 폴더에서 Zombunny를 드래그하여 연결합니다. 그리고 Spawn PointsZombunnySpwanPoint를 드래그하여 연결해주면 Element 0이라는 프로퍼티가 자동으로 생성되면서 연결됩니다.



이것은 Zombunny 스폰포인트에 관한 것이었고, ZomBear 스폰포인트를 설정하려면 Enemy Manager 오브젝트에 Enemy Manager 스크립트를 하나 더 추가합니다. 그리고 거기에 같은 방법으로 ZomBear를 연결합니다.


Hellephant도 마찬가지로 추가합시다. 다만 Spawn Time을 10으로 해줍니다. 플레이 해보면 세 종류의 적들이 각각 다른 스폰 포인트에서 생성되어 쫓아오는 것을 확인할 수 있습니다.



Game Over



이제 게임이 끝날 수 있게 해야 합니다. 그것을 위해 2D 모드로 전환하고 HUDCanvas에 Frame Selected 해 놓습니다.

이 HUDCanvas에 우클릭하고 UI>Image하여 이미지를 추가한 뒤 이름을 ScreenFader라고 짓습니다. 그리고 앵커 프리셋을 활성화하고 ALT키를 누른 상태에서 우하단을 선택해 캔버스 전체로 이미지를 넓힙니다.



이제 이미지가 화면 전체를 가렸을 겁니다. 그리고 그 색을 옅은 파랑으로 대략 조절해 놓습니다.

다음으로 다시 HUDCanvas를 선택하고 UI>Text를 추가하고 GameOverText라고 이름 지어줍니다. 그리고 앵커 프리셋을 활성화하고 ALT키를 누른 상태에서 가운데를 선택합니다.



다음으로 인스펙터 창에서 몇가지 프로퍼티들을 변경하고, Shadow 컴포넌트를 추가하고 역시 프로퍼티들을 아래 그림과 같이 수정합니다.



그리고 HUDCanvas의 자식들 순서를 재배열해야 합니다. 그것들은 알파벳순으로 나열되어 있는 것이 아니라 렌더링 되는 순서대로 나열되어 있는 것이기 때문입니다.

HealthUI, DamageImage, ScreenFader, GameOverText, ScoreText의 순서로 드래그하여 옮겨 줍시다.


이 상태에서는 씬이 전부 가려지기 때문에 ScreenFader를 선택하고 알파를 0으로 바꿉니다.


그리고 GameOverText의 칼라 알파도 0으로 바꿔놓습니다. 그러면 씬의 화면이 플레이 중의 화면 모습으로 보입니다.

이제 이 게임오버 화면에 에니메이션을 적용하고 게임오버용의 state machine을 만들 것입니다. 지금까지는 이미 에니메이션이 적용된 FPX 파일을 사용했기 때문에 animator 컴포넌트가 함께 붙어서 나왔고, 컨트롤러를 만들어서 적용하기만 하면 되었습니다. 많은 사람들이 착각하는 것이 에니메이터는 캐릭터 움직임에만 적용된다고 생각하지만, 사실은 유니티의 그 어떤 것에도 적용될 수 있습니다. 에니메이션이라 함은 캐릭터의 움직임 뿐 아니라 색의 변화, 켜고 끔, 위치의 변화 등 그 모든 것을 지칭합니다. 우리의 이 UI화면에도 마찬가지로 에니메이션을 적용할 수가 있습니다.

자식 전부에 접근할 수 있도록 HUDCanvas를 선택하고 Windows>Animation을 선택(Animator아님)합니다.

에니메이션 창이 생겨나면 드래그하여 게임뷰 옆으로 도킹시켜 놓습니다.

이 에니메이션 편집창 가운데에 에니메이팅을 시작하려면 파일을 먼저 생성하라는 말과 함께 버튼이 있습니다. 그것을 누르고 Assets>Animation 폴더에 GameOverClip이라는 이름으로 새 파일을 생성합니다.


그리고 에니메이터 창의 상단의 레코딩 버튼을 누르면 에니메이션을 만들 준비가 됩니다. 이제 Add Property 버튼을 누릅시다.

그러면 추가할 수 있는 프로퍼티들이 나타나는데 GameOverText>Text>Color를 선택합니다.

그러면 0초와 1초에 키프레임이 나타납니다.

같은 방식으로 GameOverText>Rect Transform>ScaleScreenfader>Image>Color, ScoreText>Rect Transform>Scale 프로퍼티도 추가합니다.


다음으로 1초에 있는 키프레임중 가장 윗 마름모를 클릭해서 전체를 선택하게 하고 드래그하여 30 프레임으로 가져다 둡니다.


그리고 타임라인을 뜻하는 빨간 선은 드래그하여 20프레임에 가져갑니다(숫자 부분을 클릭하면 드래그 가능해집니다).



그 상태에서 옆의 프로퍼티 중 GameOverText: Scale을 클릭해두고 K키(키프레임을 뜻함)를 누르면 그 타임라인에 해당 프로퍼티가 추가됩니다.


이제 타임라인(빨간선)을 다시 0으로 옮기고 GameOverText: Scale을 펼쳐서   Scale.x, y, z를 전부 0으로 적습니다.



타임라인을 20으로 다시 옮겨가봅니다. 이 때 인스펙터창을 보면 Scale이 붉은색으로 표시되어 있는데 크기를 모두 1.2로 바꿉니다(물론 에니미이션 편집창에서 해도 됩니다).


이제 이 텍스트는 0프레임에서 안 보이다가 20프레임에는 원래의 120% 크기로 켜졌다가 30프레임에는 원래의 크기로 돌아갑니다.

다음은 GameOverText: Color 차례입니다. 처음에는 안 보이다가 점점 보이기 시작하여 30프레임에는 완전히 보이도록 하겠습니다. 알파값이 처음에는 0이므로 30프레임에서만 1로 바꾸면 되겠습니다. 타임라인을 30프레임에 두고 Color.a를 1로 설정합니다. ScreenFader의 알파값도 마찬가지로 30프레임에서 1로 설정합니다.



ScoreText는 반대로 마지막에 GameOver가 좀더 부각되도록 크기를 줄여주는 게 좋겠습니다. 30프레임에서 0.8로 해줍니다.


에니메이션창의 플레이 버튼을 눌러보면 원하는 효과가 잘 나타나는 것을 확인 가능합니다. 그런데 이 상태로는 플레이어가 죽자마자 너무 빨리 에니메이션이 시작합니다. 다라서 전체 에니메이션을 조금 뒤로 옮기려고 합니다.

타임라인 전체를 드래그해서 선택하고 프레임을 1:30으로 끌어다 놓습니다(1:30이 잘 안 보이면 타임라인 창에서 마우스휠로 크기 조절이 가능합니다).



이제 다시 레코드버튼을 눌러 에니메이션 편집을 끝냅니다.

게임 오버 에니메이션이 완성되었습니다. 그런데 디폴트가 무한 반복이므로 해제해야 합니다. 프로젝트 창에서 Assets>Animation>GameOverClip을 선택하고 인스펙터창의 Loop Time을 체크 해제합니다.

다시 Hierarchy창에서 HUDCanvas를 선택해 봅시다. 인스펙터 창을 보면 Animator 컴포넌트가 들어가 있는게 보이고, 그 콘트롤러는 HUDCanvas라는 이름으로 생성되었습니다. 이 HUDCanvas를 더블클릭하여 Animator 창을 엽니다.

현재는 플레이어가 죽는 것을 판정해 주는 도구가 없으므로 게임을 플레이해보면 바로 게임오버 메시지가 나옵니다. 이것을 콘트롤하기 위해 에니메이터 창에 우클릭 후 Create State>Empty를 선택해서 New State를 하나 만듭니다.


이름은 Empty로 바꿔주고 우클릭하여 디폴트로 지정합니다.


그리고  우클릭, Make Transition하여 GameOverClip으로 이어주고 GameOver라는 Trigger타입의 파라미터를 하나 만듭니다.


Empty와 GameOverClip 사이를 이어주는 트랜지션을 선택하고 Conditions를 지금 만든 GameOver 트리거로 지정합니다.


자 그리고 이제 Assets>Scripts>Managers>GameOverManager를 드래그해서 Hierarchy창의 HUDCanvas에 연결합니다.


이 스크립트를 에디터로 열어봅시다.

using UnityEngine;
 
public class GameOverManager : MonoBehaviour
{
    public PlayerHealth playerHealth;
 
 
    Animator anim;
 
 
    void Awake()
    {
        anim = GetComponent<Animator>();
    }
 
 
    void Update()
    {
        if (playerHealth.currentHealth <= 0)
        {
            anim.SetTrigger("GameOver");
        }
    }
}
cs

영상에 나와있는 레벨을 재로딩 하는 것과 관련된 코드는 모두 없어졌습니다. 대신 그것은 PlayerHealth 스크립트에 RestartLevel()이라는 메소드로 들어가 있습니다. 여기선 단순히 HP가 0 이하로 떨어지면 에니메이터가 작동하도록 만들었을 뿐입니다.

저장 후 유니티로 돌아와서 Player Health 프로퍼티에 Hierarchy의 Player를 드래그해서 연결한 후, 마지막으로 Hierarchy창의 BackgorundMusic을 선택하고 인스펙터의 Play On Awake를 선택해서 음악을 깔고 실행해 봅시다.

이제 모든 것이 잘 작동합니다.

댓글 1개: