
타격(Hit) 이벤트 발사체가 파괴되기 위해서는, 마지막에 부딪히는 시점을 알아야 한다. 따라서 부딪히는 방식을 알기 위해 히트 이벤트에 대해 다루게 된다. 발사체는 UStaticMeshComponent이고, 해당 컴포넌트의 부모는 UPrimitiveComponent이다. UPrimitiveComponent는 히트 이벤트 생성이 가능하고 여기서 OnComponentHit이라는 변수가 존재한다. 해당 변수는 구조체 타입이며, 구성은 다음과 같다. 그리고 이 OnComponentHit이라는 히트 이벤트 멀티캐스트 델리게이트라고도 한다. 멀티캐스트 델리게이트는 여러 개의 함수 바인딩이 가능하다. 또한 바인딩된 함수는 인보케이션 리스트라는 곳에 저장된다. 이 인보케이션 리스트가 히트 이벤트가 호출하라고 하면 호..

발사체 이동 컴포넌트 현재 발사체가 고정되어있는 상태이므로, 이름에 맞게 발사시켜주어야 한다. 이를 위해서는 직접 위치를 설정시킬 수 있고, 임펄스를 추가해줄 수도 있으나, 우리는 언리얼에 내장된 기능인 발사체 이동 컴포넌트를 사용하여 발사시켜줄 것이다. 이를 위해 우선 발사체 클래스에서 해당 컴포넌트를 생성해주어야 한다. 생성하고 나면 해당 컴포넌트를 통해서 최대속도 및 기초속도와 같은 변수에 접근할 수 있다. 해당 값들을 직접 설정해주자. 이제 컴파일을 해보면 발사체 블루프린트 클래스에 컴포넌트가 추가되었고, 초기속도와 최대속도 변수도 값이 설정된 것을 볼 수 있다. 이제 플레이를 해보면 탱크와 터렛 모두 발사체가 무사히 발사되는 것을 볼 수 있다. 하지만 설정된 속도만큼 이동한 이후에 여전히 멈춰있..

발사체 생성 이제 발사체가 생성되는 것을 구현해줄 차례이다. 우리는 발사체를 생성되는 것을 VScode 내의 Fire 함수를 통해 하게 된다. 이때 발사체에 적용한 메시를 알아야 하는데, 이는 언리얼 블루프린트를 통해서 설정한 정보이다. 이렇게 블루프린트에서 설정한 정보를 C++에서 접근하기 위해서는 TSubclassOf가 사용된다. TSubclassOf는 UClass 타입을 반환하게 된다. UClass 타입은 클래스 타입이지만 C++와 블루프린트 사이 정보를 교환할 수 있는 언리얼 엔진의 리플렉션 시스템과 상호작용이 가능하다. 우리가 액터를 생성하는데 사용할 SpawnActor 함수의 첫 번째 인풋 매개 변수가 이 UClass 타입을 사용하게 된다. 먼저 TSubcalssof를 사용하여 AProject..

Projectile 클래스 발사하기 위한 작업을 마쳤으므로, 실제 발사체를 만들어준다. 따라서 발사체에 대한 C++ 클래스를 먼저 생성한다. 월드에 배치되기만 하면 되기에, 부모 클래스로 액터를 선택해준다. 발사체는 메시를 필요로 하므로, 이에 UStaticMeshComponent 타입 포인터가 필요하다. 그리고 전에 몇 번 했던 것과 똑같이 CreateDefaultSubobject 함수를 통해 컴포넌트를 생성해준다. 그리고 발사체는 Tick에 대한 내용이 굳이 필요없으므로, 생성자에 있는 Tick을 false로 바꿔주었다. 이렇게 만든 클래스를 바탕으로 블루프린트 클래스르를 만들어주고, 해당 스태틱 메시 컴포넌트에 발사체 메시를 넣어주면 된다.

타이머 탱크에서 Fire가 가능하도록 해줬으니, 타워에서도 가능하게 해줄 차례이다. 타워는 탱크를 보면 알아서 Fire를 해야하므로 타이머를 사용하도록 하였다. FTimerManager라고 하는 클래스로 타이머 설정이 가능하며, 이는 GetWorldTimerManager() 함수를 통해 얻게 된다. 이후 SetTimer 함수를 통해 실질적인 타이머 설정을 하게 된다. 이때 필요한 인풋 매개 변수들이 있는데 우선 타이머를 관리하는 타이머 핸들이 필요하다. 이를 위해 따로 선언해주어야할 필요가 있으며, 마찬가지로 타이머가 실행되기까지 걸리는 시간에 대한 변수와 콜백 함수가 필요하여 각각 선언 및 구현을 해줄 필요가 있다. 콜백 함수는 언제 발사할지의 여부이다. 우리는 타워의 일정 범위내에 도달하면 타워가 탱..

Fire 탱크는 발사체를 발사해야 하지만, 아직 이와 관련된 함수가 없다. 우리는 프로젝트 세팅에서 액션 매핑으로 Fire가 마우스 좌클릭으로 있었던 것을 알고 있기에, 여기에 바인드할 함수를 구현해주어야 한다. 해당 함수 자체는 터렛과 탱크 모두에게 필요하다. 따라서 베이스폰에서 해당 함수를 구현하게 된다. 탱크에서는 입력을 통해 발사가 이루어지기에 바인드 작업이 필요하다. 축 매핑의 경우 BindAxis 함수를 통해 바인드 작업을 해줬지만, 액션 매핑의 경우 BindAction 함수를 통해 바인드 작업을 하게 된다. 동작 매핑은 축 매핑과 다소 다른데, 우선 동작 매핑은 일회성이기에 모든 프레임에서 실행되지 않는다. 또한 축 매핑의 스케일 값과 같은 인풋 매개 변수가 존재하지 않는다. 버튼이 눌렸는지..

Tower 클래스 현재 포탑이 회전하도록 만들어주었지만, 탱크에만 상속되어있고 타워의 경우 클래스 자체가 존재하지 않아서 회전하지 않는다. 따라서 타워 클래스를 생성해주고, 상속 및 기타 터렛이 필요로 하는 기능을 구현해주어야 한다. 우리는 터렛을 적으로 설정하였다. 따라서 터렛의 일정 범위 내에 탱크가 위치한다면, 탱크를 향해 터렛이 따라오며 탱크를 향해 발사체를 발사해야 한다. 이를 위해 알 수 있는 것은 우리가 탱크의 위치와 탱크의 위치와 터렛사이의 거리, 그리고 범위 값을 설정해주어야 하는 것이다. 우선 탱크 포인터를 얻는 것은 BeginPlay 함수에서, 나머지는 Tick 함수에서 진행하게 된다. 현재 해당 함수들이 존재하지 않으므로, 따로 구현을 해주어야 한다. 탱크에 대해 접근할 포인터도 미..

포탑(Turret) 회전하기 저번 강의를 통해 얻은 커서 방향으로 포탑을 회전시켜줄 차례이다. 이를 위해서는 먼저 포탑을 조준하려는 방향을 얻어야 한다. 이는 포탑에서의 벡터 값과 마우스 커서(히트 위치)의 벡터 값의 차이를 구해 알아낸 방향 벡터를 통해 알 수 있다. 이렇게 구한 방향 벡터를 통해 로테이터 값을 얻을 수 있다. 이때 주의해야할 점이 있는데, 마우스 커서가 플로어를 향할 경우 포탑이 바닥을 가리키게 된다. 우리가 원하는 것은 단순 회전이지 위아래까지 움직이는 것이 아니기에 회전 값을 Yaw(Z 값)만 건드려줄 필요가 있다. 이제 직접 구현해보자. 회전 자체는 터렛과 탱크 포탑 모두 필요하기에 베이스폰에서 구현하게 된다. 앞서 얘기했듯이 목표 지점과 현재 위치의 차이값이 필요하다. 이를 위..

마우스 커서 사용하기 이제 커시 위치에서 히트 결과를 받을 차례이다. 우리가 사용할 함수는 GetHitResultUnderCursor 이다. 첫 번째로 트레이스 채널이 보인다. 이는 전 섹션에서 Grabber를 다루면서도 봤던 내용이다. 그때는 Grabber를 위한 채널을 따로 살펴봤지만, 여기서는 단순히 보이는 물체에 대해 모두 Hit이 되게 하기 위해서 ECC_Visibility를 사용한다. 다음으로 bTraceComplex는 단순 콜리전을 사용할 지, 복합 콜리전을 사용할 지에 대해 묻는 내용이다. 복합 콜리전의 경우 단순 콜리전보다 계산이 훨씬 더 많이 필요하기 때문에 단순 콜리전을 사용한다. 변수명이 Complex이므로, false를 전달하게 된다. 마지막은 전 섹션에서도 많이 다뤘던 HitRe..

캐스팅 현재 탱크가 무사히 움직이고 회전을 하지만, 탱크의 상단부에 해당하는 포탑은 회전하지 않는다. 이러면 포탑은 고정된 상태에서 발사하게 되므로, 포탑을 자유롭게 회전하여 발사체를 발사하도록 해야한다. 이를 위해 마우스 커서를 이용하며, 해당 마우스가 가리키는 쪽으로 포탑이 회전하게 된다. 마우스 커서는 컨트롤러에서 함수를 설정하여 건드리게 되는데, 이는 커서 위치에 대한 히트결과가 컨트롤러에 위치하기 때문이다. 이에 대한 작업은 BeginPlay에서 하게 된다. 하지만 탱크에는 해당 함수가 존재하지 않고, 대신 베이스폰에서 사용하지 않은 채 존재한다. 따라서 사용하지 않는 베이스폰에서 해당 함수를 지우고, 탱크에 복사를 해주었다. 그리고 컨트롤러에 대한 변수를 선언해준다. 우리가 사용하는 컨트롤러는..

로컬 회전 회전에 대해서 다루기에 앞서, AddActorLocalOffset 함수에서 두 번째 인풋 매개 변수인 스윕에 대해서 자세히 알아보고 시작한다. 스윕 기능은 루트 컴포넌트에 대해서만 스윕하며 블록 콜리전을 체크해준다. 루트 컴포넌트에 대해서만 체크하기에 연결되어있는 하위 컴포넌트에 대해서는 체크하지 않는다. 따라서 부딪히는(겹치는) 물체를 체크가 가능하며, 이를 감지하여 특정한 프레임으로 부딪힌 물체를 되돌려 보내 오브젝트를 관통하지 않게 해준다. 움직임이 실제로 구현되기 전에 겹침을 잡아내기 때문에 실제로는 부딪히지 않게 된다. 블록 콜리전 체크를 하기 때문에 콜리전 기능에 대한 활성화가 필요하다. 먼저 코드에서 스윕에 대해 true 값을 넘겨주자. 이후 루트컴포넌트인 캡슐 컴포넌트의 콜리전을..

이동 속도 기존 탱크의 이동 속도가 너무 느리게 느껴져서 이번 강의에서 이를 개선하는 작업을 해준다. 이를 위해 우선 뷰포트에서 FPS 표시해주고 이를 살펴보자. 위와 같이 FPS 표시하면 좌측에 현재 FPS가 나오게 된다. 해당 값을 자세히 보면, 플레이 하는 순간에 확 낮아지고, 수시로 조금씩 변동되는 것을 볼 수 있다. 지금은 다소 단순한 상태이기에 크게 차이는 없지만, 더 복잡해지고 무거워질수록 변동 값이 심해질 수 있다. 이러한 변동이 실제 이동에 영향을 미치기 때문에 전 섹션에서 다뤘던 델타타임을 이용하여 속도를 조절해야한다. 기존 베이스폰에서 Tick 함수를 보면 인자로 델타타임을 가지고 있지만, 탱크가 움직일 Move 함수에는 델타타임이 존재하지 않는다. 따라서 GetWorldDeltaSe..

로컬 오프셋 탱크를 움직이게 하기 위해서는 일종의 오프셋이 필요하다. 우선 오프셋을 월드 스페이스에 설정할지, 로컬 스페이스에 설정할지 정해야한다. 먼저 월드 스페이스를 살펴보자. 현재 월드에서 탱크를 누르면 기즈모가 보인다. 또 해당 기즈모는 좌측 하단에서도 추가로 볼 수 있다. 탱크의 기즈모는 회전해도 움직이지 않으며, 대신 좌측 하단의 기즈모는 같이 회전한다. 이는 월드의 방향은 바뀌지 않기 때문에 발생한다. 여기까지는 월드의 좌표계로 본 것이다. 이제 로컬 좌표계로 살펴보자. 현재 월드 좌표계를 로컬 좌표계로 바꾸는 방법은 뷰포트 상단에 있는 기능으로 전환이 가능하다. 해당 버튼을 누르면 로컬 좌표계로 변환된다. 이제 탱크를 회전하면 탱크의 기즈모도 같이 회전하게 된다. 이를 통해 알 수 있는 것..

인풋 처리하기 프로젝트 세팅에 엔진->입력을 보면 기본적인 상하좌우 이동에 대한 축 매핑과 발사체를 발사하는 액션 매핑이 되어있다. 여기서 축 매핑을 더 자세히 살펴보자. 축 매핑은 매 프레임마다 작동하여 축 매핑 처리를 하게 된다. 함수와 연결을 시켜주며, 함수 Move(float value)와 연결되었다고 가정을 하자. 해당 함수를 축 매핑에 연결시키는 것을 바인딩한다고 한다. WASD 옆에 있는 스케일의 값이 Move의 value 값으로 들어가게 된다. 이제 바인드시킬 함수를 직접 작성해보자. 우선 인풋을 처리해주는 SetupPlayerInputComponent 함수가 필요하다. 이는 BasePawn 파일에 존재하지만, 생각을 해보면 굳이 BasePawn에 있을 필요는 없다. 이동은 탱크에서만 하기..

폰(Pawn)에 빙의하기 언리얼의 기본 설정으로 아무것도 지정되지 않은 상태에서 플레이를 누르면 빈 공간을 자유롭게 돌아다닐 수 있다. 이는 우리가 원하는 것이 아니며, 우리가 만든 탱크에 빙의해야 한다. 이를 위해 해당 탱크를 누르고, 플레이어 자동 빙의 기능을 사용하게 된다. 플레이어가 0~7까지 존재하는데, 플레이어 여러명이 같은 컴퓨터를 사용할 때 의미있는 것이다. 우리는 1명이서 하기 때문에 무조건 Player 0을 사용하게 된다. 이제 실행을 해보면 이제 우리가 세팅했던 카메라 각도로 탱크를 바라보게 된다.