https://velog.io/@doorbals_512/언리얼-엔진-멀티-플레이어-시스템-Part1

 

[Unreal]언리얼 엔진 멀티 플레이어 시스템 Part1

언리얼 엔진 멀티 플레이어 시스템 이해하기 Part1

velog.io

https://velog.io/@doorbals_512/Unreal언리얼-엔진-멀티-플레이어-시스템-Part2

 

[Unreal]언리얼 엔진 멀티 플레이어 시스템 Part2

언리얼 엔진 멀티 플레이어 시스템 이해하기 Part2

velog.io

 

실제 코드나 함수 호출을 알 수는 없지만, 전반적인 구조를 잘 설명해주는 블로그.

특히 도식화가 알아보기 쉽게 잘 정리되어 있어서 개괄 구조만을 확인하기에는 이보다 좋은 예시를 본 적이 없다.

'메모장 > 참고링크' 카테고리의 다른 글

Unreal Engine Network Architecture  (0) 2024.06.12

https://sites.google.com/site/techaht/trans/unreal-net-arch

 

techaht - 언리얼 네트워킹 아키텍처

원문: http://unreal.epicgames.com/Network.htm 저자: 팀 스위니(Tim Sweeny) 기록: 2007년 5월 - 최초 번역 2011년 2월 - 갱신 (사소한 수정) 2011년 4월 - 최종 정리 Tim Sweeney Epic MegaGames, Inc. http://www.epicgames.com/ Audience

sites.google.com

17년 전에 작성되었고 지금은 원본조차 없는 원시 고대 Unreal Engine의 네트워크 아키텍쳐에 대한 내용.

코드 설명도 없고, 문장으로 풀어내고 있으며 현재 구조나 선언과 비교해서 다른 점도 많다.

하지만 작성자가 Tim Sweeny이기도 하고, 한국어로 번역이 잘 되어 있어서 언리얼 엔진의 네트워크.

특히 RPC쪽을 공부하기 앞서 약간의 흥미 유발이나 워밍업 차원으로 읽어보는 것도 나쁘지 않을 것 같다.

무엇보다 정식 문서에서 설명하지 않고 코드로 확인 할 때에도 놓치기 쉬운 사소한 부분이 담겨져 있다.

'메모장 > 참고링크' 카테고리의 다른 글

Unreal Engine Multiplayer System  (0) 2024.06.12

개발하던 언리얼 프로젝트를 Github 사용 시 1년에 8만원씩 써야 해서 git만 사용하다가 너무 불편하여 대안을 찾던 중 "차라리 AWS에 사설 Github 서버 올리면 그게 더 싸지 않을까?" 싶어서 찾아보다가 CodeCommit이라고 아얘 서비스가 나와서 환경을 설정해보았습니다.

일단 졸업하고 AWS를 처음 쓰는거라 환경 설정 하는데 시간이 꽤 걸린 것 같습니다.

윈도우 환경인 점이 한 몫 하기도 했구요.

그리고 AWS 사설 주소로 Push를 보내는게 지금 사용하는 GUI(Git Kraken)에서는 지원되지 않더라구요.

그래서 강제로 CUI 환경으로 복귀하게 되었습니다.

 

그래도 아직 자주 쓰지는 않았지만 장점도 몇 있었습니다.

일단 가격이 매우 쌌습니다. 여러 애셋을 같이 올려보았는데 한 8GB 올렸음에도 가격 정책에서 별로 잡히지가 않습니다.

그리고 개인 프로젝트라 커밋 수가 적은 것을 감안하면 가격 부담은 한달에 5달러는 커녕 1달러도 안넘을 것 같습니다.

 

한가지 아쉬운 점은 개인적으로 게임 프로젝트에서 리소스와 코드가 혼재되어 있어서 VCS 선정할 때 Git이 후순위로 밀리는 것에 대해 대안을 찾고자 Git과 Perforce를 혼용하는 방안을 고민 했는데 안타깝게도 이 방안을 구현하기는 힘들 것 같습니다.

다만 프로젝트가 어느정도 마무리가 되면 Perforce에 더 공부해서 카피 프로젝트를 EC2에 옮겨보고, 성공한다면 Github까지 복사를 한 뒤 다시 Github로 복귀해보고자 합니다.

 

여튼 개인 게임 프로젝트를 하면서 VCS에 고민이 있으신 분들은 하루 정도 투자하셔서 CodeCommit을 사용해보는 것을 추천드려 봅니다.

일반적으로 언리얼 엔진 입문을 하게 되면 Epic Games 플랫폼으로 프로젝트를 생성을 해서 개발을 시작하게 됩니다.

여기서 조금 더 발전하게 되면 VCS를 사용하게 되고, 더 나아가 엔진 코드를 따로 Clone을 해서 코드를 읽어보게 됩니다.

그러다 보면 문뜩 여타 기업들처럼 엔진 코드를 따로 빌드해서 프로젝트를 진행하고 싶은 욕심이 듭니다.

이는 엔진 코드를 수정하고자 하는 욕심에서 비롯되죠.

 

그런데 그 방법이 뭔가 명확하게 기재가 되어있지 않습니다.

특히 VCS로 관리를 하게 된다면 "그래서 이걸 어떻게 엔진에다가 관리를 하지?"라고 고민을 하게 됩니다.

오늘은 이러한 제 고민을 해결한 방식을 공유하고자 합니다.

 

미리 말씀드리자면, 제 나름대로의 방식이니 효율 등이 완벽하게 고려된 사항은 아님을 먼저 말씀드립니다.

https://github.com/RedChiken/UnrealPractice

 

프로젝터에 언리얼 엔진 코드를 넣자니 업데이트 때마다 커밋을 해줘야 하고, 그 양이 매우 방대합니다.

개인 프로젝트에서 레포지토리에 그 많은 코드를 통째로 넣는 것은 매우 비효율적이라 생각했습니다.

그리고 이미 UnrealEngine 레포지토리가 공개가 되어 있으니 이걸 최대한 활용을 하고 싶었습니다.

그러다가 제가 생각해낸 것은 submodule이었습니다.

 

https://git-scm.com/book/ko/v2/Git-%EB%8F%84%EA%B5%AC-%EC%84%9C%EB%B8%8C%EB%AA%A8%EB%93%88

 

Git - 서브모듈

gitmodules 파일에 있는 URL은 조건에 맞는 사람이면 누구든지 Clone 하고 Fetch 할 수 있도록 접근할 수 있어야 한다. 예를 들어 다른 사람이 Pull을 하는 URL과 라이브러리의 작업을 Push 하는 URL이 서로

git-scm.com

submodule을 이용하면 등록된 레포지토리를 통해 Github상에서 엔진 코드와 클라이언트 코드를 깔끔하게 분리할 수 있을거라 생각했습니다.

그리고 기회가 된다면 적극적으로 EngineCode를 수정하고 싶었습니다.

때문에 기존의 EpicGames/UnrealEngine 레포지토리를 Fork하여 RedChiken/UnrealEngine 레포지토리를 생성하였고, 이를 submodule로 등록을 했습니다.

 

이 뒤에는 튜토리얼에서 공개된 것과 비슷합니다.

프로젝트 레포지토리를 Clone을 받은 뒤, UnrealEngine 폴더에 들어가 엔진 코드를 빌드를 하고 실행을 합니다.

그럼 UE5 이후로 생성된 Unreal Engine 클라이언트를 실행한 것처럼 아래 창이 노출됩니다.

여기서 프로직트 위치를 Clone 받은 레포지토리로 지정하고, 그 안에 다시 언리얼 게임 프로젝트를 생성을 하면 됩니다.

 

이후 언리얼 게임 프로젝트를 열면 엔진 프로젝트와 비슷하게 매우 많은 항목들이 공개되어 있습니다.

대신 한가지 차이점이 있다면, 거기에 게임 프로젝트도 포함이 되어 있는거죠.

이 게임 프로젝트를 빌드를 한 뒤 개발을 시작하면 Unreal Engine 코드가 프로젝트에 포함된 게임 프로젝트를 Github에 관리하기 걸맞게 생성을 하게 됩니다.

 

이 과정에서 몇가지 고려를 해야 할 사항이 있습니다.

첫번째로, 엔진 빌드, 게임 프로젝트 초기 빌드 시 엔진 쪽 설정 파일 일부가 변경이 됩니다.

때문에 이들을 따로 커밋하거나, ignore 해야 하는데 위에 언급 했듯이 게임 프로젝트까지 모두 빌드를 한 뒤에 한꺼번에 처리하는 편이 용이합니다.

두번째로, 개발을 하다 보면 UE5는 오브젝트들을 바이너리 파일들로 관리를 하는 방식을 제공하는데 별 다른 설정 변경이 없다면 이 정책이 유지가 될겁니다.

이를 ignore하는 것도 좋지만, 저는 lfs를 사용하는 것을 추천해보고 싶습니다.

 

언리얼 엔진 개발을 하다 보면 기능 개발에 대한 설명은 조금이라도 남아 있지만 프로젝트 구성에 대한 설명은 매우 미흡합니다.

이 글이 다른 사람들에게 도움이 되었으면 합니다.

'메모장 > 개발 지식' 카테고리의 다른 글

AWS CodeCommit 사용 후기  (0) 2022.09.17
툴팁(Tooltip) 만들기  (0) 2022.05.05
C++ Lambda Capture 주의사항  (0) 2021.11.13
Print client side log in listen server  (0) 2020.06.25
FObjectInitializer constructor fatal error c1853  (0) 2020.04.20

UE4에서 제공하는 Tooltip 예시 이미지. 출처: Ryan Laley 유튜브

"언리얼에서 툴팁을 제공하는데 이걸 왜 따로 만들지?"라고 생각하실 수 있습니다.

언리얼 툴팁은 몇가지 특징이 있습니다.

 

1. 툴팁 UI가 적당히 잘 배치됩니다.

"적당히 잘"은 언뜻 들었을 때에는 썩 괜찮은 단어입니다.

하지만 시각적인 부분이 중요한 UI에서 "적당히 잘"은 최소한의 기능 제공만 의미하기도 합니다.

실제로 언리얼이 제공하는 툴팁은 보통 클릭한 커서를 중심으로 위젯이 호출됩니다.

이는 몇가지 누락된 기능이 있는데, 가장 아쉬운 점은 "화면 밖으로 나가는 것을 막지 못한다"는 점입니다.

800*600 화면에서 700위치에 150*50 크기의 툴팁을 생성하면 1/3은 화면 밖에 나가게 되는 것이죠.

 

2. 원하는 위치에 배치할 수 없다.

예를 들어 인벤토리에서 아이템 융합 UI를 생성한다고 합시다.

이 UI는 현재 융합 대기중인 아이템 뿐만 아니라 남은 아이템 목록도 한 눈에 들어와야 하기 때문에 인벤토리와 겹치면 안됩니다.

이럴 때 이 UI를 툴팁으로 만들어 버린다면 원하는 스펙을 맞출 수 없겠죠.

 

위에서 얘기한 것을 바탕으로, 새로 만들 툴팁은 최소 2가지 기능을 갖추어야 합니다.

1) 내가 원하는 위치에 정확히 배치를 해야 한다.

2) 화면 밖으로 나가지 않아야 한다.

 

1. UI의 위치 적용

1. 툴팁 위젯을 대상 위젯과 중심을 일치시키는 작업

언리얼상에서 디스플레이의 원점은 좌상단입니다.(사실 보통 그래픽스는 대체로 그렇습니다.)

그리고 UI의 기본 중심 또한 좌상단으로 잡혀 있죠.

하지만 툴팁은 기준 UI의 무게중심(개념상 일치하니 이렇게 지칭하겠습니다.)을 기준으로 그 외각에 붙어 있어야 하죠.

그렇기 때문에 1차적으로 각 UI의 중심지를 좌상단에서 무게중심으로 이동시키고,

툴팁 UI의 중심과 기준 UI의 중심을 좌표상 같은 곳에 일치하도록 하는 작업을 선행해야 합니다.

 

이는 여러분들이 고등학교(근래는 대학교)에서 배운 기하와 벡터만으로 충분합니다.

기준 UI(이하 A)의 좌상단 좌표가 (a1, a2)이고, 툴팁 UI(이하 B)의 좌상단이 (b1, b2)입니다.

여기서 A의 중심 좌표가 A`(a`1, a`2)이고, B의 것은 B`(b`1, b`2)입니다.

이 때 B와 A의 중심 좌표를 일치시키기 위해서는 디스플레이서부터 A`까지의 벡터와 디스플레이서부터 B`까지의 벡터를 감산 해야 합니다.

그렇게 되면 AB(a`1 - b`1, a`2 - b`2)라는 벡터가 나오게 됩니다.

2. 툴팁을 중심 UI와 겹치지 않게 하는 작업

A와 B가 겹쳐지면 이제 B를 A의 테두리 바깥에 붙도록 위치를 옮겨야 합니다.

이는 1보다는 훨씬 간단한 작업입니다.

B를 옮길 방향에 맞게 A와 B의 해당 크기 값을 연산해주면 됩니다.

여기서는 B를 A의 상단에 붙일겁니다.

이 때 A의 크기는 Ax * Ay이고, B의 크기는 Bx * By입니다.

그럼 B의 좌표(A`1, A`2)에 A와 B의 y크기의 절반만큼을 감산을 합니다.

감산을 하는 이유는 원점이 좌상단이기 때문에 윗 방향은 -Y축이기 때문이구요.

그럼 B의 중심 좌표가 이제 (A`1, A`2 - (Ay + By) / 2)가 됩니다.

 

3. 화면 밖으로 나간 툴팁을 다시 안으로 밀어줘야 한다.

일반적으로는 2번 과정까지만 하면 큰 문제가 없습니다.

하지만 우리는 한가지 더 고려를 해줘야 합니다.

B가 화면 밖으로 삐져나가는 경우에 다시 안쪽으로 밀어줘야 합니다.

 

이 또한 간단한 산수작업입니다.

B의 상단서부터 A의 중심까지의 Y값은 By + Ay / 2입니다.

그리고 실제 A의 중심의 Y좌표값은 A'y입니다.

이들의 차인 By + Ay / 2 - A`y 는 B가 화면 밖으로 나간만큼의 Y 수치가 되는 것이죠.

이만큼 Y좌표 값에 가산을 해주면 B가 화면에 맞게 조절이 됩니다.

 

4. 툴팁 완성

 

자세히 보면 B가 A의 위에 살짝 걸쳐진 것을 볼 수 있습니다.

하지만 어쩔 수 없습니다. 여기서는 B의 크기를 고정해놓기 때문에 A와의 겹침보다 화면 안에 들어가는게 더 우선순위가 높거든요.

물론 UI를 줄일 수 있으나 이런 UI들은 특성상 상당히 많은 정보를 제공하기 때문에 쉬이 크기 조절을 할 수 없을겁니다.

장담컨에 디 방식이 보편적인 타협책이라고 봅니다.

 

이런 과정을 거치기 위해서는 우리는 5개의 값이 필요합니다.

1) 화면 전체 크기

2) 기준 UI의 중심 좌표

3) 기준 UI의 크기

4) 툴팁 UI의 중심 좌표

5) 툴팁 UI의 크기

 

이를 3가지로 줄일 수 있는 것이 있는데, 바로 FGeometry입니다.

https://docs.unrealengine.com/4.26/en-US/API/Runtime/SlateCore/Layout/FGeometry/

 

FGeometry

Represents the position, size, and absolute position of a Widget in Slate.

docs.unrealengine.com

 

FGeomtery는 의미 그대로 Geometry. 크기와 좌표 정보를 가지고 있습니다.

이걸 이용하면 위 5가지 정보를 아래 3가지로 압축할 수 있습니다.

1) 화면 전체 크기

2) 기준 UI의 Geometry

3) 툴팁 UI의 Geometry

 

여기서 한가지 편의사항을 알려드리자면, 게임 개발 하면서 화면 전체를 덮는 UI가 적어도 1개는 발생을 하게 됩니다.

그 UI의 Geometry를 구한다면, 화면 전체의 크기를 구할 수 있게 되죠.

그럼 최종적으로 이렇게 됩니다.

1) 화면 전체를 덮는 UI의 Geometry

2) 기준 UI의 Geometry

3) 툴팁 UI의 Geometry

 

Geometry를 이용한 위젯의 위치 변경은 대략 이렇게 표현이 가능할 것 같습니다.

const FVector2D GetMovementVector(const FGeometry& DisplayGeometry, const FGeometry& IconGeometry, const FGeometry& TooltipGeometry, const EAlignVertical:::Type& VertAlign, cont EAlignHorizental::Type& HorAlign)
{
	const FVector2D& DisplaySize = DisplayGeometry.GetAbsoluteSize();

	const FVector2D& IconCenterPosition = IconGeometry.GetAbsolutePositionAtCoordinates(FVector2D(0.5f, 0.5f));
    const FVeoctor2D& IconCenterVector = DisplayGeometry.AbsoluteToLocal(IconCenterPosition);
    const FVector2D& IconSize = IconGeometry.GetAbsoluteSize();
    
	const FVector2D& TooltipCenterPosition = TooltipGeometry.GetAbsolutePositionAtCoordinates(FVector2D(0.5f, 0.5f));
    const FVeoctor2D& TooltipCenterVector = DisplayGeometry.AbsoluteToLocal(TooltipCenterPosition);
    const FVector2D& TooltipSize = IconGeometry.GetAbsoluteSize();
    
    FVector2D MoveVector = IconCenterVector = TooltipCenterVector;
    const FVector2D& TotalOutlineVector = IconSize + TooltipSize;
    Switch(VertAlign)
    {
    	case EAlignVertical::Type::Left
        	MoveVector.x -= TotalOutlineVector.x;
        	break;
        case EAlignVertical::Type::Right
        	MoveVector.x += TotalOutlineVector.x;
            break;
    }
    MoveVector.x = FMath::Clamp(MoveVector.x, 0, DisplaySize.x);
    
    swietch(HorAlign)
    {
    	case EAlignHorizental::Type::Up
        	MoveVector.y -= TotalOutlineVector.y;
            break;
        case EAlignHorizental::Type::Down
        	MoveVector.y += TotalOutlineVector.y;
            break;            
    }
    MoveVector.y = FMath::Clamp(MoveVector.y, 0, DisplaySize.x);
    
    return MoveVector;
}

 

수식은 위에서 다 설명 하였고, 몇가지 함수 설명을 첨하면 될 것 같습니다.

 

GetAbsolutePositionAtCoordinates는 NormalizedVector를 통해 중심값에서부터 절대 좌표값을 구하는 수식입니다.

즉, UI의 좌상단을 기준으로 무게중심의 절대 좌표를 구하는 함수입니다.

AbsoluteToLocal은 절대 좌표를 해당 Geometry의 Position을 기준으로 지역 좌표로 변환하는 함수입니다.

위 코드에서는 DisplayGeometry의 Position, 즉 화면의 좌상단을 기준으로 하는 상대 좌표로 변환을 하게 됩니다.

이를 통해 TooltipCenterVector와 IconCenterVector는 Display상에서의 값들로 변환이 되게 됩니다.

 

화면 밖으로 나가는 것은 DisplaySize를 구해놓고 Clamp로 변환을 해주었습니다.

Vector2D Clamp가 따로 있었다면 좋았겠지만 아쉽게도 없더군요.

 

자 이렇게 해서 간단한 수식을 작업해 보았습니다.

이걸로 마무리...를 하면 여러분들은 제대로 된 결과를 보지 못하게 될겁니다.

그리고 절 욕하겠죠.

위 코드는 한가지 맹점이 있습니다. 무엇일까요?

 

바로 Geometry 부분입니다.

정확히는 저 Geometry가 런타임 상에서 Invalid한 값이 들어올 수 있다는 점입니다.

보통 Geometry 값을 받아올 때에는 GetCachedGeometry라는 함수를 사용합니다.

https://docs.unrealengine.com/4.27/en-US/BlueprintAPI/Widget/GetCachedGeometry/

 

Get Cached Geometry

Get Cached Geometry

docs.unrealengine.com

그런데 이 GetCachedGeometry는 구조 상 몇가지 제약이 있습니다.

1) 화면상에 노출되어야 한다.

2) 최상위 레이어에 위치해야 한다.

 

이는 GetCachedGeometry가 호출 될 시 그 Tick에서의 Geometry 정보를 담아와서 반환하기 때문입니다.

사실 UI의 크기가 고정적이라면 아무런 문제가 되지 않습니다.

UI 내부에 USizeBox를 두고 모든 항목을 그 아래로 내린 뒤, OverrideSize를 지정해서 사이즈를 고정해버리면 해당 SizeBox의 OverrideSize로 값을 연산할 수 있을테니까요.

하지만 UI가 내부 데이터에 따라 크기가 변동이 된다면 엄청난 오차가 발생을 하게 될겁니다.

그리고 이런 눈꼴시러운 오차를 없애기 위해 노력을 하다 보면 저 GetCachedGeometry에서 막히게 될겁니다.

 

저 제약이 썩 거지같은 점은 "무조건 한번은 화면 레이어에 올라가야 한다"는 점입니다.

이는 디스플레이에 한번은 찍혀야 하는 의미이고, 이는 곧 잔상을 의미합니다.

이제 우리는 3번째 기능을 고민해야 합니다.

3) 화면상에 잔상이 남지 않고 어느 사이즈더라도 동작하는 Geometry 값을 구한다.

 

문제를 해결하기 위해서는 제약을 잘 살펴봐야 합니다.

1)을 다시 볼까요? 

"노출 되어야 한다"라고 되어 있지 "표시되어야 한다"라고 되어 있지 않습니다.

이것이 의미하는 바는, 어떤 형태로든 화면상에 나오기만 하면 Valid한 Geometry를 구할 수 있다는 점입니다.

그것이 "투명"하더라도 말이죠.

 

네 3번째 기능에 대한 해답은 바로 Opacity에서 alpha값을 0으로 내려버리는 것이었습니다.

이렇게 하면 레이어 상으로는 최상위에 툴팁 UI가 올라가 있지만, 투명하기 때문에 유저들이 육안으로 볼 수는 없죠.

이 때 Geometry를 구해서 위치를 옮겨주고, alpha값을 복구하면 잔상 없는 커스텀 툹팁 기능이 완성이 됩니다.

 

UTooltipWidger : public UWidget
{
	virtual void NativeConstruct() override
    {
    	SetOpacity(0.f, 0.f, 0.f, 0.f);
    }

	virtual void Open(bool bCancelAnim) override
    {
    	Super::Open(bCancelAnim);
    	auto World = GetWorld();
        if(IsValid(World) == false)
        {
        	return;
        }
        
        World->GetTimerManager().AddTimer(PositionHandle, this, &ThisClass::UpdatePosition, World->GetTickDeltaTime());
    }
    
    void UpdatePosition()
    {
    	const FGeometry& DisplayGeometry = GetDisplayGeometry();	// 어떻게 구해 왔다고 가정
        const FGeometry& IconGeometry = GetIconGeometry();			// 어떻게 구해 왔다고 가정
        const FGeometry& TooltipGeometry = GetCachedGeoemtry();
        
        SetPosition(GetMovementVector(DisplayGeometry, IconGeometry, TooltipGeometry, EVerticalAlign::Center, EHorizentalAlign::Up));
    	SetOpacity(0.f, 0.f, 0.f, 1.f);
	}
}

 

물론 이 방식은 약간의 딜레이를 요구합니다.

하지만 그 수치가 Tick 함수가 한번 호출 될 정도의 간극이라는 점을 감안하면 손해라고는 있을 수 없는 로직이라 볼 수도 있습니다.

 

저처럼 개발자(혹은 타 업무자)가 좀 더 디테일하게 커스텀이 가능한 툴팁을 필요로 하는 경우가 얼마나 있을지 잘 모르겠습니다.

특히 이런 블로그 글을 보고 들어온건 개인개발이라는 건데 개인 개발에서 이런 정도의 스펙은 잘 구현 안하잖아요?

하지만 어떻게 해야 할지 조금 막막한 기능이었으나 의외로 쉬운 로직이라는 점을 명시하면서.

이 글이 필요로 하시는 분들에게 도움이 되었으면 좋겠습니다.

근래에 동료로부터 들은 내용이라 사실 확인을 하다가 확인한 사항입니다.

처음에는 별 생각 없었는데 좀 더 생각해보니 제가 C++를 공부했을 때
Lambda의 사용 방식이나 Capture의 기능 동작은 설명이 되어 있는데 
내부적으로 어떻게 동작하는지, 혹인 이런 주의 사항은 보지 못했던 것 같더군요.

 

요는 Lambda의 Capture를 사용하면 어떻게 값을 받아오느냐 입니다.

이를 제대로 이해하기 위해서는 Lambda가 Functor와 비교해 어떻게 구성되었느냐를 알아야 합니다.

 

결론부터 얘기하면 Capture가 없는 Lambda나

Call-by-Value로 Capture를 쓰는 Lambda는 일반 Functor와 거의 동일하게 구성됩니다.

 

하지만 Call-by-Reference로 Capture를 쓰면 구조 차이가 발생합니다.

첫번째로, Reference 된 Parameter가 Pointer로 참조가 됩니다.

여기서 이 글의 가장 중요한 전달사항이 나옵니다.

Lambda의 Capture는 Call-by-Reference더라도 Pointer이기 때문에 Pointer Dangling이 발생할 수 있습니다.

이는 Lambda가 비동기적으로 생성되는 경우,

즉 네트워크나 쓰레드와 사용하는 경우에 Invalid Argument를 발생할 수 있습니다.

 

두번째로, Functor는 Constructor가 외부에 별도로 존재를 하는 반면, 

Lambda의 Constructor는 그 내부에 존재를 하고있습니다.
즉, Constructor에서 발생하는 복사 작업이 요구되지 않아 일반 Functor보다 조금 더 가볍습니다. 아주 조금요.

 

이에 대해 자세한 설명은 원문을 본 블로그를 첨부하겠습니다.

어셈블리까지 분석을 해서 비교를 해주고 있기에 직접 포스트를 읽으시면 더 명확히 이해가 갈겁니다.

 

원본 포스트 출처 - https://web.mst.edu/~nmjxv3/articles/lambdas.html

 

C++ Lambdas Under The Hood

C++ Lambdas Under The Hood Introduction C++11 introduced lambdas, which provide a syntactically lightweight way to define functions on-the-fly. They can also capture (or close over) variables from the surrounding scope, either by value or by reference. In

web.mst.edu

Q: Quaternion(쿼터니언)에 대해 설명하시오.

A: 사원수, 혹은 해밀턴 수라고도 부르며 복소수를 확장해 만든 수 체계이다.

덧셈에 대해서는 결합법칙, 교환법칙이 성립하지만, 곱셈에 대해서는 결합법칙만 성립하고 교환법칙은 성립하지 않는다.

쿼터니언은 하나의 실수와 3개의 서로 다른 복소수로 구성이 되며, 곱셈 연산 시 겹선형 연산을 한다.

예를 들어, a0 + a1i + a2j + a3k에 대해 곱의 결과는 다음과 같이 나타난다.

× 1 i j k
1 1 i j k
i i -1 k -j
j j k -1 -i
k k -j -i -1

단, 여기서 a0, a1, a2, a3는 모두 실수이다.

 

쿼터니언은 실수부와 허수부로 나눌 수 있는데,

이를 벡터에 적용하면 실수부를 스칼라 값으로, 허수부를 3차원 벡터로 간주한다.

 

마지막으로 위 예시에서 언급한 쿼터니언을 2×2 Complex matrix(복소행렬)로 나타내면 다음과 같다.

a + bi c + di
-c + di a - bi

 

Q: 물체의 회전을 연산 할 때 쿼터니언을 사용해야 하는 이유를 설명하시오

A: 오일러 각을 이용해 행렬 연산을 할 때 회전하는 축이 정지한 축과 겹쳐지는 상황이 발생하는데, 

이를 짐벌락이라 부른다.

짐벌락은 오일러 각을 연산 할 때 3개의 축을 기반으로 연산을 하기 때문인데,

이를 회피하기 위해 쿼터니언을 이용한다.

쿼터니언을 이용해서 문제가 해결되는 이유는

3개의 축의 연산이 서로 다른 복소수로 되어 있기 때문에 서로가 독립적으로 연산이 될 수 있다.

 

참고자료

ko.wikipedia.org/wiki/%EC%98%A4%EC%9D%BC%EB%9F%AC_%EA%B0%81

 

오일러 각 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 오일러 각(Euler角, Euler angle)은 강체가 놓인 방향을 3차원 공간에 표시하기 위해 레온하르트 오일러가 도입한 세 개의 각도이다.[1] 즉, 3차원 회전군 SO(3)의 한 좌

ko.wikipedia.org

ko.wikipedia.org/wiki/%EC%82%AC%EC%9B%90%EC%88%98

 

사원수 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 브로엄 다리에 새겨진 기념비. 이 곳에서 해밀턴이 사원수를 발견하였다고 한다. 수학에서, 사원수(四元數, 영어: quaternion 쿼터니언[*]) 또는 해밀턴 수(영어: Ham

ko.wikipedia.org

 

Animation은 오직 하나의 애니메이션만 담을 수 있다면, 

Montage는 여러 Animation을 하나의 애셋으로 합칠 수도 있고, Montage 앞에 선행 Animation을 붙일 수 있다.

 

또한 그 안에서 Notify를 이용해 사운드, 파티클, 루트모션 등의 추가 작업을 가할 수 있다.

그리고 이러한 작업들을 Replication 할 수 있다.

 

마지막으로 Animation은 C++이나 Blueprint에서 직접 조작이 불가능하지만, Montage는 직접 조작이 가능하다.

 

참고자료: https://docs.unrealengine.com/ko/Engine/Animation/AnimMontage/Overview/index.html

 

애니메이션 몽타주 개요

애니메이션 몽타주 애셋 작업 방식과 어디에 사용할 수 있는지에 대한 설명입니다.

docs.unrealengine.com

 

UE4에서 GC는 Object들의 Reference Graph를 만들어 참조되지 않은 Object들이 있는지 파악합니다.

Graph의 Root에는 Root Set으로 지정된 Object Set이 있고, 선언되는 모든 Object들은 이 Root Set 밑에 추가됩니다.

GC가 발동되면 Root Set부터 시작하여 Reference Tree를 순회합니다.

이 중 검색되지 않은 Object는 참조자가 없는 것으로 가정하고 제거합니다.

 

그리고 제거되지 않은 Object들은 UPROPERTY레퍼런스를 유지하거나,

포인터를 다른 언리얼 엔진 컨테이너 클래스에 저장합니다.

 

다만 Actor와 Component는 예외인데, 

Actor는 자신이 속한 Level처럼 Root Set로 다시 링크되는 Object에,

Component는 Actor 자체에 Reference가 있기 때문입니다.

Actor는 Destroy로, Component는 DestroyComponent로 삭제하거나 소유 Actor가 삭제될 때 같이 삭제됩니다.

 

그리고 이러한 GC는 프로젝트 세팅을 통해 몇가지 수정을 가할 수 있습니다.

 

참고자료: docs.unrealengine.com/ko/Programming/UnrealArchitecture/Objects/Optimizations/#%EA%B0%80%EB%B9%84%EC%A7%80%EC%BB%AC%EB%A0%89%EC%85%98

 

언리얼 오브젝트 처리

UObject 시스템의 기능에 대한 개요입니다.

docs.unrealengine.com

 

UPROPERTY는 C#의 매크로와 같이 멤버변수 앞에 선언하는 문법입니다.

이는 UE4에서 제공하는 각종 기능들의 대상이 되기 위해서는 반드시 선언되어야 합니다.

 

기본적인 역할은 URS(Unreal Reflection System)에 해당 Property가 있음을 알리는 것으로,

빌드 시 UHT(Unreal Header Tool)이 이 매크로를 감지하고 Reflection Type에 추가합니다.

 

URS에 추가된 UPROPERTY는 GC에 의해 관리가 되고,

Reflection을 통해 변수 이름이나 타입 등을 런타임 상에서 확인할 수 있습니다.

 

추가로 UPROPERTY에 지정자를 추가하여 다른 기능을 적용할 수 있습니다.

Blueprint에서 접근, 수정이 가능한다던가.

카테고리를 지정한다던가.

Replicate를 설정한다던가.

RefNotify를 지정한다던가.

 

참고자료: https://minusi.tistory.com/entry/%EC%96%B8%EB%A6%AC%EC%96%BC-UPROPERTY-Unreal-UPROPERTY

 

언리얼 UPROPERTY( Unreal UPROPERTY )

언리얼 엔진 위에서 언리얼 엔진과 상호작용하려는 코드를 작성하기 위해서는 언리얼에서 제공하는 각종 매크로의 도움을 받을 수 밖에 없습니다. 그 중, UPROPERTY는 멤버 변수( 또는 프로퍼티 ) �

minusi.tistory.com

 

+ Recent posts