🎮Unreal4/C++_TPS

[UE4] 조준점(CrossHair) 퍼뜨리기 (Alignment, WidgetTree, GetAllChildren, UWidgetLayoutLibrary, SlotAsCanvasSlot)

공대 컴린이 2023. 3. 16. 10:24
728x90

총을 연사할 때 총탄이 점점 퍼진다는 의미로 조준점이 사방으로 퍼뜨려지게 된다.

이러한 UI 기능을 구현해보자.

CrossHair Widget 만들기

먼저, 위젯 블루프린트에 조준점(CrossHair)를 만들었다. 흰색 바탕을 기본으로 하는 Image 위젯을 Border 안에 배치한 뒤, Alignment 값을 조절하여 원점으로부터 상하좌우로 위치하도록 만들었다.

 

전체적인 화면으로 보면 화면 중앙에 조준점이 제대로 만들어진 걸 확인할 수 있다.

💡 조준점 퍼뜨리기 원리

조준점을 퍼뜨리기 위해 각 Top, Bottom, Right, Left로 칭해놓은 Border들의 Alignment를 조절하였다.

현재 원점으로부터 각각 0.5 또는 1.5만큼 X, Y좌표가 떨어져있는데, 조준점을 퍼뜨리려면 해당 길이의 절댓값을 더 늘려 원점으로부터 멀리 위치하도록 만들 수 있다.

따라서 현재의 퍼뜨려진 반경과, Max 상태의 퍼뜨려진 반경을 변수로 저장하여 C++ 클래스에서 현재값이 Max까지 점차 늘어나도록 구현하였다.

🔎 C++ 클래스에서 Top,Bottom,Left,Right 위젯 불러오기

UserWidget을 상속받는 C++ 클래스를 생성한 뒤, 미리 만들어둔 CrossHair 위젯의 블루프린트 부모로 설정해주었다.

C++ 클래스에서 위젯의 속성들을 제어하기 위해 위젯 객체들을 불러오는 과정이 필요하다.

 

WidgetTree는 UI 블루프린트에 있던 계층구조를 불러오는데  RootWidget을 불러오면 가장 상위 위젯인 CanvasPanel이 불러와 진다. 따라서 해당 RootWidget을 panel 변수에 저장해놓은 뒤, panel 안에 있는 위젯들에 접근하였다.

GetAllChildren 함수를 사용하여 panel 아래 위치하는 모든 자식들의 정보를 다 가져와 widgets(TArray<UWidget*>)에 저장하였다.

이후, 조준점들의 Border에 위치하는 Alignment 값만 필요하기 때문에 Image 위젯은 가져오지 않고, Border 위젯만 가져왔다.

widgets에 있는 속성들 중 UBorder로 캐스팅되지 않는 것들은 넘어가고, 캐스팅 되는것은 Border만 담아놓은 Borders 변수에 저장해두었다.

 

마지막으로 가장 중요한 변환 과정을 거쳐야 Border의 속성값들을 변경할 수 있다.

WidgetTree는 계층구조의 구성일 뿐, 실제 CanvasPanel의 객체와는 다른 것이다. Widget Tree로 불러온 위젯들은 그저 서로간의 계층구조 관계만 가지고 있을 뿐이다.

따라서 실제로 해당 위젯들의 값을 변환하거나 다루기 위해서는 UWidgetLayoutLibrary 클래스를 사용하여 SlotAsCanvasSlot 함수로 border를 변환해주어야 한다. SlotAsCanvasSlot으로 변환을 해야 Canvas안에 있는 Border 위젯으로써 변환이 된 것이다.

이렇게 변환된 slot에서는 속성값들에 접근할 수 있으므로 Alignment에 접근하여 <FVector2D> 자료형인 TArray 배열에 저장하여 사용했다.

🚀 C++ 클래스에서 조준점 퍼뜨리기

CrossHair 클래스에서 Tick 함수를 실행하며 Top, Bottom, Left, Right의 Border들을 각각 퍼뜨려주었다.

minimum현재 Alignment값을 저장하고, maximum에는 퍼뜨려질 반경만큼을 더한 Alignment값을 저장하여 Lerp 함수로 보간하여 Alignment의 값을 구하였다.

Border의 SetAlignment 함수를 사용하기 위해 다시 Slot으로 변환해주었고, 이후 Top과 Bottom은 X값은 고정한 채, Y값만 변경하고 Left와 Right는 X값만 변경하고 Y값은 고정하여 상하좌우로 퍼질 수 있게 구현하였다.

 

이때 사용되는 Radius와 MaxRadius는 외부에서 값을 전달받아 사용하기 위해 따로 함수로 받아왔다.

🚀 CrossHair 제어 함수 호출

현재 퍼뜨려진 반경을 나타낼 CurrSpreadRadius마지막으로 퍼뜨려진 시간을 나타낼 LastAddSpreadTime, 이외에 SpreadSpeedMaxSpreadAlignment를 각각 선언하였다.

SpreadSpeed와 MaxSpreadAlignment는 총마다 다르기때문에 AR4 클래스에 따로 초기화해주었다.

LastAddSpreadTime일정 시간이 지난 다음 조준점을 다시 원래의 반경으로 돌려놔야 하기 때문에 필요한 변수이다.

 

Weapon 클래스에서 총알이 발사 될 때마다 CrossHair를 점점 퍼뜨려야 하기 때문에 Weapon OnFire 함수에 위 코드를 작성하였다.

CurrSpreadRadius는 현재 퍼진 반경을 비율로 나타낼 것이기 때문에 처음엔 0이고, 1보다 작거나 같다면 최대가 아니라는 경우를 의미한다. (Curr이 1이면 Max와 같은 것)

따라서 최대범위가 아닌 경우에는 SpreadSpeed와 Delta Time을 곱하여 반경을 계속 넓혀주었다.

이후, CrossHair 클래스에 선언해둔 UpdateSpreadRange 함수로 값을 전달하여 보간되어 적용될 수 있도록 하였다.

 

WeaponTick 함수에서 조준점이 퍼뜨려질 것인가에 대해 검사하였다.

LastAddSpreadTime은 총을 쐈을 때 게임의 진행 시간을 나타내기 때문에 0보다 크다면 한발 이상 쏜 적이 있다는 의미이다.

따라서 한발이라도 쐈다면 현재시간(GetWorld()->GetTimeSeconds())에 LastAddSpreadTime을 빼서 총을 쐈던 마지막 시간으로 값을 조정하고, 해당 시간 값이 AutoFire의 Interval 변수보다 큰 경우 조준점을 초기화해주었다.

즉, 총을 연사로 쏘던 시간을 넘길만큼 안쐈다면(한발씩 쐈다면) 조준점이 벌어지지않고 반경이 0으로 고정되게끔 구현한 것이다.

(0.25는 애매한 값을 검사하지 않도록 임의의 값을 조금 더 더해준 것이다)


이렇게되면 총을 계속해서 연사할 때 조준점이 점점 퍼뜨려지다가 Max값에 도달하면 더이상 퍼뜨려지지않고 고정되어있고, 발사가 끝나는 순간 GetWorld()->GetTimeSeconds()-LastAddSpreadTime 값이 연사간격보다 커지게 되어 퍼뜨림 반경이 원상복구된다.

 

728x90