[Unreal] 자습서 5장 - 블루프린트로 슈팅게임 만들기(2)
인트로
이번 장에서는 적을 생성하고 적, 총알, 플레이어 간의 충돌 처리도 구현할 것이다. 완성 후 실행화면은 아래와 같다.
적(Enemy) 제작하기
적 액터 제작
우선 적 역할을 할 BP_Enemy 블루프린트를 액터를 상속받아 만들어준다.
적도 우선은 큐브 형태로 표현하겠다.
적은 생성이 되면 아래로 내려간다. 플레이어보다 빠르게 이동속도 변수에 800을 주고 총알에서 구현한 코드와 똑같이 코드를 작성하면 된다.
적 공장 액터 제작
적들은 계속 일정한 시간 간격으로 생성이 되어야 한다. 이 공장 역할을 할 BP_EnemyFactory 블루프린트 클래스를 액터를 상속받아 생성한다.
BP_EnemyFactory는 생성하는 역할만 하기 때문에 눈에 보일 필요가 없으므로 따로 외형 컴포넌트는 추가하지 않는다. 액터 생성 방향을 플레이어 쪽인 아래 방향으로 설정하기 위해서 Arrow Component를 추가하고 Transform 속성의 Y축을 -90도 회전시키고 Z 축으로 180도 회전시킨다. 그러면 Arrow 화살표가 아래를 바라보게 된다.
이벤트 그래프로 들어가서 [Event Tick] 노드에서 Delta Seconds를 현재 시간과 계속 더해주고 그 값이 우리가 설정해 준 시간보다 크거나 같으면 [Spawn Actor from Class] 노드를 통해 BP_Enemy 액터를 생성해 준다.
Delta Seconds 는 1 프레임이 경과할 때마다 소요시간이다. 매프레임에 걸린 소요 시간을 누적하면 앱이 시작된 때로부터 현재 프레임까지의 총 경과 시간이 된다. 경과 시간이 지정된 시간보다 같을 때가 아니라 크거나 같을 때로 조건을 지정한 이유는 DeltaSeconds는 소수값으로 되어있기 때문에 1.0과 같이 딱 맞아떨어지는 경우가 거의 없어서 크거나 같다고 조건을 지정한 것이다.
메인 화면으로 나와 BP_EnemyFactory를 화면 위쪽에 일렬로 5개 배치한다. 실행해 보면 적들이 생성되어 아래를 향해 내려오는 모습을 볼 수 있다.
여기서 너무 적들이 동일한 시간에 똑같이 나오는 것이 심심하게 보이기 때문에 각자 지정 시간을 다르게 주어 생성 주기가 다르도록 설정해 주었다. 실행을 해보면 아래와 같이 다른 주기로 적들이 생성되어 내려오는 것을 확인할 수 있다.
적의 이동 방향 랜덤으로 주기
다음으로는 그냥 일직선으로 이동하는 적들의 이동을 확률에 따라 랜덤하게 플레이어 쪽으로 오거나 아니면 그냥 일직선으로 내려가거나 하는 방법으로 구현해 보겠다. [Random Float in Range] 노드를 이용하여 0~1 사이의 실수값을 출력하도록 하고 이 값이 TraceRate 변수보다 작으면 엑터의 이동 방향을 플레이어의 위치 방향으로 바꾸도록 하였다.
함수 노드 [Random Float in Range] 는 개발자가 설정한 최솟값과 최댓값 사이의 임의의 실수 값을 결과로 반환하는 함수이다. TraceRate 변수의 값을 0.35로 설정하면 적이 플레이어 방향으로 날아올 확률이 35%라는 의미이다.
플레이어의 방향으로 날아오게 하는 방법은 [Get Actor of Class] 함수 노드를 이용하여 BP_Player 액터를 찾아내고, [GetActorLocation] 노드를 이용하여 BP_Player의 위치를 받아올 수 있다. 플레이어의 위치벡터에서 적의 위치벡터의 차는 서로에게 이어지는 벡터가 생성되므로 이 결과를 방향으로 설정해 주면 된다.
메인으로 나와서 실행해보면 35%의 확률로 플레이어를 향해 이동하는 Enemy의 움직임을 확인할 수 있다.
충돌(Collision) 처리하기
이제 적들은 생성이 되었지만 아직 총알에 맞아서 파괴되지도 않고, 플레이어가 적에 부딪혀도 아무런 일이 일어나지 않는다. 총알과 적이 충돌해서 서로 폭발하고, 적과 플레이어가 충돌하면 플레이어를 폭발시키는 기능을 구현해 보도록 하겠다.
Collison 생성
액터들 간에 충돌을 발생시키기 위해서는 충돌 영역인 '콜리전(Collision)'을 지정해주어야 한다. 총알, 적, 플레이어 블루프린트 설정 창에서 각각 Box Collison을 생성하고 Mesh에 맞게 우측 Box Extent 값을 조절해 준다.
각각 Box Collision을 생성하였으면 액터들 간에 Collision Group을 지어주도록 한다. Project Setting으로 들어가서 좌측 목록에서 Collision 탭을 선택한다. Object 채널에서 [ New Object Channel] 버튼을 클릭하여 Player, Enemy, Bullet 채널을 추가해 주도록 한다. Object 채널은 Collision 간 충돌, Trace 채널은 Ray Tracing 기능을 이용한 충돌을 처리할 때 사용한다.
다음으로는 각 액터마다 개별 충돌 설정을 할 것이다. BP_Bullet 블루프린트 창을 열고 Box Collision을 선택하고 우측 Details 패널에서 Collision 속성을 보면 Collision Presets 가 보인다. Collision Presets는 언리얼 엔진에서 자주 사용하는 충돌 설정을 몇 가지 미리 만들어 놓은 프리셋이다. 설정을 아래와 같이 변경한다.
총알은 적과 충돌해야 하므로 Enemy만 Overlap을 체크하고 나머지는 Ignore로 설정해 준다. Block이 아닌 Overlap을 사용하는 이유는 만일 총알이 적과 충돌했다면 바로 폭발해서 사라지도록 처리할 것이기 때문에 충돌 감지만으로 충분하기 때문이다. 마찬가지로 플레이어와 적 블루프린트도 서로 작용할 채널만 Overlap 체크해 준다.
충돌 이벤트 구현
충돌 이벤트는 [ActorBeginOverlap] 이벤트 함수를 통해 실행된다. 액터와 충돌하게 되면 함수의 Other Actor 출력핀으로 충돌한 상대방 엑터의 정보를 반환한다. 반환 결과로 얻는 정보 값은 부모인 Actor 클래스 상태로 넘어오기 때문에 그 대상이 구체적으로 특정 자식 클래스로 확인 및 전환하기 위해서는 Cast 과정을 거쳐야 한다. 총알에 충돌할 액터의 클래스는 BP_Enemy 클래스이기 때문에 Other Actor 출력 핀을 [Cast To BP_Enemy]와 연결해 준다. 부딪힌 대상이 적이라면 적 액터를 [Destroy Actor] 노드를 통해 삭제하고, 그 위치에 폭발 파티클을 생성해 준다. 파티클 생성은 [Spawn Emitter at Location] 노드를 통해 생성된다. 파티클 생성이 완료되었으면 총알 자신또한 [Destroy Actor] 노드로 삭제시킨다.
실행을 해보면 총알에 맞은 적과 총알은 폭발 효과와 함께 사라지는 모습을 볼 수 있다.
적의 동작도 총알과 비슷하다. Other Actor 출력 핀을 [Cast To BP_Player]라고만 변경해 주면 된다. 그런데 플레이어와 충돌하여 플레이어가 삭제되었을 경우 플레이어의 위치값을 받아오는 것에 문제가 생기므로 이동방향을 설정하는 [Set Direction] 노드 전에 [Is Valid] 노드를 추가한다. [Is Valid] 노드는 결과로 반환되는 오브젝트가 유효한 것인지를 확인하는 오브젝트 유효성 검사 노드이다.
킬 존(Kill Zone) 제작
적이 플레이어를 지나쳐 화면 밖으로 나가면 삭제되지 않은 채 계속 떠돌아다닐 것이다. 이렇게 되면 컴퓨터의 메모리 공간을 다 차지하기 때문에 불필요한 액터를 제거하기 위해 킬 존(Kill Zone)을 만들어주겠다.
먼저 BP_KillZone 액터 블루프린트를 생성해 준다.
킬 존 또한 충돌처리를 해야 하므로 위에서와 같이 채널을 추가해 주고 Box Collision 역시 추가해 준다.
Collision 속성은 Player와 자기 자신인 KillZone만 제외하고 모두 Overlap 체크를 해준다. 플레이어가 나가서 파괴되는 일이 없도록 하기 위함이다.
코드는 매우 간단하다. 충돌을 하면 닿은 액터를 파괴시키면 된다.
이렇게 설정을 하고 실행을 해보면 플레이어를 Ignore로 체크해 주었음에도 불구하고 플레이어가 킬 존에 닿으면 파괴되는 것을 확인할 수 있다. 이 이유는 플레이어 큐브 메시에 Collision이 부착되어 있기 때문이다. 큐브 컴포넌트를 선택하여 Collision Presets 값을 NoCollision으로 바꾸어주면 이 문제는 해결된다.
아웃트로
이번 장에서는 적을 구현해서 충돌처리까지 구현해 보았다. 다음으로는 배경과 각 액터에게 외부 모델링을 적용해 보겠다.