반응형

ue4 플러그인 EditorScriptingUtilities 추가시 이것 을 사용 하려면

 

프로젝트명.Build.cs 에

 

 

PublicDependencyModuleNames.AddRange(new string[] {

"EditorScriptingUtilities" }

 

PrivateDependencyModuleNames.AddRange(new string[] {

"EditorScriptingUtilities" }

 

각각 추가해 주면 된다

 

 

반응형
반응형

절대축 기준..

 

 

GetWorld()->GetFirstPlayerController()->GetControlRotation() : 0~360 사이 각도 리턴

 

GetWorld()->GetFirstPlayerController()->GetControlRotation().Quaternion().Euler()  :  -180 ~ 180 사이의 각도를 리턴한다 

 

 

GetControlRotation 은 컨트롤러의 Rotation 을 리턴한다

 

FRotator APawn::GetControlRotation() const
{
return Controller ? Controller->GetControlRotation() : FRotator::ZeroRotator;
}

 

FRotator AController::GetControlRotation() const
{
ControlRotation.DiagnosticCheckNaN();
return ControlRotation;
}

 

 

회전 값을 행렬로 변환하기 FRotationMatrix()FRotationMatrix(GetControlRotation())

 

 

회전 방향에 대한 절대 축 얻기

 

GetControlRotation() 절대축 기준 회전 값

FRotationMatrix(GetControlRotation()).GetUnitAxis(EAxis::X)

FRotationMatrix(GetControlRotation()).GetUnitAxis(EAxis::Y)

 

vector3 방향으로 회전 행렬 생성 (X 축 기준으로 얼마나 회전이 되었는지 계산

FRotationMatrix::MakeFromX( vector3  ).Rotator()

 

 

 

1차 선형 보간

FMath::FInterpTo

 

회전 보간

FMath::RInterpTo

 

 

충돌 처리

 

도형을 특정 시작 위치에서 목표하는 지점까지 쓸면서(Sweep) 충돌한것이 있다면  반환 하는 함수

 

FHitResult hitResult;
//NAME_None 숫자 0
FCollisionQueryParams params(NAME_None, false, this);

 

bool bResult =

 GetWorld()->SweepSingleByChannel(hitResult, GetActorLocation(), GetActorLocation() + GetActorForwardVector() * 200.0f,
FQuat::Identity, ECollisionChannel::ECC_GameTraceChannel12, FCollisionShape::MakeSphere(50.0f), params);

 

ECollisionChannel::ECC_GameTraceChannel12 : 충돌한 채널

 

FCollisionShape::MakeSphere : 스피어를 만든다

 

 

 

 

FMatrix FRotationMatrix::MakeFromZ(FVector const& ZAxis)

 

ZAxis 이 벡터를 Z 축에 대한 기준으로 삼고 이를 토대로 x,y 축을 뽑아내어 회전 행렬을 만든다

ZAxis 의 방향이 나온 결과에 있어선 z 축이 된다

 

 

 

컴포넌트->SetupAttachment( 붙일 대상, 소켓에 붙일려면 소켓명기입 )

 

액터->AttachToComponent(붙일 대상,  컴포넌트를 붙일 규칙, 소켓에 붙일려면 소켓명기입 )

이 방식으로 하면 무기가 월드 아웃라이너에서 액터로 따로분리 되어 있는것을 볼 수 있다

 

+CreateDefaultSubobject에서 사용하는 문자열의 값은 액터에 속한
  컴포넌트를 구별하기 위한 Hash 값 생성에 사용되고 유일해야한다.

 

//바로 오브젝트를 바로 붙일수 있는건 아니고 Skeletal 이면 이를 관리하는 USkeletalMeshComponent 컴포넌트를 먼저 먼든 다음
//이 컴포넌트에서 관리할 메쉬포인터 쪽으로 무기를 넘겨주고
//실제 이 무기를 사용(캐릭텉에 부착하려면) 무기 컴포넌트를 뼈대에 붙여야 한다
FName weaponSocket(TEXT("hand_rSocket"));
if (GetMesh()->DoesSocketExist(weaponSocket))
{
        _weapon = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("WEAPON"));
        static ConstructorHelpers::FObjectFinder<USkeletalMesh> SK_WEAPON(TEXT("/Game/무기 경로.."));
        if (SK_WEAPON.Succeeded())
        {
            _weapon->SetSkeletalMesh(SK_WEAPON.Object);
        }
        _weapon->SetupAttachment(GetMesh(), weaponSocket);
    }


//액터를 붙일때의 코드 형태 (AABWeapon 클래스 안에서 웨폰을 생성하여 갖고 있는 상태)
FName weaponSocket(TEXT("hand_rSocket"));
auto currentWeapon = GetWorld()->SpawnActor<AABWeapon>(FVector::ZeroVector, FRotator::ZeroRotator);

if (currentWeapon != nullptr)
{
currentWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, weaponSocket);
}

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
void AABCharacter::SetWeapon(class AABWeapon* newWeapon)
{
    ABCHECK(nullptr != newWeapon && nullptr == _currentWeapon);
    FName weaponSocket(TEXT("hand_rSocket"));
    if (nullptr != newWeapon)
    {
        newWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, weaponSocket);
        newWeapon->SetOwner(this); //네트웍 리플리케이션을 위한 setOwner(this);
        _currentWeapon = newWeapon;
    }
}


* Set the owner of this Actor, used primarily for network replication. 
 * @param NewOwner The Actor whom takes over ownership of this Actor
 */
UFUNCTION(BlueprintCallable, Category=Actor)
virtual void SetOwner( AActor* NewOwner );
 
 

 

 

SetVisibility(bool)          : 인게임과 에디터에서 bool 에 따라 보이거나 안보이거나 한다

SetHiddenInGame(bool) :true 일때 에디터 에서 보이지만 인게임에선 안보인다

 

 

SetActorEnableCollision : 액터 전체의 콜리전 을 껐다 켰다 한다

SetCollisionProfileName("") :  콜리전 preset 을 변경 할 수 있다

 

 

 

컴포넌트  OnComponentBeginOverlap 델리게이트 설정 방법

 

//할당
_trigger->OnComponentBeginOverlap.AddDynamic(this&AABItemBox::OnCharacterOverlap);
 
 
 
//헤더 부분에 함수 선언
 
UFUNCTION() 
void OnCharacterOverlap(UPrimitiveComponent* OverlappedComponent, 
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, 
bool bFromSweep, const FHitResult& SweepResult);
 
 
//정의 
void AABItemBox::OnCharacterOverlap(UPrimitiveComponent* OverlappedComponent, 
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, 
bool bFromSweep, const FHitResult& SweepResult) 
///



 

 

 

 

 

 

//게임 인스턴스 구하기

auto abGameInstance = Cast<UABGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));


프로퍼티 

Transient

이 프로퍼티는 휘발성이라, 저장 또는 로드되지 않습니다. 이런 식의 지정자가 붙은 프로퍼티는 로드 시간에 0 으로 채워집니다.

https://docs.unrealengine.com/ko/Programming/UnrealArchitecture/Reference/Properties/index.html


액터 수명 주기

https://docs.unrealengine.com/ko/Programming/UnrealArchitecture/Actors/ActorLifecycle/index.html

 

액터의 수명 주기

액터가 로드 또는 스폰된 후 결국 죽을 때 벌어지는 일에 대해서입니다.

docs.unrealengine.com

 

게임 시작 과정 

 
만약 레벨에 이미 올라와 있는 액터가 있다면 
 
AActor::AActor() // 레벨에 올라가 있던것
 
AActor::PostLoad or AActor::PostActorCreated  // 레벨에 올라가 있던것
이 과정을 거친다음
 
UGameInstance::Init  : 상주하는 게임 인스턴스가 초기화
 
 
AActor::PostInitializeComponents // 레벨에 올라가 있던것
 
이후에는 GameMode 에 의해서 생성된것을 기준으로 보면
 
APlayerController::AABPlayerController         //컨트롤러 먼저 생성하고
 
APlayerController::PostInitializeComponents 
 
AGameMode::PostLogin(26)PostLogin Begin  //게임모드 PostLogin 이 실행하기 전(함수는 실행되나 
                                 Super::PostLogin(NewPlayer); 이 실행되기 이전임, 즉 이 함수 실행이 되면서
 
ACharacter::ACharacter(76)                      //게임 모드에서 생성되는 캐릭터
ACharacter::PostActorCreated(218)           //캐릭터가 생성됨
ACharacter::PostInitializeComponents(196)
 
APlayerController::OnPossess(15)OnPossess begin   //컨트롤러가 Pawn(캐릭터)를 시작 전
(즉 Super::OnPossess(InPawn); 실행전)
 
ACharacter::PossessedBy(223)   //캐릭터가 컨트롤러에 의해서 소유됨
 
APlayerController::OnPossess(18)OnPossess end //캐릭터 소유, Super::OnPossess(InPawn); 실행이후
 
AGameMode::PostLogin(28)PostLogin end //로그인 과정 끝


Actor::BeginPlay() //미리 로드되어 있던것

AABPlayerController::BeginPlay()

ACharacter::BeginPlay()

AGameMode::BeginPlay() //미리 로드되어 있던것



AGameMode::StartPlay(33)StartPlay() //게임 플레이 시작

블루프린트를 만들때 부모를 .cpp 로 만든다음 SkeletalMesh 값을 자식 BP 에서 변경 했을때

 

C++ 쪽에서 부모에서 default 로 기본이 되는 mesh 를 적용했고 이것을 상속 받은

BP 에서 열고 새롭게 할당한 Mesh 가 블루프린트에서 적용되는 C++ 지점은 PostLoad() 부분이다

PostLoad 를 레벨이 이미배치 됐을때 호출 되는것이며

SpawnActor 로 스폰하면 PostLoad가 아닌 PostActorCreated 이 호출 된다(즉 상호 배제적)

 

 

 

 

 

 

델리게이트

 

다이네믹 델리게이트

C++ 만의 델리게이트가 있고 C++ 과 블루프린트간에 연동되는 델리게이트가 있는데 이 연동 되는 델리게이트를

다이네믹 델리게이트라 한다, 다이네믹 델리게이트의 경우 UFUNCTION() 과 사용해야함으로 C++ 람다식을 사용할 수는 없다

 

DECLARE_MULTICAST_DELEGATE 는 람다 가능

 

 

FOnMontageEndedMCDelegate OnMontageEnded;  : 몽타주 애니가 끝날때(섹션 재생도중 중간에 끝나도) 이벤트가 발생

 

DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnMontageEndedMCDelegate, UAnimMontage*, Montage, bool, bInterrupted);

//Animinstance.h 에 있는 내장형

UPROPERTY(BlueprintAssignable) 

FOnMontageEndedMCDelegate OnMontageEnded;

 

 

 

UFUNCTION() //다니네믹 멀티 라서 UFUNCTION 이 들어감

void OnAttackMontageEnded(UAnimMontage* montage, bool bInterrupted);

 

 

//UFUNCTION() c++ 과 블루프린트에서 모두에 호출되는 함수 바인딩시 AddDynamic

_aBAnim->OnMontageEnded.AddDynamic(this&AABCharacter::OnAttackMontageEnded);

 

 

 

 

멀티캐스트 델리게이트  : 등록된 모든 함수에게 알려주는 기능

 

람다를 델리게이트에 바인딩 시

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

DECLARE_MULTICAST_DELEGATE(FOnNextAttackCheckDelegate);

 

 

FOnNextAttackCheckDelegate OnNextAttackCheck;

 

// UFUNCTION 을 연결하는 것이 아님

    _aBAnim->OnNextAttackCheck.AddLambda([this]()->void

    {

        //ABLOG(Warning, TEXT("OnNextAttackCheck"));

        _canNextCombo = false;

        if (_isComboInputOn)

        {

            AttackStartComboState();

            _aBAnim->JumpToAttackMontageSection(_currentCombo);

        }

    }

    );

 

// 연결된것 모두 호출

OnNextAttackCheck.Broadcast(); // 호출

 

 

C++ 블루프른트와 연동 되면서 등록된 함수모두에게 알려주는 기능 : 다이네믹 멀티캐스트 델리게이트

DECLARE_DYNAMIC_MULTICAST_DELEGATE

 

 

 

Montage_IsPlaying () : 몽타주가 재생중인지 확인

Montage_JumpToSection()  : 해당 섹션으로 몽타주 재생 아니 jump 하기

 

Primitive : 기본 도형

 

 

 

 

void AttackCheck();

 

일반 UObject 함수 바인딩시

_aBAnim->OnAttackHitCheck.AddUObject(this, &AABCharacter::AttackCheck);

 

 

이벤트

여러 함수에 바인딩하여 모두 한 번에 실행시킬 수 있는 델리게이트입니다.

이벤트(Event)는 멀티캐스트 델리게이트 와 매우 유사합니다.

그러나 어느 클래스도 이벤트 바인딩이 가능하지만, 이벤트를 선언한 클래스만이 이벤트의 Broadcast, IsBound, Clear 함수를 호출할 수 있습니다.

즉 이와 같이 민감한 함수에 대한 접근권을 외부 클래스에 주면 어떡하지 하는 걱정 없이,

이벤트 오브젝트는 퍼블릭 인터페이스에 노출시킬 수 있다는 뜻입니다. 이벤트가 사용되는 곳은, 순수 추상 클래스의 콜백 포함하기, 외부 클래스의 Broadcast, IsBound, Clear 함수 호출 제한시키기 입니다.

 

DECLARE_EVENT( OwningType, EventName )

이벤트를 생성합니다.

DECLARE_EVENT_OneParam( OwningType, EventName, Param1Type )

파라미터가 하나인 이벤트를 생성합니다.

...

DECLARE_EVENT 매크로의 첫 파라미터는 이 이벤트를 "소유"(own)하게 될 클래스,

 Broadcast() 함수를 호출할 수 있는 클래스입니다.

 

https://docs.unrealengine.com/ko/Programming/UnrealArchitecture/Delegates/Events/index.html

 

 

 

//액터에 UI 위젯을 달수 있는 컴포넌트, 액터가 이것을 달면 UI HP 같은 바를 나타낼 수 있음

//이걸 들고 있으면 됨

UWidgetComponent 

 

이것을 사용하려면 언리얼 엔진에서 UMG 모듈을 추가해주야 한다

기본적으로 이 모듈은 제외되어 있음

 

프로젝트명.Build.cs

 

파일에 보면 모듈들이 있는데 그 중 UMG 가 빠져 있는 것을 볼 수 있다

 

언리얼 엔진은 여러 모듈들이 합쳐진 형태로 구성되고 해당 프로젝트마다 모듈을 조합하여 하나의 프로젝트를 구성하게 된다

 

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore"});

 

"UMG" 부분 추가

 

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG" });

 

UE4 프로젝ㅌ에서 Source> runtime > UMG 폴더에 위치하게된다

 

이렇게 모듈을 추가하면 .h 는 추가해줘야 하지만 전체 경로를 몰라도 Public 폴더에 있는 헤더 파일을 게임 프로젝트(현재 게임을 만드는 프로젝트)에 이있는 헤더 파일처럼 자유롭게 참조 할 수 있다 

헤더는 Source>Runtime>UMG>Public>Components>WidgetComponent.h  에 있는데

 

WidgetComponent.h 이것을 포함하면 된다

#include "Components/WidgetComponent.h"

 

UPROPERTY(VisibleAnywhere, Category=UI)
class UWidgetComponent* _hpBarWidget;

 

_hpBarWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("HPBARWIDGET"));

 

_hpBarWidget->SetupAttachment(GetMesh());

 

 

UI 로직은 위젯 블루프린트는 애님인스턴스와 비슷하게 C++ 클래스에서 미리 만들어 제공할 수 있다

위젯 블루프린트가 사용하는 기반 C++ 클래스는 UserWidget 이다

 

 

 

위젯의 초기화 시점이 4.21 부터 PostInitializeComponents 에서 BeginPlay 로 변경 됨

 

 

UI 초기화 시점은 PlayerController의 BeginPlay 에서 UI 가 생성된다

생성시 NativeConstruct 함수가 호출된다

PlayerController::PostInitializeComponents() 함수에서 실행한 명령은 UI 에 반영 되지 않음으로

UI 시스템이 준비되면 호출 되는 함수 NativeConstruct 함수 에서 실행한다

여기에서 위젯 내용을 업데이트 한다

 

 

위젯에 있는것중에 이름으로 ProgressBar 를 얻어올 수 있음

_hPProgressBar = Cast<UProgressBar>(GetWidgetFromName(TEXT("PB_HPBar")));

 

void UABCharacterWidget::NativeConstruct()
{
Super::NativeConstruct();
_hPProgressBar = Cast<UProgressBar>(GetWidgetFromName(TEXT("PB_HPBar")));
ABCHECK(_hPProgressBar!=nullptr);
UpdateHPWidget();
}

반응형
반응형

 

먼저 TPointerIsConvertibleFromTo 이 부분에 대해 알아보자

 

template <typename From, typename To>

struct TPointerIsConvertibleFromTo

{

private:

    static uint8  Test(...);

    static uint16 Test(To*);

 

public:
    //sizeof(Test((From*)nullptr)  해당 함수에 대한 리턴 타입 크기를 얻는다 즉 2 또는 1이 리턴 됨
    //함수 실행 없이 리턴 타입 크기를 얻어올 수 있다

    enum { Value  = sizeof(Test((From*)nullptr)) - 1 };

};

먼저 이건 From 타입에서 To 라는 타입으로 변환 가능하면 Value 값이 1 이 되며 만약 불가능한

포인터 간의 변환이라면 컴파일 타임에 enum 의 Value 값이 0 이 된다

 

static uint8  Test(...); 은     static uint16 Test(To*); 이 아닌 인자를 받기 위한 함수의 가변 형이다

즉 To 타입이 아닌 타입은 리턴 타입 크기를 1로 처리하겠다는 의미

 

ex)..
int printz(...); // valid, but the arguments cannot be accessed portably
보통 가변인자는 앞에 가변인자 개수를 알려주는 정수형이 오지만 위의 경우에는 단지 리턴 타입 사이즈를 얻기위함과 To 가 아닌 타입을 받기위한 처리로 가변인자로 처리한것

 

 

실행 없이 함수의 리턴 타입 크기를 얻어온다( 그래서 int fun(char* a); 까지만 있어도 됨

It returns the size of the return type from that function (4 on my implementation since that's what an int takes up for me), which you would discover had you run it as is, then changed the return type to char (at which point it would give you 1).

The relevant part of the C99 standard is 6.5.3.4.The sizeof operator:

The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.

Keep in mind that bold bit, it means that the function itself is not called (hence the printf within it is not executed). In other words, the output is simply the size of your int type (followed by a newline, of course).

 

 

 

그다음 볼 구문은 TEnableIf

template <bool Predicate, typename Result = void>
class TEnableIf;

 

기본 적으로 봐야 하는 부분은 Predicate 부분인데 Predicate 로 넘어온 값이
1 즉 참이면 Type 이 정의 되고 그렇지 않다면 즉 0이 넘어온다면 Type은 정의 되지 않는다
Result=void 라는 것을 잊지 않고 이어서 나가보자

 

이 구문은 템플릿의 부분 특수화가 적용 되어 있는 구문이다

 

원래 아래와 같은 이러한 템플릿이 존재 했는데 

 

template <bool Predicate, typename Result = void>
class TEnableIf

{

}

 

 

여기서 Predicate 부분에 대해서만 부분적으로 특수화를 진행한다 

부분 특수화를 진행하려면 나머지 인자를 그대로 써주면 된다 그리하여

bool 타입은 true, 또는 false 임으로 이에 대해 부분 특수화를 진행하면 다음처럼 된다

 

template <typename Result>
class TEnableIf<true, Result>
{
public:

   typedef Result Type;   

   //이때는 Type 을 Result 로 정의 하겠다는 의미인데 별다른 타입 정의가 없다면 Result 는
     위의 기본 디폴 트 값으로 인하여 void 타입이 된다

};

template <typename Result>
class TEnableIf<false, Result>
{  

  //즉 이때는 Type 을 정의하지 않겠다, 존재 Type  존재자체가 없어서 Type 을 쓰면

    컴파일 에러를 발생시키겠다는 의미이다

};

 

 

이때

template <bool Predicate, typename Result = void> 
class TEnableIf

{

이 구문을 간략하게 {} 를 제거 하고

 

template <bool Predicate, typename Result = void> 
class TEnableIf;

 

이렇게 표현 하는 것이 가능하다, ture ,false 모두 다 정의했음으로

 

 

 

아래는 이 두개를 응용한 코드이다

#include <type_traits>

#include <iostream>

 

using namespace std;

 

 

template <bool Predicate, typename Result = void>

class TEnableIf;

 

template <typename Result>

class TEnableIf<true, Result>

{

public:

    typedef Result Type;

};

 

template <typename Result>

class TEnableIf<false, Result>

{ };

 

template<typename T>

class aaa {

public:

 

    //enum { Value = sizeof(unsigned short) - 1 };

    enum { Value = sizeof(unsigned short- 1 };

};

 

template<typename = TEnableIf<aaa<int>::Value>::Type>

void fn()

{

 

}

 

int main()

{

    fn();

    aaa<int> ai;

    auto ddddd= ai.Value;

 

    auto sdfsd=aaa<int>::Value;

 

    TEnableIf<aaa<int>::Value> sdf;

    

    //TEnableIf<aaa<int>::Value>::Type* sdfdd;

    //TEnableIf<true>::Type sdfd;

 

 

    return 0;

}

 

 

 

 

TSubclassOf (1)

다른 타입 변환 간의 안정적인 타입을 확인하고자 하는 템플릿 클래스

이 클래스는 UClass* 를 멤버 변수로 담는 템프릿 클래스이다

 

template<class TClass>
class TSubclassOf
{

....

private:
UClass* Class;

}

 

상세 구문..

template<class TClass>
class TSubclassOf
{
    template <class TClassA>
    friend class TSubclassOf;
public:
    /** Default Constructor, defaults to null */
    FORCEINLINE TSubclassOf() :
        Class(nullptr)
    {
    }
    /** Constructor that takes a UClass and does a runtime check to make sure this is a compatible class */
    FORCEINLINE TSubclassOf(UClass* From) :
        Class(From)
    {
    }
    /** Copy Constructor, will only compile if types are compatible */
    template <class TClassA, class = typename TEnableIf<TPointerIsConvertibleFromTo<TClassA, TClass>::Value>::Type>
    FORCEINLINE TSubclassOf(const TSubclassOf<TClassA>& From) :
        Class(*From)
    {
    }
    /** Assignment operator, will only compile if types are compatible */
    template <class TClassA, class = typename TEnableIf<TPointerIsConvertibleFromTo<TClassA, TClass>::Value>::Type>
    FORCEINLINE TSubclassOf& operator=(const TSubclassOf<TClassA>& From)
    {
        Class = *From;
        return *this;
    }
 
 
...중략...
 
private:
    UClass* Class;
};
 

 

이 중에서도 아래 구문을 보자

 

우선 아래 구문은 TSubclassOf 에 대한 대입 연산자 구문을 기반으로 진행된다

 

TClassA 이것은 자기 자신이 아닌 assign 될 대상 타입이 된다
TClass 가 자기 자신 타입, 즉 넘어온 타입이 자기 자신으로 포인터 변환이 가능한지 여부에 따라 컴파일 에러를 벹게 할것인지 아닌지를 결정한다

 

/** Copy Constructor, will only compile if types are compatible */

    template<class TClassA

    class = typename TEnableIf<TPointerIsConvertibleFromTo<TClassATClass>::Value>::Type>

    FORCEINLINE TSubclassOf& operator=(const TSubclassOf<TClassA>& From)

    {

        Class = *From;

        return *this;

    }

 

TEnableIf 과 TPointerIsConvertibleFromTo 의 조합임을 알 수 있다 

TPointerIsConvertibleFromTo 의 결과 값인 Value 값이 1 이면 => 변환 가능한 포인터라면

TEnableIf 에서 Type 을 typede 하게 되고 Type 은 void 처리 되어 class=void 가 되어

컴파일 에러가 발생하지 않지만 그렇지 않은 경우에는 컴파일 오류를 발생시킨다

class=   이렇게만 됨으로

 

Copy Constructor, 는 형식이 호환 되는 경우에만 컴파일 처리한다

 

 

//컴파일 가능

template<typename = void >
void fn3() {
}

fn3();

 ....

 

 

//컴파일 불가

template<typename =  >
void fn3() {
}

fn3();

 

 

 

TSubclassOf (2)

TSubclassOf 는 UClass 유형의 안전성을 보장해 주는 템플릿 클래스입니다. 예를 들어 디자이너가 대미지 유형을 지정하도록 해주는 프로젝타일 클래스를 제작중이라 가정합시다. 그냥 UPROPERTY 유형의 UClass 를 만든 다음 디자이너가 항상 UDamageType 파생 클래스만 할당하기를 바라거나, TSubclassOf 템플릿을 사용하여 선택지를 제한시킬 수도 있습니다. 그 차이점은 아래 코드와 같습니다:

 

두 번째 선언에서, 템플릿 클래스는 에디터의 프로퍼티 창에 UDamageType 파생 클래스만 선택되도록 합니다. 첫 번째 선언에서는 아무 UClass 나 선택할 수 있습니다. 아래 그림에서 확인됩니다.

 

 

이러한 UPROPERTY 안전성에 추가로, C++ 수준에서의 유형 안전성도 확보할 수 있습니다.

 

비호환 TSubclassOf 유형을 서로에게 할당하려는 순간, 컴파일 오류가 나게 됩니다.

(위에 언급한 이유로)

 

범용 UClass 를 할당하려는 경우, 할당이 가능한지 검증하는 실행시간 검사를 합니다.

실행시간 검사가 실패하면, 결과값은 nullptr 입니다.

 

UClass* ClassA = UDamageType::StaticClass();
 
TSubclassOf<UDamageType> ClassB;
 
ClassB = ClassA; // Performs a runtime check //런타임때 체크
 
TSubclassOf<UDamageType_Lava> ClassC;

//컴파일 타임때 체크
ClassB = ClassC; // Performs a compile time check

 

ref :  

https://stackoverflow.com/questions/12659486/what-does-sizeof-functionargument-return

https://docs.unrealengine.com/en-US/API/Runtime/Core/Templates/TPointerIsConvertibleFromTo/index.html

 

https://docs.unrealengine.com/ko/Programming/UnrealArchitecture/TSubclassOf/index.html

https://en.cppreference.com/w/cpp/language/variadic_arguments

반응형
반응형

 

 

투본 IK 는 3개의 조인트를 갖고 IK 를 구하는 방식이다
(언리얼에서의 정의 : 투 본 IK 를 사용해서 3 조인트 체인에 IK 를 적용하는 법입니다.
Two Bone IK (투 본 IK) 컨트롤은 캐릭터의 사지와 같은 3 조인트 체인에 Inverse Kinematics(역운동학, IK)를 적용합니다.)

 

투본 IK 의 경우 본 3개로 IK 를 구현 하는데 중간 조인트가 어느 방향쪽으로 걲일지에 대한 기준있게 되고

이 기준 위치를 따라 중간 본이 어떤 방향으로 꺽일지가 정해지게 된다

(무기/총 잡을 때 등에 쓰일 수 있다)

 

 

 

 

 

IK Bone : 컨트롤할 본 이름, IK 에 의해 컨트롤 되는 메인 본 체인 : 오른 손을 선택

 

Joint Target : 오른쪽 어깨를 선택 해 놓고

 

2본 IK(Two Bone IK) 노드엣 Joint Target Location 의 위치를 바꾸면 그 위치 기준으로 하여

꺽을 방향이 역으로 생기게 된다

 

아래 이미지는 Joint Target Location 을 캐릭터가 바라보는 앞쪽으로 뺏을때 팔이 역으로 꺽이는 형태이고 

 

 

 

Joint Target Location 를 음수로 하면 즉 캐릭터의 뒤쪽에 배치를 시키면

 

 

바르게 팔이 꺽이는 것을 볼 수 있다

 

 

이 상태에서 

 

Effector Location 위치를 바꾸면  right hand 가 대상을 따라간다

 

 

 

Analytic Two-Bone IK in 2D

December 29, 2008

Due to their complexity, inverse kinematics (IK) problems are often solved with iterative solutions. While iterative solutions can handle different IK poblems with a single algorithm, they can also become computationally expensive. If a specific IK problem needs to be computed often, it is worth considering an analytic solution.

 

If we limit the problem to a two bone chain in a two-dimensions, we can derive the anaytic solution without much complexity. This will generally be more efficient than its iterative alternatives. While this specific case lends itself to a 2D world, it can also be used in 3D as long as the kinematic chain's motion is limited to a single plane.

 

The goal of is to create a robust function with the following requirements.

  1. Let the user specify which direction to bend the bone chain (there can be up to two valid solutions, each bending in an opposite direction).
  2. Notify the user when there was no valid solution based on the input.
  3. When there is no valid solution, calcualte the closest alternative.
  4. Produce the expected results for every target position. Most implementations found online or in books only function properly when the target is in a certain quadrant or when it remains in a specific range.

 

If you aren't interested in the exciting math ahead, jump to the end of the article for sample code. You can also download the RJ_Demo_IK application and source code to see a running implementation. If you're the type that likes to know how things work (like me), we will walk through the derivation in detail. It might look extensive at first, but it's only because I've broken it down step by step. Hopefully you'll be able to follow along without much difficulty.

 

You might also be interested in the following articles on different IK solvers:

 

원점에 조인트 하나와 중간에 꺽인(θ2 앞에) 조인트 그리고 대상으로할 Target 지점 (x,y) 이렇게 3개가 주어지고

거리 d1, d2 가 주어진다

그리고 원점에서부터 target 까지의 거리 h 로 놓을 수 있는데

 

Two bone ik 는 figure1 의 도형을 보면 θ2, θ1 을 구하는 것으로 좁혀진다

아래의 유도 과정은 삼각함수들을 통해 구하는 과정으로 먼저 θ2 를 구한 다음 이를 토대로 θ1을 구해 나가는 방식이고

d1 과 d2 거리의 차의 거리까지가 target 으로 접근하는 최소 거리 d1+d2 의 거리가 target 으로 향할 수 있는 최대 거리이며 target 으로 향할수 있을때 target 점 까지의 각도를 두개의 각도  θ2, θ1 를 구할 수 있지만 그 외의서는

벌을 최대한 뻗어 Target 으로 향하는 방향을 가리키게 된다는 유도 과정이다

Defining the problem

Our problem domain consists of a two bone chain where bone1 is located at the origin and bone2 is a child of bone1. Each bone conistis of an angle θ and a distance dd. The goal is to get the end of bone2 to line up with the target point (x,y). Because we have set bone1 at the origin, you will need to transform your destination point relative to bone1. Figure 1 shows how our variables fit together. The distance from the orgin to the target point is defined as hh.

figure 1

 

The solvable domain

 

figure 2

 

Math review

Before we hop into the derivation, I want to review a few trigonometric identities that we will be using. For more information on the identities, Wikipedia has an extensive list and also has a page on the derivations.

 

 

Solving for θ2

figure 3

 

 

 

We now have our analytic equation for θ2, but there are a few nuances to discuss in more detail.

  1. theta2 기준으로 봤을 때 d1 과 d2 가 회전 되어서 구부러지는 것을 생각해보면 되는데 theta2가 -1이면 d1 과 d2 가 펴지고, theta2 가 1 이면 d1 과 d2 가 최대로 구부러지게 된다
    If cosθ2 is not within the range [-1,1], it is outside of the arccosθ domain. This will happen when our target point is a location the bones cannot reach. A value of -1 will give us an angle of 180° (i.e. bone2 is fully bent) and a value of 1 will give us an angle of 0° (i.e. bone2 is fully extended). When the value is less than -1, our bone is trying to bend father than is physically possible to reach a point that is too close to the origin. When the value is greater than 1, our bone is trying to extend farther than is physically possible to reach a point that is too far from the origin. This means that we can compute the best non-valid solution by clamping the value into the legal [-1,1] range.

  2. If d1 or d2 is zero, we will divide by zero when computing cos⁡θ2. When one or both bones have a zero length, we can set θ2 to any value we desire (I suggest zero or the value used on a previous frame). When we solve for θ1 it will correct itself according to our selected θ2. The solvable domain for this case has also been reduced to a single circle centered at the origin with a radius of d1+d2.
  3. When we can evaluate arccosθ, our result is limited to the range [0,π]. If we use the result as is, we will always find the solution where our second bone turns in the positive direction. If it is our intention to find the solution that bends in the negative direction, we need to negate the new θ2.

(theta2 기준으로 보면 

 

Solving for θ1

figure 4

 

 

여기서 tan 공식을 보면 

 

이런 형태가 있는데 이것을 이용하여 다음처럼 전개한다

Writing the code

We can now write our algorithm in code. The function will calculate a valid IK solution if possible. If there is no valid solution, it will calculate one that gets the end of bone2 as close to the target as possible. Check out the RJ_Demo_IK application for a functional implementation of this code.

 

These code samples are released under the following license.

 

/******************************************************************************

  Copyright (c) 2008-2009 Ryan Juckett

  http://www.ryanjuckett.com/

  

  This software is provided 'as-is', without any express or implied

  warranty. In no event will the authors be held liable for any damages

  arising from the use of this software.

  

  Permission is granted to anyone to use this software for any purpose,

  including commercial applications, and to alter it and redistribute it

  freely, subject to the following restrictions:

  

  1. The origin of this software must not be misrepresented; you must not

     claim that you wrote the original software. If you use this software

     in a product, an acknowledgment in the product documentation would be

     appreciated but is not required.

  

  2. Altered source versions must be plainly marked as such, and must not be

     misrepresented as being the original software.

  

  3. This notice may not be removed or altered from any source

     distribution.

******************************************************************************/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
///***************************************************************************************
/// CalcIK_2D_TwoBoneAnalytic
/// Given a two bone chain located at the origin (bone1 is the parent of bone2), this
/// function will compute the bone angles needed for the end of the chain to line up
/// with a target position. If there is no valid solution, the angles will be set to
/// get as close to the target as possible.
///  
/// returns: True when a valid solution was found.
///***************************************************************************************
public static bool CalcIK_2D_TwoBoneAnalytic
(
    out double angle1,   // Angle of bone 1
    out double angle2,   // Angle of bone 2
    bool solvePosAngle2, // Solve for positive angle 2 instead of negative angle 2
    double length1,      // Length of bone 1. Assumed to be >= zero
    double length2,      // Length of bone 2. Assumed to be >= zero
    double targetX,      // Target x position for the bones to reach
    double targetY       // Target y position for the bones to reach
)
{
    Debug.Assert(length1 >= 0);
    Debug.Assert(length2 >= 0);
  
    const double epsilon = 0.0001// used to prevent division by small numbers
  
    bool foundValidSolution = true;
  
    double targetDistSqr = (targetX*targetX + targetY*targetY);
  
    //===
    // Compute a new value for angle2 along with its cosine
    double sinAngle2;
    double cosAngle2;
  
    double cosAngle2_denom = 2*length1*length2;
    if( cosAngle2_denom > epsilon )
    {
        cosAngle2 =   (targetDistSqr - length1*length1 - length2*length2)
                    / (cosAngle2_denom);
  
        // if our result is not in the legal cosine range, we can not find a
        // legal solution for the target
        if( (cosAngle2 < -1.0|| (cosAngle2 > 1.0) )
            foundValidSolution = false;
  
        // clamp our value into range so we can calculate the best
        // solution when there are no valid ones
        cosAngle2 = Math.Max(-1, Math.Min( 1, cosAngle2 ) );
  
        // compute a new value for angle2
        angle2 = Math.Acos( cosAngle2 );
  
        // adjust for the desired bend direction
        if!solvePosAngle2 )
            angle2 = -angle2;
  
        // compute the sine of our angle
        sinAngle2 = Math.Sin( angle2 );
    }
    else
    {
        // At least one of the bones had a zero length. This means our
        // solvable domain is a circle around the origin with a radius
        // equal to the sum of our bone lengths.
        double totalLenSqr = (length1 + length2) * (length1 + length2);
        if(    targetDistSqr < (totalLenSqr-epsilon)
            || targetDistSqr > (totalLenSqr+epsilon) )
        {
            foundValidSolution = false;
        }
  
        // Only the value of angle1 matters at this point. We can just
        // set angle2 to zero. 
        angle2    = 0.0;
        cosAngle2 = 1.0;
        sinAngle2 = 0.0;
    }
  
    //===
    // Compute the value of angle1 based on the sine and cosine of angle2
    double triAdjacent = length1 + length2*cosAngle2;
    double triOpposite = length2*sinAngle2;
  
    double tanY = targetY*triAdjacent - targetX*triOpposite;
    double tanX = targetX*triAdjacent + targetY*triOpposite;
  
    // Note that it is safe to call Atan2(0,0) which will happen if targetX and
    // targetY are zero
    angle1 = Math.Atan2( tanY, tanX );
  
    return foundValidSolution;
}
 
 

 

 

 

ref : http://www.ryanjuckett.com/programming/analytic-two-bone-ik-in-2d/

ref : https://docs.unrealengine.com/ko/Engine/Animation/NodeReference/SkeletalControls/TwoBoneIK/index.html

반응형
반응형

 

chain 의 Root Node(=base)

각 chain 들의 노드(=link) 라 네이밍을 가정 하고 시작한다

 

root node 의 반대편 즉 chain 의 끝 노드(Chain End)에서부터 root node 까지 노드(Link)를 방문 하면서

Chain End 노드가 target 에 접근 하도록 각 노드(link)의 각도를 taret 방향으로 회전 시켜주는 방식이다

 

나중엔 Chain End 가 Target 과 같거나 가장 근접한 결과를 뽑아내는 알고리즘이다

FABRIK 와 달리 알고리즘의 해가 정확하지 존재하지 않을 수 있다

 

즉 CCD 는 가장 근사한 해를 찾는 알고리즘임으로 해를 찾지 못하는 경우를 대비하여

loop count 를 두고 그 횟수를 제한해야 하는 부분이 필요하다

 

 

 

CCD 영상

 

 

 

언리얼 에서의 CCD 영상

 

CCDIK (Cyclic Coordinate Descent Inverse Kinematics, 순환 좌표 하강 역운동학) 스켈레탈 컨트롤 노드는 경량 IK 솔버로 (FABRIK 과 비슷하며) 보통 본 체인 구동에 쓰입니다. 하지만 FABRIK 와 달리, CCDIK 는 솔브 도중 본의 회전을 제한하려는 경우 유용하게 쓰일 수 있는 각 컨스트레인트를 정의하는 기능을 제공합니다.

CCDIK 노드는 아래 그림처럼 절차적 애니메이션 구동에 쓰일 수 있습니다.

 

 

 

 

 

위 비디오에서 CCDIK 노드 프로퍼티의 Solver (솔버) 섹션을 사용하여 캐릭터의 왼쪽 어깨에 Root Bone (루트 본)을 설정했습니다. 그런 다음 Tip Bone (끝 본)을 캐릭터 검지 마지막 숫자로 설정합니다. Effector Location (이펙터 위치)를 조정할 때, IK 솔버가 인계하면, 체인의 본 각각이 따르고, 정의한 Rotation Limit Per Joints (조인트 당 회전 제한) 값을 기반으로 합니다. 이 각 제한은 본이 이상하게 회전하는 것을 방지하며 원하는 결과를 내기 위해서는 (본 단위로) 값을 조정해야 할 수 있습니다.

 

=> CCD는 조인트당 회전 되는 각도의 제한을 두어서 팔이 의도하지 않은 관절 형태로 회전 되는 것을 방지  할 수 있다

 

 

Tip bone : 본의 끝 노드로 오른손 검지 끝마디로 설정하고

Root bone 은 오른쪽 어깨 본으로 세팅 해놓으면 Root bone 부터 tip bone 까지에 걸처 본들에 대한 제한 각도들을 설정 할 수 있다(각 본에 대해 회전 제한 각도를 설정하기 위한 본들은 알아서 잡힌다, 본의 개수에 맞게끔) 가장 아래 쪽 7번 인덱스가 검지 끝 본을 말하며 6번은 검지 끝 본 이전 것노드를 말하며 각 숫자는 회저회전 할 수 있는 각도를 설정할 수 있다, 이 각도로 IK 도 중 본이 이상하게 꺽이는 현상을 방지할 수 있다

위 그림에서 보면 오른손 손가락이 타겟 즉 Effector Location 을 향하고 있는 것을 볼 수 있다, 하지만 각도를 0~2에 해당 하는 부분에 대해 0 으로 설정해 놓았기 때문에 회전 추적 되지 않고 어깨부터 3개 의 본들은 그자리에 있는 것을 볼 수 있다

 

 

나머지 3개에 대해서도 30도 정도의 회전각 반경을 주면 아래 그림 처럼 오른쪽 어깨부터 오른손 검지 손가락이 

타겟지점으로 향하는 것을 볼 수 있다

 

 

 

 

 

Using an iterative technique to determine orientations

for a multiple link IK chain

The way we will study here was first presented by Chris Welman in his

masters thesis on IK as an extension to work developed by Li-Chun

Tommy Wang and Chih Cheng Chen in an IEEE paper 'Transactions on

Robotics and Automation'. The technique is called Cyclic Coordinate

Descent. The principal algorithm is as follows:

 

1 Set a loop counter to zero.

2 Start with the last link in the chain.

3 Rotate this link to point towards the target.

4 Move down the chain and repeat step 2.

5 When the base object is reached, determine if the target has been

reached or a loop limit has been reached. If so exit, if not increment

loop counter and repeat from step 2.

This algorithm has the benefit of simplicity and can be easily

implemented.

To derive the angle

, we go back to the familiar methods of using

vectors. Remember that the dot product of vectors is given by

 

Unfortunately, the angle does not imply direction and we are interested in

both the angle and the direction. Since the cross product of two vectors

gives a perpendicular vector, we can use this to determine the direction.

By extending the vectors from two to three dimensions, we then have a z

value. The sign of this z value will give the direction in which to rotate.

 

 

defines the direction of rotation.

When using the algorithm, we start by iterating through the link chain to

find the last link. We then create unit vectors from the pivot point of the link

to the chain end and from the pivot point to the target. The inverse cosine

of the dot product of these two vectors gives the angle of rotation and the

third term of the result of the three-value vector cross product gives the

direction of rotation. We generate a rotation matrix using this information

and record the current chain end position.

 

 

We then move one link down the chain and determine new vectors from

the pivot point of the new link to the chain end and target and repeat the

dot product and cross product calculations. We then create a rotation

matrix and calculate the new location for the chain end. This procedure is

repeated until the base object is rotated. At this point we determine if we

are close enough to the target to exit the function. If not, we must repeat

the procedure. It is possible that the target cannot be reached. The

function needs to ensure that under such circumstances the program

does not enter an infinite loop. A simple technique to avoid this is to limit

the looping to a certain number of times; if this is exceeded then the

function exits.

If the total length of the links is less than the distance to the target, then

the goal can never be achieved and it can be useful to add this condition

to the code. Under these circumstances we return to the problem of

orientating a single link, the base link, and setting all other links to zero

rotation.

The code for the Cyclic Coordinate Descent method is as follows:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
void CIKDemoDoc::SolveMultiLink() 
{
    //links.next 시작 부분 base 에 해당 하는 노드
    CLink* link = links.next, * lastlink, * tmplink; 
    int loopcount = 0
    double x, y, totallength, linkslength = 0.0, dotprod;
 
    double mag, theta, sign, sqdist; VECTOR pe, pt;
    
    //Check solution is possible
    //시작 부분 base link 와 target 까지의 거리를 구한다
    x = target.x - link->pos.x;
    y = -target.y - link->pos.y;
    totallength = sqrt(x*+ y*y);
    
    //Find the last link and links length
    
    //link(chain)들의 총 길이를 구한다
    while (link->next)
    {
        linkslength += link->length;
        lastlink = link;
        link = link->next;
    }
 
    //lastlink : chain End 마지막 노드
 
    //체인의 총 길이가 타겟에 닿을 수 있는 범위에 있는지 판단 
    //즉 체인이 타겟을 추적할 수 있는 범위안에 있는지 판단
    if (totallength < linkslength)
    {
        //Calculate Cyclic Coordinate Descent solution
        //link 마지막 노드
        link = lastlink;
 
        //최대 20 번까지 접근을 한다 : 완전한 해를 구하지 못하는 경우는 도중에 끊어야 한다
        while(link && loopcount<20)
        {
            //end effector 따라가는 target에 해당, right 는 chain end 로 가는 단위 벡터인듯
            //Calculate current endeffector position
            //endeffector = lastlink->length * lastlink->right + lastlink->pos;
            //endeffector 현재 chain 의 끝 지점을 endeffector 로 칭하는 듯
            endeffector.x = lastlink->length * lastlink->right.x +lastlink->pos.x;
            endeffector.y = lastlink->length * lastlink->right.y +lastlink->pos.y;
            
            //chain end 와 target 사이의 거리를 구한다
            //Calculate the squared distance from end effector to//target
            x = target.x - endeffector.x;
            y = target.y - endeffector.y;
            
            //chain end 와 target 사이의 거리가 0.1 보다 작다면 추적을 중지한다
            sqdist = x*+ y*y;
            if (sqdist < 0.1
                break;
            
            //link : pivot 에서 target 까지의 거리를 구한다
            //Calculate pivot to target vector
            pt.x = target.x - link->pos.x;
            pt.y = target.y - link->pos.y;
            pt.z = 0;
            
            //Calculate pivot to end effector vector
            
            //pivot 위치에서 endeffector( 첫번째 실행할때는 endeffector 가 chain End) 
//까지의 거리를 구한다
            pe.x = endeffector.x - link->pos.x;
            pe.y = endeffector.y - link->pos.y;
            pe.z = 0;
 
            //Convert to unit vectors
            mag = sqrt(pt.x * pt.x + pt.y * pt.y);
            pt.x/=mag; 
            pt.y/=mag;
 
            mag = sqrt(pe.x * pe.x + pe.y * pe.y);
            pe.x/=mag; 
            pe.y/=mag;
            
            //Calculate dot product
            //endeffector 와 Target 의 내적을 구한다 => 1 * 1 * cos(theta)
            dotprod = pe.x * pt.x + pe.y * pt.y;
            
            //Calculate cross product for direction
            //외적 z 성분을 통해 회전 부호를 결정할 수 있다 
            sign = pe.x * pt.y - pe.y * pt.x;
            
            //각도를 구한다
            if (sign > 0.0)
            {
                theta = -acos(dotprod);
            }
            else
            {
                theta = acos(dotprod);
            }
            
            //각도를 구한다음 pivot 위치에서 end effector  가 target 로 까지 회전 
//되어야 하는 각도를 더해준다
            link->rot.z += theta;
            
            //Set matrices for current link and all children
            
            tmplink = link;
            while(link)
            {
                link->CreateMatrix();
                link = link->next;
            }
            
            link = tmplink;
            
            //Move on to next link
            //link 가 첫번째 link 와 같다면 loopcount 를 증가 시키고 
            //(chain end) 끝 노드 부터 다시 반복을 시도하여 해를 구하기 위한 시도를 한다
            if (link==links.next)
            {
                loopcount++;
                link = lastlink;        
            }
            else
            {
                //나음 link 로 이동한다
                link = link->parent;
            }
        }
    }
    else
    {
        //chain 범위 밖에 있는 경우 첫번째  link 에는 target 으로 향하는 각도를 넣어주고
        //나머지 자식에해당하는 노드들은 0 도로 채우준다(체인이 닿을 수 있는 범위 
//밖임으로 뻗는 형태가 된다)
        //Just use the single link solution
        link = links.next;
        x = target.x - link->pos.x;
        y = -target.y - link->pos.x;
 
        //첫번째 base  link 에서 target 까지의 거리를 구한다음
 
        //taret으로 향하는 절대 각을 구한다
        link->rot.z = atan(y/x);
 
        if (x<0
            link->rot.z = PI + link->rot.z;
 
        //나머지 
        while (link->next)
        { 
            link = link->next; 
            link->rot.z = 0.0
        }
    }
 
    link = links.next; 
 
    while (link) 
    {
        link->CreateMatrix(); 
        link = link->next; 
    }
}
 
 

 

 

 

 ref :

http://nedrilad.com/Tutorial/topic-73/Real-time-3D-Character-Animation-with-Visual-C-197.html

http://nedrilad.com/Tutorial/topic-73/Real-time-3D-Character-Animation-with-Visual-C-198.html

https://docs.unrealengine.com/ko/Engine/Animation/NodeReference/SkeletalControls/CCDIK/index.html

 

 

반응형
반응형

 

FABRIK Algorithm : IK

 

 

 

 

 

1. target 지점으로  reach 를  하면( Forward) 기준이 되는 끝 노드 base 노드가 원래 있던곳에서 떨어지게 된다

2. 이때문에 다시 역으로 target 으로 reach 했던 것을 다시 원래 최초의 끝 노드 지점을 target 으로 잡고
    reach 를 하는 (backward) 방식을 FABRIK ( Forward-and-Backward Reaching Inverse Kinematics ) 이라 한다

 

여기서 reach 는 각 노드들마다 target 을 기준으로 삼고 원래 해당 노드에서 target 기준으로 tail 노드를 끌어와서 target 지점을 목표로 각 노드들이 따라가는 것을 말한다

 

Tip bone : 끝 본에 해당하며  이것을 검지 손가락으로 설정하고

Root bone  은 어깨로 설정하여

타겟 : Effector Trasnform 위 이미지에서는 현재 붉은색 동그라미 부분의 좌표축의 위치

 

Effector 의 위치를 변경하면 어깨부터 검지까지 Effector 를 따라 가는것을 볼 수 있지만 관절마다의 각도 제한이 없기 때문에 뒷 부분으로 갈때 팔이 기이하게 꺽이는 현상을 볼 수 있다

 

 

Effector 위치를 FABRIIK 가 따라 갈수 있는 범위내 (즉 어깨부터 검지 손가락의 총 길이 내의 위치내)

에 있고 뼈가 꼬이지 않는 범위 부근에 위치한다면 다음 그림 처럼 정상적으로 보일순 있다

즉 FABRIK 은 인체 관절 보다는 자유스러운 형태에서의 IK 에 더 적합함을 알 수 있다

 

FABRIK 

FABRIK stands for Forward-and-Backward Reaching Inverse Kinematics, and it’s a very simple, fast, and clever solution to the inverse kinematics problem.

Let’s inspect this interesting algorithm in 2D.

Problem

First of all, what is Inverse Kinematics (IK)?

Suppose you have a robotic arm with three joints, whose job is to point to a location:

 

 

 

How do you calculate the correct angle of each joint in order to hit the target?

This seems stupid at first – we solve this problem all day long, inside our bodies, as we reach for objects and navigate the world. But there is surprising complexity in this problem.

In order to appreciate the FABRIK solution, watch the following video as different algorithms attempt to solve IK in 3D:

FABRIK Demonstration Video

Iterative Reaching

FABRIK’s solution to this problem is to iteratively reach for a target location.

Let’s start with the most basic setup.

Suppose there aren’t any joints, and you simply want a line of a fixed length to follow your mouse cursor:

 

 

Reaching, according to FABRIK, consists of:

  1. Given a line and a target…
  2. Set the line’s head point to the target, stretching the line
  3. Slide the line’s tail point along the new stretched line to fix the length

Let’s look at this visually.

 

 

 

Let’s look at some code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function reach(head, tail, tgt){
  // returns new head and tail in the format of:
  //   [new_head, new_tail]
  // where `new_head` has been moved to `tgt`
 
  // calculate the current length
  // (in practice, this should be calculated once and saved,
  //  not re-calculated every time `reach` is called)
  var c_dx = tail.x - head.x;
  var c_dy = tail.y - head.y;
  var c_dist = Math.sqrt(c_dx * c_dx + c_dy * c_dy);
 
  // calculate the stretched length
  var s_dx = tail.x - tgt.x;
  var s_dy = tail.y - tgt.y;
  var s_dist = Math.sqrt(s_dx * s_dx + s_dy * s_dy);
 
  // calculate how much to scale the stretched line
  var scale = c_dist / s_dist;
 
  // return the result
  return [
    // copy the target for the new head
    { x: tgt.x, y: tgt.y },
 
    // scale the new tail based on distance from target
    { x: tgt.x + s_dx * scale, y: tgt.y + s_dy * scale }
  ];
}
 
cs

 

This should be fairly straight-forward, but there is one important property to notice:

This function always succeeds. The line will always be moved so that it touches the target.

Making it Iterative

How can we make the process iterative? I.e., instead of pulling around a single line, how can we pull along a chain of lines?

 

It’s quite ingenious:

The act of sliding the tail along the line can itself be a reach.

Think about it. We have all the ingredients.

To perform a reach, we need a line (head + tail), and a target location. Well, in a chain, the tail of one segment is the head of another segment. For each segment, we simply perform a reach with the new target, until we run out of segments – then we slide the final tail to the target:

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// initialize target
var tgt = mousePosition();
 
// for each segment (except the last one)
for (var i = 0; i < segments.length - 1; i++){
  // perform a reach for this segment
  var r = reach(segments[i], segments[i + 1], tgt);
 
  // update this node
  // (`r[0]` is guaranteed to be the same point as `tgt`)
  segments[i] = r[0];
 
  // update the target, so the next segment's head
  // targets this segments new tail
  tgt = r[1];
}
 
// for the last segment, move it to the target
segments[segments.length - 1= tgt;
 

 

 

 

Forward and Backward

We haven’t quite made a robot arm though, have we?

A robot arm doesn’t move around freely like a chain – it’s fixed at the base.

The last insight of FABRIK is that since we are guaranteed the head of the chain will always reach its target, we can perform the iterative reach in reverse to ensure that the base of the arm will always stay fixed.

Said another way:

First we perform the iterative reach like normal, so the head is guaranteed to be touching the target. Then we perform the iterative reach in reverse, so that the tail is guaranteed to be touching the base.

And that gives us an arm:

 

 

 

Here’s the code:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// save the fixed base location
var base = segments[segments.length - 1];
 
// perform the iterative reach in the forward direction
// (same as before)
var tgt = mousePosition();
for (var i = 0; i < segments.length - 1; i++){
  var r = reach(segments[i], segments[i + 1], tgt);
  segments[i] = r[0];
  tgt = r[1];
}
segments[segments.length - 1= tgt;
 
// at this point, our base has moved from its original
// location... so perform the iterative reach in reverse,
// with the target set to the initial base location
tgt = base;
for (var i = segments.length - 1; i > 0; i--){
  var r = reach(segments[i], segments[i - 1], tgt);
  segments[i] = r[0];
  tgt = r[1];
}
segments[0= tgt;
 
 
 

Conclusion

It’s quite amazing that such a simple concept – a stretched reach – has turned into solving the complex and puzzling inverse kinematics problem.

There are a lot of ways to extend this algorithm too.

Moving into 3D complicates it because orientation of the segments matter, but by tweaking the reach function, it can be made to work.

Adding constraints like fixed range of motion over a joint is possible too, but requires thought about where to enforce the constraints during the iterative process.

Moving objects other than lines is possible too (i.e., rigid triangles), if you can break down the objects into lines, and traverse the heirarchy correctly while iterating.

The important part is understanding the core idea, which I hope is clear now. Go forth, experiment, and have fun!

 

permanent link
back to top

posted 9 Jan 2018 by Sean
tags: 2d, algorithm, javascript, heuristic

 

 

ref : https://sean.cm/a/fabrik-algorithm-2d

 

 

 

언리얼에서도 FABRIK 이 사용 되는것을 볼 수 있다

 

 

FABRIK

Forward And Backward Reaching Inverse Kinematics, 앞뒤로 늘어나는 IK 를 사용하는 애니메이션 노드입니다.

 

 

Forward And Backward Reaching Inverse Kinematics (FABRIK) 은 (최소 2 링크 이상) 임의 길이의 본 체인에 작동하는 IK 솔버입니다.

우선 (위 그림과 같이) 애니메이션 블루프린트  애님 그래프  FABRIK 노드를 추가하는 것으로 FABRIK 을 사용할 수 있습니다.

추가하고 나면, 컴포넌트 포즈 에 추가로 노드는 이펙터 트랜스폼 을 받는데, 절대 트랜스폼 이 될 수도, (같은 스켈레톤의 다른 본을 기준으로 하는) 절대 트랜스폼이 될 수도 있습니다. 엔드 이펙터 세팅은 TwoBone_IK 노드와도 매우 유사합니다.

적용된 스켈레탈 컨트롤의 현재 세기를 결정하기 위하여 FABRIK 노드는 플로트 Alpha 를 받는데, 노드 자체에서 설정하거나 플로트 변수를 통해 설정 가능합니다.

FABRIK 노드에 대한 디테일 패널에서는 솔버 작동방식을 더욱 세밀히 조절하는 데 사용할 수 있는 추가 세팅을 확인할 수 있습니다:

ref : 

https://docs.unrealengine.com/ko/Engine/Animation/NodeReference/Fabrik/index.html

영상 : https://www.youtube.com/watch?v=Okq-mkEvcGU&t=82s

 

 

 

 

위 알고리즘하고 매칭해본다면 End Effector  가 따라가는 Target, Solver 가  base 에 해당한다고 보면 된다

Effector : 오른 손_r  (hand_r)

Solver 는 허리 쪽 Spine_02 

 

 

 

 

반응형
반응형

 

다음 처럼 fn 함수를 만들고 Call in Eidtor(에디터에서 호출) 을 체크해놓으면

 

 

 

아래 그림처럼 Default 부분에 fn 버튼이 만들어지고 클릭하면 해당 함수가 바로 실행되는 것을 볼수가 있다

 

 

 

 

===========================================================

 

 

 

 

 

You can call Blueprint events and functions on demand in the Unreal Editor. This can be especially useful any time you need to run the same Blueprint graph both at runtime and in the editor. For example, you can test or preview your runtime gameplay within the editor UI. However, it can also be a simple way to trigger Blueprints within the editor that require an Actor or a location in 3D space as a context.

Supported Blueprint Classes

Not all Blueprint classes allow their Custom Events and Functions to run in the Unreal Editor.

  • The steps described below work for any Blueprint class that you can place in a Level—that is, any class that derives directly or indirectly from Actor.

  • If you need access to editor-only features like working with Assets in the Content Browser, you can derive your Blueprint class from a placeable editor-only base class, such as EditorUtilityActor. However, keep in mind that you won't be able to trigger your Blueprint at runtime when you use an editor-only base class, because editor-only classes are not included in packaged Unreal Engine applications.

Editor Utility Blueprint classes that derive from the Actor base class don't expose buttons in the Details panel for any Functions or Custom Events that are marked as callable in the editor. If you absolutely need to use a button in the Details panel to drive your Blueprint logic, create your graphs in a normal Blueprint class and not in an Editor Utility Blueprint class. However, for a much more flexible and powerful approach to creating a custom UI to drive Blueprint logic in the Unreal Editor, consider using an Editor Utility Widget instead.

Steps

  1. Any time you use a Custom Event node in the Event Graph of your Blueprint class, you can set the Graph > Call in Editor option in the Details panel:

    Similarly, when you create a new function on your Blueprint class, you can select the node for your new function and set the same option in the Details panel:

  2. Add an instance of your Blueprint class into your Level if you haven't already.

  3. Select the Blueprint Actor in the Level Viewport or in the World Outliner. The Details panel shows a button for each of the Call in Editor events and functions you've set up. You'll typically find them in the Default section, where Blueprint classes also expose variables that are marked as Instance Editable.

    If your Custom Event or function has any inputs, it will not be shown in the Details panel.

  4. Click these buttons to trigger execution of the Event Graphs starting from your Custom Event nodes, or to trigger your custom functions.

https://docs.unrealengine.com/ko/Engine/Editor/ScriptingAndAutomation/Blueprints/CallInEditor/index.html

반응형
반응형

비주얼 어시스트 언리얼엔진이 생겼습니다




매크로 관련하여 생겼는데 기존에 다른 방법으로 추가 할수 있는방법 이 있었지만 본격적으로 지원을 하기 시작하네요



반응형
반응형
SLATE_ATTRIBUTE 와 TAttribute 에 대해서 알아보겠습니다




먼저 언리얼 내부를 보다보면 SLATE_ATTRIBUTE  라는 것을 볼 수있다 그런데 이 매크로를 보면 다음과 같습니다



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define SLATE_ATTRIBUTE( AttrType, AttrName ) \
        TAttribute< AttrType > _##AttrName; \
        WidgetArgsType& AttrName( const TAttribute< AttrType >& InAttribute ) \
        { \
            _##AttrName = InAttribute; \
            return this->Me(); \
        } \
    \
        /* Bind attribute with delegate to a global function
         * NOTE: We use a template here to avoid 'typename' issues when hosting attributes inside templated classes */ \
        template< typename StaticFuncPtr > \
        WidgetArgsType& AttrName##_Static( StaticFuncPtr InFunc )    \
        { \
            _##AttrName = TAttribute< AttrType >::Create( TAttribute< AttrType >::FGetter::CreateStatic( InFunc ) ); \
            return this->Me(); \
        } \
        template< typename Var1Type > \
        WidgetArgsType& AttrName##_Static( typename TAttribute< AttrType >::FGetter::template TStaticDelegate_OneVar< Var1Type >::FFuncPtr InFunc, Var1Type Var1 )    \
        { \
            _##AttrName = TAttribute< AttrType >::Create( TAttribute< AttrType >::FGetter::CreateStatic( InFunc, Var1 ) ); \
            return this->Me(); \
        } \
cs


이 안에  TAttribute 라는 변수를 생성하고 있습니다

그런데 언뜻 보면   Slate_Attribute 매크로에서 이 TAttribute 타입 변수에 별의별 타입을 다 담는것 처럼 보여서 이것의 정체가 무엇인지
혼동 할 수가 있는데 이것은 함수와 인자를 담는 델리게이트의 역할을 같이 하고 있습니다

또한 델리게이트를 생성하는 방법은 다양합니다 함수를 델리게이트에 담는 방법, reference count 가 있는 shared 포인터와 연관된 
델리게이트 등 다양하게 델리게이트와 연관 될 수 있게 설계 되어 있습니다



example..
1
2
3
4
5
6
template< class UserClass, typename Var1Type >    \
WidgetArgsType& AttrName( TSharedRef< UserClass > InUserObjectRef, 
typename TAttribute< AttrType >::FGetter::template TSPMethodDelegate_OneVar_Const< UserClass, Var1Type >::FMethodPtr InFunc, 
Var1Type Var1 )    \
{ \
     _##AttrName = TAttribute< AttrType >::Create( TAttribute< AttrType >::FGetter::CreateSP( InUserObjectRef, InFunc, Var1 ) ); \
     return this->Me(); \
} \
cs




정리하자면 

언리얼 내부에 TAttribute 라는 template  클래스를 볼 수 가 있는데 

예를 들어 TAttribute<bool>  해주면 bool 이라는 변수 타입을 갖는 변수가 하나 생성 되고, 
또한 bool 변수에 대해 처리하고자 하는 바인딩 함수를 걸 수 있습니다

변수 타입의 확장형이라고 볼 수 있습니다 (확장이란 말이 약간 오해의 소지가 있을 수 있지만 코드를 보면 무슨 의미인지 아실 수 있을 겁니다)

SLATE_ATTRIBUTE 는 곧 변수와  델리게이트를 갖는 TAttribute 를 만들고 생성시 지정한 AttrName 에 델리게이트를 담는 함수를
define 한 매크로이다

Tip : 
또 매크로가 있는 특성상 지정한 이름에 뒤에 _Static 등을 붙여 함수로 만들어 치환 하는 경우가 많아서 그냥 소스를 보다가 갑자기
듣보 보도 못한 함수나 변수가 나오는게 이러한 이유때문이다, 자세한건 아래 소스를 참고하면 됩니다





아래 분석 구문을 보면.. 중간 중간 주석을 달아 놓았습니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#define FUNC_DECLARE_DELEGATE( DelegateName, ... ) \
 
    typedef TBaseDelegate<__VA_ARGS__> DelegateName;
 
// 이것이 delegate 생성기, 함수 등을 bind 할 수 있다
 
 
 


 
#define DECLARE_DELEGATE_RetVal( ReturnValueType, DelegateName ) FUNC_DECLARE_DELEGATE( DelegateName, ReturnValueType )
 
 

 


 
/*예로
아래 구문 중 이런 구문이 있다
    static TAttribute Create( typename FGetter::FStaticDelegate::FFuncPtr InFuncPtr )
    {
        const bool bExplicitConstructor = true;
        return TAttribute( FGetter::CreateStatic( InFuncPtr ), bExplicitConstructor );                //이 것이 델리게이트에 InFuncPtr 함수를 바인딩 하면서 생성 하는 과정
    }
FGetter 는 아래 정의된 델리게이트인데 , 델리게이트 FGetter 를 통해서 CreateStatic 함수로 InFuncPtr이라는 수를 바인딩해서 만든 델리게이트를 리턴해
TAttribute 를 생성한다
 
*/





//예를 들어 TAttribute<bool>  해주면 bool 이라는 변수 타입을 갖는 변수가 하나 생성 되고, 
//또한 bool 변수에 대해 처리하고자 하는 바인딩 함수를 걸 수 있다
/**
 * Attribute object
 */
template< typename ObjectType >
class TAttribute
{
public:
    /**
     * Attribute 'getter' delegate
     *
     * ObjectType GetValue() const
     *
     * @return  The attribute's value
     */
    DECLARE_DELEGATE_RetVal( ObjectType, FGetter );
    /** Default constructor. */
    TAttribute()
        : Value()         // NOTE: Potentially uninitialized for atomics!!
        , bIsSet(false)
        , Getter()
    { }
    /**
     * Construct implicitly from an initial value
     *
     * @param  InInitialValue  The value for this attribute
     */
    template< typename OtherType >
    TAttribute( const OtherType& InInitialValue )
        : Value( InInitialValue )        
        , bIsSet(true)
        , Getter()
    { }
........
중략
........
    /**
     * Static: Creates an attribute that's pre-bound to the specified 'getter' delegate
     *
     * @param  InGetter        Delegate to bind
     */
    static TAttribute Create( const FGetter& InGetter )
    {
        const bool bExplicitConstructor = true;
        return TAttribute( InGetter, bExplicitConstructor );
    }
    
    /**
     * Creates an attribute by binding an arbitrary function that will be called to generate this attribute's value on demand. 
     * After binding, the attribute will no longer have a value that can be accessed directly, and instead the bound
     * function will always be called to generate the value.
     *
     * @param  InFuncPtr Member function to bind.  The function's structure (return value, arguments, etc) must match IBoundAttributeDelegate's definition.
     */
    static TAttribute Create( typename FGetter::FStaticDelegate::FFuncPtr InFuncPtr )
    {
//이 것이 델리게이트에 InFuncPtr 함수를 바인딩 하면서 생성 하는 과정
        const bool bExplicitConstructor = true;
        return TAttribute( FGetter::CreateStatic( InFuncPtr ), bExplicitConstructor );   
    }
    /**
     * Sets the attribute's value
     *
     * @param  InNewValue  The value to set the attribute to
     */
    template< typename OtherType >
    void Set( const OtherType& InNewValue )
    {
        Getter.Unbind();
        Value = InNewValue;
        bIsSet = true;
    }
    /** Was this TAttribute ever assigned? */
    bool IsSet() const
    {
        return bIsSet;
    }
    /**
     * Binds an arbitrary function that will be called to generate this attribute's value on demand.
     * After binding, the attribute will no longer have a value that can be accessed directly, and instead the bound
     * function will always be called to generate the value.
     *
     * @param  InUserObject  Instance of the class that contains the member function you want to bind.
     * @param  InFunctionName Member function name to bind.
     */
    template< class SourceType >
    void BindUFunction( SourceType* InUserObject, const FName& InFunctionName )
    {
        bIsSet = true;
        Getter.BindUFunction(InUserObject, InFunctionName);
    }
    /**
     * Creates an attribute by binding an arbitrary function that will be called to generate this attribute's value on demand.
     * After binding, the attribute will no longer have a value that can be accessed directly, and instead the bound
     * function will always be called to generate the value.
     *
     * @param  InUserObject  Instance of the class that contains the member function you want to bind.
     * @param  InFunctionName Member function name to bind.
     */
    template< class SourceType >
    static TAttribute< ObjectType > Create(SourceType* InUserObject, const FName& InFunctionName)
    {
        TAttribute< ObjectType > Attrib;
        Attrib.BindUFunction<SourceType>(InUserObject, InFunctionName);
        return Attrib;
    }
........
중략
........

    /**
     * Is this attribute identical to another TAttribute.
     *
     * @param InOther The other attribute to compare with.
     * @return true if the attributes are identical, false otherwise.
     */
    bool IdenticalTo(const TAttribute& InOther) const
    {
        const bool bIsBound = IsBound();
        if ( bIsBound == InOther.IsBound() )
        {
            if ( bIsBound )
            {
                return Getter.GetHandle() == InOther.Getter.GetHandle();
            }
            else
            {
                return Value == InOther.Value;
            }
        }
        return false;
    }
........
중략
........
private:
    /** Special explicit constructor for TAttribute::Create() */
    TAttribute( const FGetter& InGetter, bool bExplicitConstructor )
        : Value()        
        , bIsSet( true )
        , Getter(InGetter)
    { }
    // We declare ourselves as a friend (templated using OtherType) so we can access members as needed
    template< class OtherType > friend class TAttribute;    


    /** Current value.  Mutable so that we can cache the value locally when using a bound Getter (allows const ref return value.) */
    mutable ObjectType Value;            //이건 이전에 이전에 지정한 타입에 대한 변수이고


    /** true when this attribute was explicitly set by a consumer, false when the attribute's value is set to the default*/
    bool bIsSet;
    /** Bound member function for this attribute (may be NULL if no function is bound.)  When set, all attempts
        to read the attribute's value will instead call this delegate to generate the value. */
    /** Our attribute's 'getter' delegate */
    FGetter Getter;                        //그래서 이게 delegate 가 됨
};
----------------------------------------------------------------------------------------
/**
 * Use this macro to add a attribute to the declaration of your widget.
 * An attribute can be a value or a function.
 */
#define SLATE_ATTRIBUTE( AttrType, AttrName ) \
        TAttribute< AttrType > _##AttrName; \
        WidgetArgsType& AttrName( const TAttribute< AttrType >& InAttribute ) \
        { \
            _##AttrName = InAttribute; \
            return this->Me(); \
        } \
    \
    ......
//some example..
SLATE_ATTRIBUTE( FText, ToolTipText )
SLATE_ATTRIBUTE( TOptional<EMouseCursor::Type>, Cursor )
SLATE_ATTRIBUTE( bool, IsEnabled )
SLATE_ATTRIBUTE( EVisibility, Visibility )
SLATE_ATTRIBUTE( TOptional<FSlateRenderTransform>, RenderTransform )
SLATE_ATTRIBUTE( FVector2D, RenderTransformPivot )
cs




그렇다면 왜 Slate_ATTRIBUTE 를 만들언 놓은 것인가 왜 델리게이트와 엮이는 매크로를 만들어 놓은 것인가..?




다음 구문을 한번 봅시다


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/* Base class for named arguments. Provides settings necessary for all widgets. */
template<typename WidgetType>
struct TSlateBaseNamedArgs
{
    typedef typename WidgetType::FArguments WidgetArgsType;
    
    TSlateBaseNamedArgs()
    : _ToolTipText()
    , _ToolTip()
    , _Cursor( TOptional<EMouseCursor::Type>() )
    , _IsEnabled( true )
    , _Visibility( EVisibility::Visible )
    , _RenderTransform( )
    , _RenderTransformPivot( FVector2D::ZeroVector )
    , _ForceVolatile( false )
    {
    }
 
    /** Used by the named argument pattern as a safe way to 'return *this' for call-chaining purposes. */
    WidgetArgsType& Me()
    {
        return *(static_cast<WidgetArgsType*>(this));
    }
 
    /** Add metadata to this widget. */
    WidgetArgsType& AddMetaData(TSharedRef<ISlateMetaData> InMetaData)
    {
        MetaData.Add(InMetaData);
        return Me();
    }
 
    /** Add metadata to this widget - convenience method - 1 argument */
    template<typename MetaDataType, typename Arg0Type>
    WidgetArgsType& AddMetaData(Arg0Type InArg0)
    {
        MetaData.Add(MakeShareable(new MetaDataType(InArg0)));
        return Me();
    }
 
    /** Add metadata to this widget - convenience method - 2 arguments */
    template<typename MetaDataType, typename Arg0Type, typename Arg1Type>
    WidgetArgsType& AddMetaData(Arg0Type InArg0, Arg1Type InArg1)
    {
        MetaData.Add(MakeShareable(new MetaDataType(InArg0, InArg1)));
        return Me();
    }
 
//이 아래 부분을 보면 됨..
    SLATE_ATTRIBUTE( FText, ToolTipText )
    SLATE_ARGUMENT( TSharedPtr<IToolTip>, ToolTip )
    SLATE_ATTRIBUTE( TOptional<EMouseCursor::Type>, Cursor )
    SLATE_ATTRIBUTE( bool, IsEnabled )
    SLATE_ATTRIBUTE( EVisibility, Visibility )
    SLATE_ATTRIBUTE( TOptional<FSlateRenderTransform>, RenderTransform )
    SLATE_ATTRIBUTE( FVector2D, RenderTransformPivot )
    SLATE_ARGUMENT( FName, Tag )
    SLATE_ARGUMENT( bool, ForceVolatile )
 
    TArray<TSharedRef<ISlateMetaData>> MetaData;
};
cs

 






여기에서 보면 SLATE_ATTRIBUTE 를 뭉태이로 쓴 것이 보이는데 변수 명 들을 보면 델리게이트와 역일 법한 것들로 보이죠?

즉 SLATE_ATTRIBUTE 로 변수를 만든 다는 것은 나중에 이 변수를 통해서 함수를 바인딩 하고 원하는 시점에 바인딩 된 함수들을 호출하기 위해

존재하는 것 정도로 그 의도를 추측 할 수가 있습니다


이것들을 묶고 있는 클래스 명 (TSlateBaseNamedArgs) 또한 이와 유사한 성향의 연관성을 띈 네이밍으로 볼 수 있습니다













3dmpengines.tistory.com

참고 : 언리얼엔진4 소스
URL : https://www.unrealengine.com


 


반응형
반응형

Unreal Engine

Welcome to the Unreal Engine source code!

From this repository you can build the Unreal Editor for Windows and Mac, compile Unreal Engine games for Android, iOS, Playstation 4, Xbox One, HTML5 and Linux, and build tools like Unreal Lightmass and Unreal Frontend. Modify them in any way you can imagine, and share your changes with others!

We have a heap of documentation available for the engine on the web. If you're looking for the answer to something, you may want to start here:

If you need more, just ask! A lot of Epic developers hang out on the forums or AnswerHub, and we're proud to be part of a well-meaning, friendly and welcoming community of thousands.

Branches

We publish source for the engine in three rolling branches:

The release branch is extensively tested by our QA team and makes a great starting point for learning the engine or making your own games. We work hard to make releases stable and reliable, and aim to publish new releases every few months.

The promoted branch is updated with builds for our artists and designers to use. We try to update it daily (though we often catch things that prevent us from doing so) and it's a good balance between getting the latest cool stuff and knowing most things work.

The master branch tracks live changes by our engine team. This is the cutting edge and may be buggy - it may not even compile. Battle-hardened developers eager to work lock-step with us on the latest and greatest should head here.

Other short-lived branches may pop-up from time to time as we stabilize new releases or hotfixes.

Getting up and running

The steps below will take you through cloning your own private fork, then compiling and running the editor yourself:

Windows

  1. Install GitHub for Windows then fork and clone our repository. To use Git from the command line, see the Setting up Git and Fork a Repo articles.

    If you'd prefer not to use Git, you can get the source with the 'Download ZIP' button on the right. The built-in Windows zip utility will mark the contents of zip files downloaded from the Internet as unsafe to execute, so right-click the zip file and select 'Properties...' and 'Unblock' before decompressing it. Third-party zip utilities don't normally do this.

  2. Install Visual Studio 2015. All desktop editions of Visual Studio 2015 can build UE4, including Visual Studio Community 2015, which is free for small teams and individual developers. Be sure to include C++ support as part of the install, which is disabled by default.

  3. Open your source folder in Explorer and run Setup.bat. This will download binary content for the engine, as well as installing prerequisites and setting up Unreal file associations. On Windows 8, a warning from SmartScreen may appear. Click "More info", then "Run anyway" to continue.

    A clean download of the engine binaries is currently 3-4gb, which may take some time to complete. Subsequent checkouts only require incremental downloads and will be much quicker.

  4. Run GenerateProjectFiles.bat to create project files for the engine. It should take less than a minute to complete.

  5. Load the project into Visual Studio by double-clicking on the UE4.sln file. Set your solution configuration to Development Editor and your solution platform to Win64, then right click on the UE4 target and select Build. It may take anywhere between 10 and 40 minutes to finish compiling, depending on your system specs.

  6. After compiling finishes, you can load the editor from Visual Studio by setting your startup project to UE4 and pressing F5 to debug.





https://github.com/EpicGames/UnrealEngine


반응형
반응형

 

 

 

MyOnBeginOverp 을 OnAAA 로 생각하고 보시면 됩니다

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
UCLASS(abstract)
class AAA : public UPageWidget
{
 
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "what the")
void OnAAA(UElem* _Elem);
}
 
 
 
void UTabPanel::OnAAA_Implementation(UElem* _Elem)
{
}
 
cs

 

 

 

BlueprintNativeEvent 만 있다면

 

1. C++ 에서 OnAAA 를 호출 할 수있는데 BP 에 OnAAA event 노드가 없다면 C++ 의  OnAAA_Impl... 은 호출된다

2. 하지만 BP 에 OnAAA event 노드가 있다면 C++ 의 OnAAA_Impl... 은 호출되지 않는다

3. 이때 BP 에 OnAAA event 노드에 대한 부모 OoAAA  노드 가 있다면 이 노드가 호출될때 C++ 의 OnAAA_Impl... 이 호출되게 된다

 

이벤트에 오른쪽 버튼을 눌러 부모의 OnAAA 이벤트를 추가하면 됨

 

 

그런데 이때는 이벤트 자체를 함수처럼 BP에서 호출 할 수가 없는데 그러고 싶을 때는

BlueprintCallable 를 같이 넣어주게 되면 이벤트를 함수처럼 호출 할 수 있다

이때 BP에서 함수로 이벤트를 호출 하면 호출 되는 대상은 BP의 이벤트 OnAAA 가 된다

 

 

반응형

'게임엔진(GameEngine) > Unreal4' 카테고리의 다른 글

SLATE_ATTRIBUTE 와 TAttribute  (0) 2017.05.14
Unreal Engine4 Build process  (0) 2017.05.13
언리얼 안드로이드 빌드 오류  (0) 2017.04.06
Generate Visual Studio project files  (0) 2017.03.31
PBR 자세한 샘플 예시  (0) 2017.03.31
반응형

언리얼 안드로이드 빌드 오류

언리얼 엔진을 사용하면서 안드로이드 웍스라는 패키지를 알게 되었고 이 패키지를 통해 쉽게 Android SDK 부터 JDK 까지 설치할 수 있었습니다.

안드로이드 퀵 스타트 문서를 참고 하시면 됩니다.

너무도 당연히 안드로이드 빌드를 하면 자연스럽게 되는 줄 알았는데 바로 에러에 직면하게 되더군요.


처음 오류는 스크린샷을 찍어두진 않았지만 경로가 제대로 지정되어 있지 않아 발생하는 오류 였습니다.

프로젝트 설정을 열어보니 경로 정보가 하나도 입력되어 있지 않더군요.
위 이미지와 같이 경로 이미지를 입력해 주었더니 해결 되었습니다.

하지만 그렇게 끝나면 간단했겠지만 실제로는 아래와 같은 에러가 다시 발생하더군요.

ERROR: SWT folder 'lib\x86_64' does not exist.



경로 설정도 다 했고 뭐가 문제일까? 역시나 구글링을 해보니 해결 방법이 보이더군요.

https://answers.unrealengine.com/questions/213886/cant-launch-and-build-for-android.html

이 글의 답변처럼 PATH를 지정해 줬더니 .... 안되더군요.



결국 한참을 헤매다가 재부팅을 했더니 해결되네요. -ㅅ-;
같은 삽질을 겪으시는 분들을 위해 해결 과정을 남겨 놓습니다.


참고자료

안드로이드 퀵 스타트
언리얼 빌드 시스템 가이드
https://answers.unrealengine.com/questions/213886/cant-launch-and-build-for-android.html




출처 : https://indiegd.blogspot.kr/2016/11/blog-post_8.html

반응형
반응형

Generate Visual Studio project files






uproject 파일에서 오른쪽 버튼을 누르면 Generate Visual Studio project files  이 메뉴를 볼 수 있는데 이 명령어가 하는 역할은


.sin 과 .vcxproj 파일들을 자동으로 생성해 준다는 것이다


즉 모든 프로젝트 파일을 자동으로 저 명령어로 만들어진다는 것


이 말은 버전관리 대상이 될 필요는 없다는 뜻과도 같음


vcxproj.filters 와 vcxproj.user 파일도 마찬가지

반응형
반응형

물리 기반 머티리얼


이 글은 언리얼 엔진 4 의 물리 기반 머티리얼 시스템을 빠르게 익히고자 하는 이를 돕기 위해 디자인된 문서입니다. 최소한 언리얼 엔진 3 이상, 언리얼 엔진에서의 머티리얼 생성에 어느정도는 익숙한 분들을 대상으로 합니다. 언리얼에서의 머티리얼이 완전 처음이신 경우, 핵심 머티리얼 개념 문서부터 먼저 참고해 보시는 편이 나을 것입니다.

여기서의 목표는 이 시스템을 사용하여 표준 머티리얼을 제작하는 방식을 "평이한 언어로 요점정리" 해 드리기 위함으로, 기존 버전의 언리얼 엔진 작업방식에 익숙한 분들에게도 헛갈릴 수 있기 때문입니다. 물리 기반 접근법에 관련해서 오직 머티리얼 관련된 부분에만 초점을 맞춰 보겠습니다. 사용가능한 입력 전체에 대한 설명은, 머티리얼 입력 문서를 참고해 주세요.

물리 기반이란?

Physically based, 물리 기반 셰이딩이란, 빛의 작용이 이래야 할 것이다 하고 직관적으로 추정하기 보다는, 실제로 어떠한 작용을 하는지 추정하는 것을 뜻합니다. 그 결과 좀 더 정확하고 자연스러운 외양을 얻을 수 있습니다. 물리 기반 머티리얼은 라이팅 환경에서도 똑같이 잘 작동합니다. 게다가 머티리얼 값도 덜 복잡해지고 상호의존적이 되어, 좀 더 직관적인 인터페이스가 가능해 졌습니다. 이러한 장점은 픽사와 [4] 디즈니의 [3] 최근작에서 명확히 볼 수 있듯이, 비실사 렌더링에도 적용 가능합니다.

이런 저런 이유로 인해, 언리얼 엔진 4 에서는 새로운 물리 기반 머티리얼 및 셰이딩 모델을 채택했습니다. UE4 의 물리 기반 머티리얼 및 셰이딩 모델 관련해서 기술적으로 자세한 내용은, 시그래프 프리젠테이션을 [2] 참고해 주시기 바랍니다.


아래 링크가 이 글의 원글인데 슬라이드를 움직이면서 각각 PBR속성별 자세한 결과들을 볼 수가 있습니다

https://docs.unrealengine.com/latest/KOR/Engine/Rendering/Materials/PhysicallyBased/index.html

반응형
반응형




인터페이스 선언

인터페이스 클래스 선언은 일반 언리얼 클래스 선언과 비슷하나, 크게 두 가지 차이점이 있습니다. 첫째, 인터페이스 클래스는 UCLASS 매크로 대신 UINTERFACE 매크로를 사용하며, UObject 를 직접 상속하는 대신 UInterface 를 상속합니다.

UINTERFACE([specifier, specifier, ...], [meta(key=value, key=value, ...)])
class UClassName : public UInterface
{
    GENERATED_BODY()
};

둘째, UINTERFACE 클래스는 실제 인터페이스가 아닙니다. 언리얼 엔진의 리플렉션 시스템에 보이도록 하기 위해서만 존재하는 공백 클래스입니다. 다른 클래스에서 상속하게 되는 실제 인터페이스는 같은 클래스 이름에 첫 글자만 "U" 에서 "I" 로 바뀝니다.

.h 파일에서 (예: ReactToTriggerInterface.h):

#pragma once

#include "ReactToTriggerInterface.generated.h"

UINTERFACE(Blueprintable)
class UReactToTriggerInterface : public UInterface
{
    GENERATED_BODY()
};

class IReactToTriggerInterface
{    
    GENERATED_BODY()

public:
    /** 이 오브젝트를 활성화시키는 트리거 볼륨에 반응합니다. 반응에 성공하면 true 를 반환합니다. */
    UFUNCTION(BlueprintCallable, BlueprintCallable, Category="Trigger Reaction")
    bool ReactToTrigger() const;
};

함수를 .h 파일에 직접 작성할 수 있는데, 기본적으로 아무 것도 하지 않는 함수이거나 false, 0, 공백 스트링 반환처럼 사소한 동작을 하는 함수의 경우 종종 그렇게 합니다. 보다 복잡한 함수는 .cpp 파일에 작성하여 컴파일 시간을 줄일 수 있습니다. 순수 가상 (pure virtual) 함수가 지원됩니다. 여기 샘플 함수는 .h 파일에 작성해도 될만큼 간단하긴 하지만, .cpp 파일에 작성하도록 하겠습니다. (ReactToTriggerInterface.cpp 와 같은) cpp 파일에 이제 다음과 같은 부분이 있을 것입니다:

#include "ReactToTriggerInterface.h"

bool IReactToTriggerInterface::ReactToTrigger() const
{
    return false;
}

접두사가 "U" 인 클래스는 생성자나 기타 다른 함수가 필요치 않은 반면, "I" 인 클래스는 모든 인터페이스 함수를 포함하며, 다른 클래스에 실제로 상속되는 클래스이기도 합니다.

블루프린트가 이 인터페이스를 구현할 수 있도록 하려면 Blueprintable 지정자가 필요합니다.





C++ 로 인터페이스 구현

새 클래스에서 인터페이스를 사용하려면, 단순히 (사용중인 UObject 기반 클래스에 추가로) 접두사가 "I" 인 인터페이스 클래스를 상속하기만 하면 됩니다.

class ATrap : public AActor, public IReactToTriggerInterface
{
    GENERATED_BODY()

public:
    virtual bool ReactToTrigger() const override;
};

주어진 클래스의 인터페이스 구현 여부 확인

인터페이스를 구현하는 두 C++ 와 블루프린트 클래스 사이의 호환을 위해서는, 다음 함수 중 하나를 사용하면 됩니다:



// OriginalObject 가 UReactToTriggerInterface 를 구현한다면 bIsImplemented 는 true 가 됩니다.

bool bIsImplemented = OriginalObject->GetClass()->ImplementsInterface(UReactToTriggerInterface::StaticClass()); 


// OriginalObject 가 UReactToTriggerInterface 를 구현한다면 ReactingObject 는 null 이외의 값이 됩니다.

IReactToTriggerInterface* ReactingObject = Cast<IReactToTriggerInterface>(OriginalObject); 



U 접두사 클래스에 Cast 를 사용하려고 하면 실패하는 반면, StaticClass 함수는 I 접두사 클래스에 구현되어 있지 않아 컴파일되지 않습니다.

다른 언리얼 유형으로의 형 변환

언리얼 엔진의 형변환 시스템은 한 인터페이스에서 다른 인터페이스로, 또는 인터페이스에서 적합한 경우 언리얼 유형으로 형 변환을 지원합니다.


// 인터페이스가 구현된 경우 ReactingObject 는 null 이외의 값이 됩니다.

IReactToTriggerInterface* ReactingObject = Cast<IReactToTriggerInterface>(OriginalObject); 


// ReactingObject 가 null 이외의 값이고 ISomeOtherInterface 를 구현하는 경우 DifferentInterface 는 null 이외의 값이 됩니다.

ISomeOtherInterface* DifferentInterface = Cast<ISomeOtherInterface>(ReactingObject); 


// ReactingObject 가 null 이외의 값이고 OriginalObject 는 AActor 또는 AActor 파생 클래스인 경우, Actor 는 null 이외의 값이 됩니다.

AActor* Actor = Cast<AActor>(ReactingObject); 


블루프린트 구현가능 클래스

블루프린트가 이 인터페이스를 구현할 수 있도록 하려면, Blueprintable 메타데이터 지정자를 사용해야 합니다. 블루프린트 클래스가 덮어쓰려는 모든 인터페이스 함수는 BlueprintNativeEvent 또는 BlueprintImplementableEvent 여야 합니다. BlueprintCallable 마킹된 함수는 여전히 호출은 가능할 것이나, 덮어쓰기는 불가능합니다. 다른 모든 함수는 블루프린트에서 접근할 수 없을 것입니다.




출처 : https://docs.unrealengine.com/latest/KOR/Programming/UnrealArchitecture/Reference/Interfaces/index.html



반응형
반응형


UUserWidget 클래스에서 SynchronizeProperties 함수가 있는데 


//~ Begin UWidget Interface

virtual void SynchronizeProperties() override;


이 클래스를 상속받아 함수를 오버라이드 해서 관련 코드를 작성하면 해당 결과를 에디터 상에서 실행 없이 바로 볼 수 있다



UCLASS(Blueprintable)

class UCCCWidget : public UUserWidget 


    virtual void SynchronizeProperties() override;


};





void UCCCWidget::SynchronizeProperties()

{

    Super::SynchronizeProperties();

//원하는 동작 작성..

}


반응형
반응형




World Location 은 일반적으로 알고 있는 월드 공간에서의 좌표 위치를 말한다


어떤 오브젝트의 자식 일지라도 자식을 SetWorldLocation 으로 위치를 지정하면 자식 오브젝트는 월드 좌표로 값이 세팅 되어 위치하게 된다는데에서는


큰 어려움은 없는데, Relative 의 경우에는 다른 말로 Local 좌표라고도 얘기를 하는데 이 것은 한 오브젝트에 대해서 해당 하는 내용이 아닌


자식 오브젝트(or Compoenent )를 가지고 있는 경우 자식의 위치를 부모 기준으로 얼마나 이동 할지에 대한 위치를 설정할때 쓰는 함수or 변수이다



하지만 특이점은(특이점이라 할 것도 없지만) 자식 오브젝트가 root 자체인 경우 즉 자식을 가지고 있지 않은 경우에서의 Relative Location 은 부모의 Location 즉 World Location 과 동일한 값을 가지게 된다!!







 3DMP engines



반응형
반응형

A 메쉬애니를 B메쉬에 동일한 애니메이션을 적용 시키고자 할 때 SetMasterPoseComponent를 쓰면 된다

동일한 동작이 됨으로 어떤 연출이나 효과에 좀 쓰일 수 있는 컴포넌트


USkinnedMeshComponent::SetMasterPoseComponent








UFUNCTION (BlueprintCallable , Category="Components|SkinnedMesh" )
void SetMasterPoseComponent
(
    USkinnedMeshComponent * NewMasterBoneComponent
)




참고 Ref :

https://docs.unrealengine.com/latest/INT/API/Runtime/Engine/Components/USkinnedMeshComponent/SetMasterPoseComponent/index.html

반응형
반응형



Render 3D objects in UMG Widget/HUD


https://engineunreal.wordpress.com/2015/04/21/render-3d-objects-in-umg-widget-hud/

As you may be aware of, it is not possible to directly render 3D objects/blueprints/meshes in a UMG widget (as of UE 4.7.4 at the time of writing).

I shall try to clearly explain how I achieved it. Here is what we are trying to get as a result: [gif coming soon]

The basic principle is to capture your 3D object to a 2D ‘image’ that you will be able to use in your UMG widget.

Basic explanation is good, but having more details is even better! So here are the steps:

  1. Create a scene capture blueprint
  2. Capture to a 2D texture
  3. Create a material from that texture
  4. Use that material in your widget

ProcessI you feel more like reverse engineering without reading what I have to say, I also made the code available on GitHub :p Download from GitHub


Prerequisites / Project setup

What you need to be able to follow this is a UE4 project with a UMG widget that displays somewhere in your game (basically in your HUD for instance).

If you don’t have such a UE project, you can follow this simple tutorial and come back here right after having completed it.

But if your very own project seems to fit to that description, you can skip directly to the scene capture creation.

Now that we are on common grounds, we are good to go!

1 – Create a scene capture blueprint

Begin by creating a… yep, a Blueprint, how surprising! Choose ‘Actor’ as its parent class. I named it ‘CaptureBlueprint’. New Actor Blueprint

Open it. We will start by creating a closed ‘room’. For that, add 6 StaticMesh components ‘Floor_400x400’ (that mesh is provided by default in the engine). Make sure that the root component of the BP is the actual floor of the ‘room’. “Why the heck would I need to build a box?!”, that’s probably what you’re thinking right now. I know that this comes ‘out of the blue’, but trust me, it will make sense! Here is what it looks like without the roof: room_outside

Now that we have our ‘studio’ (I think that’s a cool descriptive name actually, I’m going to call it ‘studio’ until the end), we need to change its walls color. We are willing to create a ‘bluescreen’, yes, exactly like the ones they use when shooting movies!

For that purpose, we will create a Material that we will be able to apply on those walls. I named it ‘StudioMaterial’. Edit it. Add a Constant4Vector component from the Palette and change its color to a color your 3D model won’t contain (again, this is a bluescreen). I chose a bright cyan color RGBA(0, 1, 1, 0). Studio Material color

Set this color as the Emissive Color of the Material. Finally, set the Shading Model of the Material to Unlit. This is really important, as with that option, our studio walls won’t be affected by the 3D object shadows and the lights, and will therefore appear with just one united color (the one we’ve just chosen, actually). Studio Material Unlit

Now back to our studio, set the walls material to the one we just created. You can now add the 3D Object/Mesh you want to render in the UMG widget to the studio! I did not have the time to find a funny model to render, so I just chose a default Mesh (SM_MatPreviewMesh_02). Make sure it is in the studio room (at least for the part you want to see). You can of course make the studio bigger if needed. Studio BlueprintThen add a Camera to the Blueprint. Try to put it in front of the Object to render, so that the camera can actually film it. Studio Camera

Finally, add a SceneCaptureComponent2D to the Camera. That is the component we are going to use to render what the camera films to a 2D Texture. Studio Capture component

As a final step for our studio, we add some light to the Blueprint. I insert the Light component in front of my object and I let the light options to their default value. You can of course add more than one, tweek their options, and place them wherever you like. If you don’t add light(s), then your capture will look very dark (but it could be the effect you want, I don’t know!). Add light to the studio

You can now relax, we are done with the tricky part of this. Compared to what we have already achieved here, what’s left will seem as easy as my first girlfr… NO, I cancel this sentence.


2 – Capture to a 2D texture

Now we need to tell our SceneCaptureComponent to what it should save what it captures. For that purpose, we first have to create a new TextureRenderTarget2D in our project. I named it ‘TextureRenderTarget’. Render Target creation

Edit it and set the ‘Size X’ and ‘Size Y’ to one that fits your needs. Note that the default is 256×256, and that those sizes will impact the quality of the rendering in UMG. Here I chose the maximum size, which is 2048×2048. Render Target size settings

Now back to our ‘CaptureBlueprint’, just set this brand new element as the Texture Target of the Scene Capture Component, then save. Set BP Render Target

That’s it, what the camera captures will be saved to your 2D texture!


3 – Create a material from the texture

Here we are going to ‘transform’ our captured 2D texture by removing its blue background (or the color you chose for your studio walls) and ‘convert’ it to a material that we will eventually be able to use in UMG.

First, create a new Material. I named it ‘CapturedMaterial’. Save and edit it. Set its Blend Mode to ‘Translucent’, because we will need some transparency (the Unreal Engine wiki is really insightful about the blend modes if you want to know more about them). Then set its Shading Mode to ‘Unlit’, to be sure that it won’t be altered by any dynamic light once in our HUD. HUD Material basic settings

Once this is done, be sure to check ‘Used with UI’ in the ‘Usage’ panel of the settings, as if you don’t you simply won’t be able to select this Material in a UMG widget. HUD Material UI setting

Now we’re good to go! Add a Texture Sample to the Material, and set your ‘TextureRenderTarget’ as its Texture in the settings panel. HUD Material texture sample add

Link it to the Material ‘Emissive Color’ pin. HUD Material emissive

Now comes the part where we are going to make our studio background color transparent, so that only our model will be visible in our HUD.

Add a Constant3Vector component from the Palette and change its color to the color you used as the walls color – remember that I used the color RGBA(0, 1, 1). HUD Material wall color

Then add the necessary elements to make your material look like this (the ‘0’ and the ‘1’ are simple Constant elements): HUD Material wall color transparency

Don’t forget to set the B constant of the If component. Adjust it if you later notice that the borders of your object are blue-ish when rendered (the higher its value is, the more blue will be transparent, but it could be that some unexpected parts of your object disappear if the value is too high). UMG Material B value

This is simply the part handling the transparency of the background color. I think what it does is straightforward enough.

Save. That’s it! Our Material is done!


4 – Use the material in a UMG widget

Now that we have a Material we can use in UMG, let’s… yeah, let’s use it!

Edit the UMG widget in which you want the 3D object to appear, and add an Image component where you want the object to appear. I will simply add it to the basic Canvas Panel, as you can see. UMG add image

Size it the way you desire. I am simply gonna go with 256×256. UMG image size

Then, in the Appearance panel, under Brush, set our ‘CapturedMaterial’ as the Image. UMG set Material as Image

Compile and save.

Last step, we need to add our CaptureBlueprint to our map, because it will not capture anything if it is not physically present in the world. Place it so that the player will not be aware of its existence (basically outside or under the map). Place capture BP in mapNotice that when you select a Blueprint that has a camera in it, a small window opens and shows what the camera sees.

Save, hit play, and enjoy: Your object is actually rendered on the HUD! Notice that the borders of my object are a bit blue-ish because of the reflections of the studio walls. If that happens to you too, you can fix it by adding lights to the studio in the CaptureBlueprint, and/or by augmenting your object material roughness.

Final result

Finally…That’s it, we have rendered a 3D Object in UMG! For now it does nothing in particular exept just standing there, but hey, it is a Blueprint after all, you can all the behaviors and functionnalities you can imagine to it. I don’t know, why not a 3D health gauge, with some particle effects like steam when it goes in the red for instance? Or whatever you need!

The whole project is available on GitHub if you need it. Feel free to clone/fork/download it!

Download from GitHub


Conclusion

I must admit that this looks more like a workaround than a real nice and perfect solution, and I realize that it needs a lot of some tweeking to look decent. But it will do the trick until Epic Games makes this a native feature of its great UMG… I hope…. And if I say please?!…😦

Don’t hesitate if any part of this seems unclear, or if you have any question/wittycomment/suggestion, or whatever.

See ya!


반응형
반응형

Vulkan 이 언리얼4에 들어가게됩니다, 언리얼 4.14 preview 에 포함 되는데.. 이런 언리얼 엔진을 깔아야 함...


아놔 빌드 시간,,, ㅡ.ㅡ;;;


벌칸은 오픈지엘 차기작 버전으로 폰에는 겔럭시 7 부터 가능해용~


Vulkan

https://wiki.unrealengine.com/Vulkan

How to use Vulkan in Unreal Engine 4


Rate this Article:
5.00
 (one vote)

Approved for Versions:(master)

Overview

On February 16th 2016 the final Vulkan specifications along with the Vulkan SDK were released. Vulkan is a new graphics API and Unreal Engine 4 supports it! In this small tutorial I will show you how to use Vulkan in UE4. So let's get started!

Setup

Setting up Vulkan for UE4 is easy, but you have to make sure to do everything in the correct order.

1. Download the newest graphics driver for your GPU, make sure you get the version with Vulkan support. Both AMD and Nvidia have released such drivers. Download it, install it and restart your PC.

2. Go to the official website and download the Vulkan SDK for your platform: http://lunarg.com/vulkan-sdk/ Then just install it, not that much to say about it.

3. Go to the Unreal Engine 4 github site: https://github.com/EpicGames/UnrealEngine

For being able to access it, you need to have your Unreal Engine Account linked to your github Account. Select the "master" branch and click on "download zip" on the right. Wait for the download to finish, extract the zip file, then first run the "Setup.bat" and once that finished, run the "GenerateProjectFiles.bat" and then double click the generated "UE4.sln" file to open up the Visual Studio project.

Right click the UE4 solution and select "Build". Then you have to wait a while for Unreal Engine to build. It's important that you build the engine *after* you installed the SDK. So if you have a recent master build, you can't use it if you only installed the Vulkan SDK after you built the engine.

4. Once the engine build finished, there are multiple options how to get Vulkan running. You can launch the whole Engine in "Vulkan Mode" with going into the Engine/Binaries/Win64/ directory, then right click the "UE4Editor.exe" and select "Create Shortcut". Then edit the shortcut so that it's pointing to

"C:\YOUR_INSTALL_DIRECTORY\Engine\Binaries\Win64\UE4Editor.exe" -vulkan

You can now launch the engine from that shortcut and it's running Vulkan!

Another way to see it running is just using the engine build without the -vulkan parameter, but using the new "Vulkan Preview" mode you find below the "Mobile Preview" you probably know about.

VulkanPreview.jpg

This will launch your game as a standalone application running Vulkan. On first launch, a lot of Shaders will have to compile and that might take a while, but once that's finished, you see your project running on the new Vulkan API!

But keep in mind that Vulkan is only using the mobile rendering path of UE4 at the moment, so you won't be able to use some of the "High End" features UE4 offers. Still, it's awesome to see a new API like Vulkan to run on your PC :)


I hope you find some useful information in this, is there are any questions left I would suggest to ask in the forums :) Cheers!

~John Alcatraz


반응형
반응형

1. 먼저 해야 되는게 우선 마티네를 클릭한다!!! (이게 중요)

2.  1번 직 후 바로, 레벨 블루프린트에서 오른쪽 버튼을 눌러서 마티네

 컨트롤 이벤트를 추가한다

3. 이후에 이벤트 트렉이 이벤트를 추가하면 자동으로 레벨블루프린트에 넣어 놓았던 이벤트에 추가적으로 이벤트가 들어 가 있는 것을 확인 할 수 



https://docs.unrealengine.com/latest/KOR/Engine/Matinee/UserGuide/BlueprintComponents/index.html


시간에 따라 이벤트 발동시키기

마티네에서, 블루프린트 액터에 대해 이벤트 트랙을 만들어 특정 이벤트를 발동시킬 시간을 정할 수 있습니다. 그런 다음 해당 이벤트를 블루프린트에 노출시키고 특정 시간에 발동시키고자 하는 함수에 걸어줍니다.

마티네 이벤트를 사용하여 블루프린트에서 특정 시간에 동작을 발동시키려면:

  1. 마티네에서, 블루프린트에 연관된 그룹에 이벤트 트랙을 추가합니다.

    Matinee_EventTrack.png

  2. 이벤트 트랙의 특정 시간에 키프레임을 추가합니다. 키프레임을 추가할 때마다, 새 이벤트에 이름을 지어달라는 창이 뜹니다.

    Matinee_AddEvent.png

    Matinee_Events.png

  3. 레벨 블루프린트에서, 마티네 액터를 선택한 채로, 그래프에 우클릭합니다. 가능한 옵션 중에 마티네 액터에 대해 마티네 콘트롤러(MatineeController)를 추가하는 옵션이 있습니다. 선택해서 마티네 콘트롤러 노드를 생성해 주면, 거기에 마티네에서 키프레임을 생성한 이벤트가 나열됩니다.

    Matinee_AddController.png

    Matinee_ControllerWithEvents.png

    이제 마티네 콘트롤러의 이벤트 실행 핀을 마티네 시퀀스 재생 도중 발동시키려는 함수 노드에 걸어주면 됩니다.


반응형
반응형

이것저것 하다 간만에 다시 이벤트를 만드는데 순간 어떻게 했지라는 정적이...

간단한걸 까먹는다 .. ㅋ


How do i call a custom event funtion?


https://answers.unrealengine.com/questions/154297/how-do-i-call-a-custom-event-funtion.html



I'm following this tutorial:

https://www.youtube.com/watch?list=PLCWzG3zfTUPOMKrOw3MS6rrx4ACxbF6Kg&v=DH8HHqiMbWA&feature=player_detailpage#t=595

At about the ten minute mark I am told to create a custom event, which I can do; however, immediately after I am told to call the custom event function and connect it to the first sequence pin. When I try to call the function, the function does not exist even though I have the option checked off to call the event in the editor.

How can I accomplish what is being done in the video?

I am using the 4.6.1 version of the engine.

Thanks

Product Version: Not Selected
Tags:
more ▼

eXi gravatar image eXi ♦ Jan 05 '15 at 6:25 AM

Öhm, this should work the same way, but i'm not sure if Epic changed something in the 4.6.1 update. There are a few things to check:

Are you sure you spelled it correctly?

Would you mind trying to press "Compile" in the upper left corner first?

Does this also happen in a complete new project?

Can you show me your Blueprint (:?

Can you make sure that you have "Context sensitive" marked at the upper right corner of the contest menu that opens when you rightclick?

Can you try calling the event by only rightclicking and searching it instead of dragging of the pin.

orrimer gravatar image orrimer Jan 05 '15 at 3:20 PM

https://www.dropbox.com/s/usido8i2gxdcyuk/Screenshot%202015-01-05%2001.16.12.png?dl=0

Attached screenshot shows a custom event I made and what happens when i try to search for the function call. It was compiled before searching. I started off with a blank c++ template and then created a new empty level. Please let me know if there is any other information you require.

eXi gravatar image eXi ♦ Jan 05 '15 at 6:26 PM

I'm not at my pc right now, but would you mind disabling "Call in Editor" at the Event options at the lower left of the BP and try it again?

rjvm gravatar image rjvm Jan 05 '15 at 10:25 PM

in level BP right clic and put this ===> alt text

alt text

custon event.png (60.3 kB)
call event.jpg (298.4 kB)



반응형
반응형

하드디스크에 정보를 저장하기


Remarks

This class acts as a base class for a save game object that can be used to save state about the game. When you create your own save game subclass, you would add member variables for the information that you want to save. Then when you want to save a game, create an instance of this object using CreateSaveGameObject, fill in the data, and use SaveGameToSlot, providing a slot name. To load the game you then just use LoadGameFromSlot, and read the data from the resulting object.



https://docs.unrealengine.com/latest/KOR/Gameplay/SaveGame/index.html

게임 저장하기

  • SHARE:

언리얼 엔진

게임 저장 및 로드 구성을 위한 가장 간단한 방법은 SaveGame 오브젝트를 사용하는 것입니다.

BlankSaveGame.png

SaveGame 오브젝트를 여러가지 부속으로 된 박스로 생각해 보세요. 각 부분마다 변수를 만들어 일정량의 정보를 저장하도록 설정할 수 있습니다. 예를 들어 플레이어의 점수는 물론 이름도 저장하는 변수를 만들 수 있습니다. Character, PlayerController, GameMode 와 같은 다른 클래스에서 SaveGame 오브젝트로 정보를 전달할 수 있습니다. 그러기 위해서는 먼저 SaveGame 오브젝트의 인스턴스를 생성하거나 복사해야 합니다.

SaveGameVariables.png

그런 다음 예를 들어 PlayerController 에서 플레이어의 점수와 이름에 대한 값을 받은 다음 SaveGame 오브젝트 내 일치하는 변수에 저장하면 됩니다.

StoreVariables.png

SaveGame 오브젝트는 이제 이 변수 값을 하드 디스크에 저장할 때까지 유지합니다.

StoredVariables.png

마지막으로 SaveGameToSlot 을 사용해서 SaveGame 오브젝트를 하드 디스크 상의 파일에 저장하면 됩니다. 다른 프로그램에서 새 게임 또는 문서를 저장하는 것과 똑같은 방식으로 이루어지는데, SaveGameToSlot 에 파일명을 지어주기 때문입니다. 기본적으로 세이브 게임은 Saved\SaveGames 폴더에 .sav 파일로 저장됩니다. SaveGameToSlot 에 사용자 ID 도 주어야 나중에 로드할 정보를 찾아보는 데 도움이 됩니다.

SaveGame 오브젝트에서 정보를 로드하는 것 역시 단순한 프로세스인데, 정보 저장의 거의 반대입니다. LoadGameFromSlot 함수를 사용하여 입력에서 파일명과 사용자 ID 를 받습니다. LoadGameFromSlot 으로 하드 디스크에서의 정보를 로드하면 SaveGame 오브젝트의 새로운 사본이 생성됩니다.

StoredVariables.png

이 오브젝트를 사용하면 게임내 다른 오브젝트와 액터에 있는 정보를 복사할 수 있습니다. 이 예제에서는 플레이어가 저장된 게임을 열어 플레이를 재시작할 때와 마찬가지로 플레이어의 점수와 이름을 PlayerController 속으로 로드시킵니다.

LoadingVariables.png





블루프린트로 게임 저장하기

https://docs.unrealengine.com/latest/KOR/Gameplay/SaveGame/Blueprints/index.html

  • SHARE:
언리얼 엔진

SaveGame 오브젝트 생성

SaveGame 오브젝트를 새로 생성하기 위해서는, 블루프린트 클래스를 새로 생성 합니다. 부모 클래스 선택 대화창이 뜨면, Custom Class드롭다운을 펼친 다음 SaveGame 을 선택합니다. 검색창에서 SaveGame 에 바로 점프할 수 있습니다. 새 블루프린트 이름은 MySaveGame 이라 합니다.

savegame.png

새로 만든 SaveGame 오브젝트 블루프린트에서, 저장하고자 하는 정보를 담을 변수를 생성합니다.

이 예제에서는, SaveSlotName 과 UserIndex 에 대한 기본값 저장에 사용될 변수도 선언되어 있어, 이 SaveGame 오브젝트에 저장하는 각 클래스에서는 그 변수를 따로 설정할 필요가 없습니다. 이 단계는 옵션으로, 기본값이 변경되지 않은 경우 하나의 세이브 슬롯을 덮어쓰게 됩니다.

SaveGameVariables.png

블루프린트 컴파일 이후 변수에 대한 기본값을 설정할 수 있습니다.

변수 저장하기

전체 저장 구성 스냅샷입니다. 이미지를 클릭하면 원래 크기로 볼 수 있습니다.

먼저 Create Save Game Object 노드를 사용하여 SaveGame 오브젝트의 인스턴스를 생성 또는 복사합니다. Save Game Class 드롭다운 이름을 새로운 SaveGame 오브젝트 블루프린트 이름으로 설정했는지 확인합니다. 이 경우 MySaveGame 입니다. Create Save Game Object 노드가 범용 SaveGame 오브젝트 사본을 만들기 때문에, Return Value 핀을 드래그한 다음 Cast to MySaveGame 를 놓아야 합니다. 나중에 SaveGame 오브젝트를 쉽게 재사용하려면 변수로 승격 을 통해 변수에 대한 형변환 결과를 저장하세요.

SaveGameBP_1.png

이제 커스텀 SaveGame 오브젝트인 MySaveGmae 사본을 Save Game Instance 변수에 저장했으니, 이제 거기에 정보를 전송할 수 있습니다. 여기서 Player Name 은 PlayerOne 으로 설정되어 있습니다. 정보를 저장하고자 하는 변수가 더 있는 경우, 이 단계를 반복해 주면 됩니다.

SaveGameBP_2.png

이제 Save Game To Slot 노드를 사용하여 SaveGame 오브젝트의 정보를 하드 디스크에 저장하면 됩니다. Save Game To Slot 노드는 파일명과 사용자 ID 를 받는데, 여기서는 앞서 만들어둔 기본값이 됩니다.

SaveGameBP_3.png

변수 로드하기

전체 저장 셋업에 대한 스냅샷입니다. 이미지를 클릭하면 확대해 볼 수 있습니다.

첫 단계는 옵션으로, SaveGame 오브젝트 안에 기본 세이브 슬롯과 사용자 ID 를 저장하는 것을 기반으로 합니다. 여기서는 빈 SaveGame 오브젝트를 먼저 생성하여, 거기서 기본 SaveSlotName 과 UserIndex 를 읽을 수 있도록 합니다. 마찬가지로 이 부분은 모든 게임 구현에 적용되는 것은 아닙니다.

LoadGameBP_1.png

Load Game From Slot 노드를 사용하여 SaveGame 오브젝트 블루프린트의 인스턴스를 생성할 수 있습니다. 정보 저장을 위해 SaveGame 오브젝트를 생성했을 때와 마찬가지로, 특정 SaveGame 오브젝트 블루프린트로 형변환시켜 줘야 그 결과를 변수에 저장하여 나중에 쉽게 접근할 수 있습니다.

LoadGameBP_2.png

하드 디스크에서 새로운 SaveGame 오브젝트를 로드한 이후에는 변수값을 읽어 필요한 액터 또는 클래스에 할당하거나 여기서처럼 직접 사용할 수도 있습니다.

LoadGameBP_3.png

반응형
반응형


이 둘의 차이점은 실행하다 성공이냐 아니면 실행하다 실패냐에 따라 트리를 빠져나올지 결정한다


selAndSeq.png


https://docs.unrealengine.com/latest/KOR/Engine/AI/BehaviorTrees/QuickStart/10/index.html

  1. 비헤이비어 트리 애셋이 이미 열려있지 않다면, 콘텐츠 브라우저 에서 더블클릭 하여 엽니다.

  2. 비헤이비어 트리 모드에서, 그래프 영역에 우클릭 한 다음 Composites -> Selector 노드를 두 개 추가합니다.

    selector.png

  3. 비헤이비어 트리 모드에서, 그래프 영역에 우클릭 한 다음 Composites -> Sequence 노드를 추가합니다.

    sequence.png

이 둘은 비헤이비어 트리에서 사용할 수 있는 두 가지 주요 의사결정 노드입니다. Selector 노드는 자손을 왼쪽에서 오른쪽 순으로 실행하다, 그 중 하나가 성공 하면 트리를 빠져나옵니다. 반면 Sequence 노드는 자손을 왼쪽에서 오른쪽 순으로 실행하다, 그 중 하나가 실패 하면 빠져나옵니다.



반응형
반응형

주의 점이라고 하긴 했지만 하면 안된다 그런게 아니고 네이밍만 보고선 어? 라는 부분이 생기게 될 수 있는데

[Size to Fit 에 체크를 해놨을 경우]

이 함수는 Add 처럼 Element 를 하나씩 뒤에서 추가하는 것이 아니고

해당 추가할 위치의 인덱스를 지정해주면 0번지 부터 해당 인덱스 번지까지 순차적으로 쭉 생성 한다는 것이 주의할 점이다


빈 TArray 에서 인덱스를 3으로 주면

0~3까지 4개의 공간이 생기게 된다





Set Array Elem



  • SHARE:

Target is Kismet Array Library

Set Array Elem
Target Array
Index
0
Item
Size to Fit

Inputs

In
Exec
Target Array
Array of Wildcards

The array to perform the operation on *

Index
Integer

The index to assign the item to *

Item
Wildcard (by ref)

The item to assign to the index of the array *

Size to Fit
Boolean

If true, the array will expand if Index is greater than the current size of the array

Outputs

Out
Exec


반응형
반응형
int 를 FString 으로 변환하기

int32 YourInt = 9000;
FString IntAsString = FString::FromInt(YourInt);



https://forums.unrealengine.com/showthread.php?67066-how-to-convert-quot-int32-quot-to-quot-string-quot-in-ue4-c




I have a wiki on exactly this subject!

I cover

FString to uint8 / int32

FString to float

float to FString

int32 to FString

FString to std::string

std:string to FString


~~~

Wiki Link

Rama's Wiki on String Conversions
https://wiki.unrealengine.com/String...oat_to_FString

~~~

C++ Code For You

This is the code you want:

Code:
int32 YourInt = 9000;
FString IntAsString = FString::FromInt(YourInt);

Enjoy!

Rama





https://wiki.unrealengine.com/String_Conversions:_FString_to_FName,_FString_to_Int32,_Float_to_FString


Overview

  1. FString to FName
  2. std::string to FString
  3. FString and FCString Overview
  4. FString to Integer
  5. FString to Float
  6. Float/Integer to FString
  7. UE4 C++ Source Header References

All the header files I refer to in this tutorial are found in

your UE4 install directory  / Engine / Source

you will probably want to do a search for them from this point :)

Converting FString to FNames

Say we have

FString TheString = "UE4_C++_IS_Awesome";

To convert this to an FName you do:

FName ConvertedFString = FName(*TheString);

std::string to FString

#include <string>
 
//....
 
some function
{
  std::string TestString = "Happy"; 
  FString HappyString(TestString.c_str());
}

FString to std::string

#include <string>
 
//....
FString UE4Str = "Flowers";
std::string MyStdString(TCHAR_TO_UTF8(*UE4Str));

FCString Overview

Converting FString to Numbers

The * operator on FStrings returns their TCHAR* data which is what FCString functions use.

If you cant find the function you want in FStrings (UnrealString.h) then you should check out the FCString functions (CString.h)

I show how to convert from FString to FCString below:

Say we have

FString TheString = "123.021";

FString to Integer

int32 MyShinyNewInt = FCString::Atoi(*TheString);

FString to Float

float MyShinyNewFloat = FCString::Atof(*TheString);


Note that Atoi and Atof are static functions, so you use the syntax FCString::TheFunction to call it :)


Float/Integer to FString

FString NewString = FString::FromInt(YourInt);
 
FString VeryCleanString = FString::SanitizeFloat(YourFloat);

Static functions in the UnrealString.h :)

UE4 Source Header References

CString.h
UnrealString.h
NameTypes.h

See CString.h for more details and other functions like

atoi64 (string to int64)
Atod	(string to double precision float)


For a great deal of helpful functions you will also want to look at

UnrealString.h for direct manipulation of FStrings!


For more info on FNames check out

 NameTypes.h

Enjoy!


반응형
반응형

FString names = FString::Printf(TEXT("UserName [%s]  UserNickName [%s]"), *LocalAccountName, *LocalAccountNickName);


UE_LOG(gamelog, Log, TEXT("%s"), *names);


GEngine->AddOnScreenDebugMessage(-1, 50000.f, FColor::Red, *names);

반응형
반응형

typedef TWeakPtr<Combine_Base> wpCombineBase;




TSharedPtr<Combine_EditorLogin> spCombineEditorLogin = 

StaticCastSharedPtr<Combine_EditorLogin>(retTop);

//스태틱 캐스팅과 콘스트 캐스팅은 지원하는데 Dynamic cast 는 없다



TSharedPtr<Combine_EditorGameSvrAuth> spCombineEditorGameSvrAuth = 


StaticCastSharedPtr<Combine_EditorGameSvrAuth>(retTop);




//TweakPtr 이 존재하는데 C++ 에서는 Lock() 함수지만 Unreal 에서는 Pin()

//이라는 이름으로 shared_ptr 로 convert 하는 함수를 지원한다


auto pred = [](wpCombineBase elem1)->bool {

return elem1.Pin()->GetTypeID() == "EditorLogin";

};


반응형
반응형

I hope I understood your question correctly, but it sounds like you're wondering how to create the .generated.hheader files.

The .generated.h header files are generated automatically for you by Unreal Header Tool (UHT). They will be generated whenever you declare a USTRUCT() or UCLASS() type in your header files. The name of the generated header will be the name of your header file, plus ".generated" appended. For example, if your header is called MyHeader.h and declares one or more UCLASS or USTRUCT types, the file MyHeader.generated.h will be generated for you.

The contents of the .generated.h file depend on the details of your type declarations. They generally contain glue code for run-time reflection, serialization, default constructors and initialization code.



https://answers.unrealengine.com/questions/298930/how-to-create-only-header-file-while-exist-inside.html

반응형
반응형

액터의 생명 주기

https://docs.unrealengine.com/latest/KOR/Programming/UnrealArchitecture/Actors/ActorLifecycle/index.html

  • SHARE:

언리얼 엔진

이 문서는 액터의 생명 주기에 대한 하이 레벨 개요서로, 액터가 레벨에 어떻게 인스턴스가 생성 (Spawn) 되고 소멸 (Destroy) 되는지에 대해서입니다.

아래 흐름도는 액터 인스턴스가 생성되는 세 가지 주요 경로에 대해 보여줍니다. 액터 생성이 어떻게 되었든지 관계없이, 소멸까지 모두 같은 경로를 따릅니다.

생명 주기 분해도


디스크에서 로드

이 경로는 이미 레벨에 있는 액터에 대해서, LoadMap 이 발생했을 때나 (스트리밍 또는 서브 레벨에서) AddToWorld 가 호출되었을 때 발생합니다.

  1. 패키지/레벨에 있는 액터가 디스크에서 로드됩니다.

  2. PostLoad 로드 이후 - 디스크에서의 로드 완료 후 serialize 된 액터에 의해 호출됩니다. 커스텀 버전이나 픽스 작업은 이 곳에 와야 합니다. PostLoad 는 PostActorCreated 와 상호 배제됩니다.

  3. InitializeActorsForPlay 플레이용 액터 초기화

  4. RouteActorInitialize 액터 초기화 경로변경 - 초기화되지 않은 액터에 대해 호출됩니다 (심리스 트래블 전환 처리)

    1. PreInitializeComponents 컴포넌트 초기화 이전 - 액터의 컴포넌트에 InitializeComponent 를 호출하기 전 호출합니다.

    2. InitializeComponent 컴포넌트 초기화 - 액터에 정의된 각 컴포넌트 생성용 헬퍼 함수입니다.

    3. PostInitializeComponents 컴포넌트 초기화 이후 - 액터의 컴포넌트 초기화 완료 후 호출됩니다.

  5. BeginPlay 플레이 시작 - 레벨이 시작되면 호출됩니다.

에디터에서 플레이

에디터에서 플레이 경로는 디스크에서 로드와 거의 똑같지만, 액터를 디스크에서 로드하기 보다는 에디터에서 복사해 옵니다.

  1. 에디터에 있는 액터를 새 월드로 복제합니다.

  2. PostDuplicate (복제 이후)를 호출합니다.

  3. InitializeActorsForPlay (플레이용 액터 초기화)

  4. 초기화되지 않은 액터는 RouteActorInitialize (액터 초기화 전달), (심리스 트래블 이전 처리)

    1. PreInitializeComponents (컴포넌트 초기화 이전) - 액터의 컴포넌트에서 InitializeComponent (컴포넌트 초기화)가 호출되기 이전에 호출됩니다.

    2. InitializeComponent 컴포넌트 초기화 - 액터에 정의된 컴포넌트 각각에 대한 생성을 돕는 함수입니다.

    3. PostInitializeComponents 컴포넌트 초기화 이후 - 액터의 컴포넌트 초기화가 완료된 이후 호출됩니다.

  5. BeginPlay 플레이 시작 - 레벨이 시작되면 호출됩니다.

스폰

액터를 스폰(인스턴싱)할 때 따르게 되는 경로입니다.

  1. SpawnActor (액터 스폰)이 호출됩니다.

  2. PostSpawnInitialize (스폰 초기화 이후)가 호출됩니다.

  3. PostActorCreated 액터 생성 이후 - 스폰된 액터의 생성 이후에 호출됩니다. 생성자같은 것이 여기에 와야 합니다.PostActorCreated (액터 생성 이후)는 PostLoad (로드 이후)와 상호 배제적입니다.

  4. ExecuteConstruction (생성 실행):

    • OnConstruction 생성시 - 액터의 생성 지점으로, 블루프린트 액터가 컴포넌트를 만들고 블루프린트 변수를 초기화시키는 곳입니다.

  5. PostActorConstruction (액터 생성 이후):

    1. PreInitializeComponents 컴포넌트 초기화 이전 - 액터의 컴포넌트에서 InitializeComponent (컴포넌트 초기화)가 호출되기 전에 호출됩니다.

    2. InitializeComponent (컴포넌트 초기화) - 액터에 정의된 컴포넌트 각각의 생성을 돕는 함수입니다.

    3. PostInitializeComponents 컴포넌트 초기화 이후 - 액터의 컴포넌트 초기화가 완료된 이후 호출됩니다.

  6. OnActorSpawned (액터가 스폰되었을 때)가 UWorld 에 방송됩니다.

  7. BeginPlay (플레이 시작)이 호출됩니다.

디퍼드 스폰

"Expose on Spawn" (스폰시 노출) 설정된 프로퍼티가 있으면 액터는 디퍼드(유예시켜 나중에 한 번에 몰아서) 스폰이 가능합니다.

  1. SpawnActorDeferred 액터 디퍼드 스폰 - 순차적 액터 스폰을 뜻하는 것으로, 블루프린트 컨스트럭션 스크립트 이전 추가적인 구성이 가능합니다.

  2. SpawnActor (액터 스폰) 안의 모든 것이 일어나지만, PostActorCreated (액터 생성 이후) 뒤에는 다음과 같은 일이 일어납니다:

    1. 유효하지만 완료되지는 않은 액터 인스턴스로 다양한 "초기화 함수" 구성 / 호출을 합니다.

    2. FinishSpawningActor 액터 스폰 마무리 - 액터를 마무리시키기 위해 호출되며, SpawnActor (액터 스폰) 줄의 ExecuteConstruction (생성 실행)을 집어듭니다.

수명의 막바지에 달해서

액터 소멸(destroy)은 여러가지 방식으로 가능하나, 존재의 종말은 항상 같습니다.

게임플레이 도중

이는 완벽히 옵션인데, 많은 액터가 플레이 도중에는 실제로 죽지 않을 것이기 때문입니다.

Destroy 소멸 - 액터를 제거해야겠다 싶지만 게임플레이가 계속 진행중일 때 게임에서 수동으로 호출됩니다. 액터는 킬 대기 상태로 마킹되며, 레벨의 액터 배열에서 제거됩니다.

EndPlay 플레이 종료 - 액터의 수명이 막바지에 다다랐음을 보증하기 위해 여러 곳에서 호출됩니다. 플레이 도중에는 Destroy (소멸)이나, Level Transition (레벨 전환)이나, 액터가 들어있는 스트리밍 레벨이 언로드되면 이 함수가 발동됩니다. EndPlay 가 호출되는 경우는 다음과 같습니다:

  • Destroy 명시적 호출

  • 에디터에서 플레이 종료

  • Level Transition (심리스 트래블 또는 맵 로드)

  • 액터가 들어있는 스트리밍 레벨 언로드

  • 액터의 수명 만료

  • 어플리케이션 종료 (모든 액터 소멸)

어떻게 발생했는지와는 상관없이, 액터는 RF_PendingKill 마킹되어 다음 가비지 콜렉션 주기 동안 deallocate (할당 해제)됩니다. 또한 pending kill (킬 대기상태)를 수동 검사하기 보다는, 보다 깔끔한 FWeakObjectPtr 사용을 추천합니다.

OnDestroy 소멸시 - Destory (소멸)에 대한 구식(legacy) 반응입니다. 여기 있는 것들은 Level Transition (레벨 전환)이나 기타 게임 정리용 함수에 호출되는 EndPlay (플레이 종료)에 옮기는 것이 좋을 것입니다.

가비지 콜렉션

오브젝트가 소멸 마킹된 이후에는, 가비지 콜렉션이 일어날 때 메모리에서 실제로 제거되어 사용중이던 리소스가 해제됩니다.

오브젝트의 소멸 도중 호출되는 함수는 다음과 같습니다:

  1. BeginDestroy 소멸 시작 - 여기서 오브젝트의 메모리를 해제하고 기타 (그래픽 스레드 프록시 오브젝트 등의) 멀티스레드 리소스 처리를 할 수 있습니다. 소멸 예정 관련 대부분의 게임플레이 함수성은 더욱 일찍 EndPlay 에서 처리되었을 것입니다.

  2. IsReadyForFinishDestroy 소멸 마무리 준비 여부 - 가비지 콜렉션 프로세스는 이 함수를 호출하여 오브젝트 할당을 영구히 해제할 준비가 되었는지 여부를 결정합니다. false 를 반환하면, 이 함수는 다음 가비지 콜렉션 패스까지 실제 오브젝트 소멸 작업을 유예시킵니다.

  3. FinishDestroy 소멸 마무리 - 오브젝트가 곧 소멸되므로, 내부 데이터 구조체를 해제시킬 마지막 기회입니다. 메모리 해제 이전 마지막 호출입니다.

고급 가비지 콜렉션

언리얼 엔진 4 의 가비지 콜렉션 프로세스에서는 모두 같이 소멸되는 오브젝트 클러스터를 만듭니다. 클러스터를 만들면 오브젝트를 개별적으로 삭제할 때에 비할 때 가비지 콜렉션 관련 전체적인 메모리 교란(churn) 정도나 총 시간이 감소됩니다. 오브젝트가 로드되면서 서브오브젝트가 생성될 수 있습니다. 오브젝트와 그 서브오브젝트를 가비지 콜렉터의 단일 클러스터로 합쳐 넣는 경우, 엔진은 전체 오브젝트가 해제 준비될 때까지 클러스터에 사용되는 리소스의 해제를 지연시켰다가, 모든 리소스를 한 번에 해제시킬 수 있습니다.

가비지 콜렉션은 대부분의 프로젝트의 경우 환경설정 또는 변경할 필요가 전혀 없으나, 가비지 콜렉터의 클러스터 작동방식을 바꿔서 효율을 향상시킬 수 있는 경우가 있을 수 있는데, 그 방법은 다음과 같습니다:

  1. Clustering 클러스터링 - 이 옵션은 끕니다. 프로젝트 세팅 에서 Garbage Collection (가비지 콜렉션) 섹션 아래 Create Garbage Collector UObject Clusters (가비지 콜렉터 오브젝트 클러스터 생성) 옵션을 false 로 설정하면 됩니다. 대부분의 프로젝트의 경우 이 옵션을 끄면 가비지 콜렉션 효율이 떨어지므로, 퍼포먼스 테스트를 통해 득이 되는 것이 확실한 경우에만 끄는 것이 좋습니다.

  2. Cluster Merging 클러스터 병합 - 클러스터링이 true 로 설정된 경우, (프로젝트 세팅 에서 Garbage Collection (가비지 콜렉션) 섹션 아래) Merge GC Clusters (GC 클러스터 병합) 옵션을 true 로 설정하면 클러스터 병합 기능을 켤 수 있습니다. 이 옵션은 기본적으로 꺼져있으며, 모든 프로젝트에 적합하지도 않습니다. 오브젝트의 클러스터를 만드는 프로세스 도중, 오브젝트를 검사하여 그 안에 다른 오브젝트로의 레퍼런스를 찾을 수 있습니다. 클러스터 병합이 없으면 (기본 동작) 그 레페런스가 기록은 되나, 로드되는 오브젝트와 그 서브오브젝트는 원래 클러스터에 남아있게 됩니다. 클러스터 병합이 있으면, 로드되는 오브젝트의 클러스터와 레퍼런스 대상 오브젝트가 합쳐집니다. 예를 들어 파티클 시스템 애셋이 머티리얼 애셋을 레퍼런스하나 클러스터 병합 옵션은 꺼진 경우, 머티리얼과 파티클 시스템은 가비지 콜렉션용 별도의 클러스터에 남아있습니다. 클러스터 병합 옵션이 켜진 경우, 파티클 시스템이 머티리얼을 레퍼런스하고 있기 때문에 파티클 애셋 클러스터가 머티리얼 클러스터와 병합됩니다. 이러한 방식은 오픈 월드 게임처럼 콘텐츠 스트리밍이 일어나는 경우엔 바람직하지 않을 수 있는데, 다수의 클러스터가 병합되면서 보다 거대하고 다양한 오브젝트 그룹을 형성하기 때문입니다. 클러스터의 오브젝트들은 개별적으로 소멸되지 않고, 그 그룹의 모든 오브젝트가 소멸 설정될 때까지 기다리게 됩니다. 그렇게 되면 그 오브젝트 중 소수만 사용되어도 거대한 오브젝트 클러스터가 메모리에 남아있게 되는 경우가 생길 수 있습니다. 하지만 클러스터 병합 기능을 켜고, 코드에서 다른 오브젝트에 공유되지 않는 레퍼런스된 오브젝트가 다수 있는 애셋과 같은 특별한 경우를 수동으로 추가해 주면, 서브오브젝트들을 한꺼번에 청소하고, 가비지 콜렉터가 게임 도중 유지하면서 검사해야 하는 개별 항목 및 서브오브젝트 수가 줄어들기에 퍼포먼스가 향상될 수 있습니다. AdvancedGC.png


반응형

+ Recent posts