SkeletalMeshComponent  USkeletalMesh 의 인스턴스를 만드는 데 사용됩니다. 스켈레탈 메시 (외형)에는 복잡한 스켈레톤 (서로 연결된 본)이 안에 있어, 스켈레탈 메시의 버텍스 각각을 현재 재생중인 애니메이션에 일치시키도록 도와줍니다. 그 덕에 SkeletalMeshComponent 는 캐릭터, 동물, 복잡한 기계, 복합적인 동작을 보이거나 변형이 필요한 것에 적합합니다.  - 언리얼 공식문서 -

 

즉 캐릭터, 동물 등 애니메이션이 있는 매쉬를 사용하기 위해 내부에 본이 들어간 매쉬를 위한 컴포넌트이다.


 

1. Skeletal Mesh

까마귀의 스켈레탈 매시

 

우측에 본들을 볼수 있다

 

 

이러한 작업 된 스캘레탈 메쉬는 마야, 블랜더 등에서 작업되어 넘어온다. (본을 삽입하고 움직이는 과정을 블랜더에서는 리깅이라고 한다)


2. Skeletal Mesh Component

위에서 확인한 스켈레탈 매쉬를 사용하기 위해 해당 컴포넌트를 생성하고 스캘레탈 매쉬를 할당해보자

Bird.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "Bird.generated.h"

UCLASS()
class TEST01_API ABird : public APawn
{
	GENERATED_BODY()

public:
	ABird();
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
	virtual void Tick(float DeltaTime) override;


protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

private:
	UPROPERTY(VisibleAnywhere)
	class UCapsuleComponent* Capsule;

	// 여기
	UPROPERTY(VisibleAnywhere)
	class USkeletalMeshComponent* BirdMesh;

};

Bird.cpp

 

// Fill out your copyright notice in the Description page of Project Settings.

#include "Components/CapsuleComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Bird.h"

ABird::ABird()
{
	PrimaryActorTick.bCanEverTick = true;

	//생성자 위치에서 Capsule의 변수를 설정해줌
	Capsule = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule"));
	Capsule->SetCapsuleHalfHeight(20.f);
	Capsule->SetCapsuleRadius(15.f);
	// 폰의 최상위 컴포넌트를 Capsule로 바꿔준다.
	SetRootComponent(Capsule);

	BirdMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Bird"));
	BirdMesh->SetupAttachment(GetRootComponent());


}

void ABird::BeginPlay()
{
	Super::BeginPlay();
	
}

void ABird::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

void ABird::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}

 

 

캡슐에 attech된 것을 볼 수 있다.

아직 뷰포트에는 까마귀의 매쉬가 보이지 않는다.

 

디테일 > 스케레탈 메시 에
스켈레탈 매쉬와 캡슐

좌측 하단에 보면 X, Y, Z가 보인다. X가 정면이므로 지금 까마귀는 측면을 보고 있는 것

까마귀를 회전시켜주자

 

까마귀의 머리가 x축을 바라보고 있다.

그리고 까마귀를 아래로 내려 캡술과 일치시키자

디테일 > 애니메이션

애니메이션을 추가해보자

 

블루 프린트를 드래그하여 화면으로 옮기면 까마귀와 캡슐을 볼 수 있다.

 

 

하늘을 나는 캡슐 까마귀 완성

캡슈컴포넌트는 충돌감지에 사용된다

 

폴리곤에 직접 충돌감지를 할 수 있지만 복잡한 폴리곤의 경우 너무 많은 리소스비용이든다. 

이를 방지하기위해 캡슐컴포넌트를 사용한다.

캡슐 컴포넌트와 충돌(Collision)

 


1. 새로운 pawn의 생성

 

 

 

# Bird.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "Components/CapsuleComponent.h"
#include "Bird.generated.h"

UCLASS()
class TEST01_API ABird : public APawn
{
	GENERATED_BODY()

public:
	ABird();
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
	virtual void Tick(float DeltaTime) override;


protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

private:
	// 캡슐의 변수 선언
	UPROPERTY(VisibleAnywhere)
	UCapsuleComponent* Capsule;

};

 

# Bird.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "Bird.h"

ABird::ABird()
{
	PrimaryActorTick.bCanEverTick = true;

	//생성자 위치에서 Capsule의 변수를 설정해줌
	Capsule = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule"));
        Capsule->SetCapsuleHalfHeight(20.f);
	Capsule->SetCapsuleRadius(15.f);
	// 폰의 최상위 컴포넌트를 Capsule로 바꿔준다.
	SetRootComponent(Capsule);


}

void ABird::BeginPlay()
{
	Super::BeginPlay();
	
}

void ABird::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

void ABird::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}

 

# 블루프린트

 

캡슐의 생성
우측의 shape에서 높이와 반경을 결정할 수 있다.

 

 


Include Header

위의 예제에서 우리는 UCapsuleComponent를 Bird.h에 include 했다.

이와 같은 방식은 문제가 있다. 

이와 같이 최종적으로 사용하는 파일의 .h 파일은 기하급수적으로 많은 헤더 파일을 include 하게 될 뿐만아니라.

 

각 헤더파일끼리 Cross 참조하게 되는 경우 에러가 발생 한다.

 

이를 해결하기위해 UCapsuleComonent가 Class임을 명시하고 cpp 파일에서 include를 받는것이 바람직하다.

 

 

변경된 Bird.h 파일

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
// UCapsuleComonent의 include가 사라졋다.
#include "Bird.generated.h"

UCLASS()
class TEST01_API ABird : public APawn
{
	GENERATED_BODY()

public:
	ABird();
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
	virtual void Tick(float DeltaTime) override;


protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

private:
	// 캡슐의 변수 선언 해당 변수가 class임을 명시한다.
	UPROPERTY(VisibleAnywhere)
	class UCapsuleComponent* Capsule;

};

 

Bird.cpp

// 컴포넌트 include
#include "Components/CapsuleComponent.h"
#include "Bird.h"

ABird::ABird()
{
	PrimaryActorTick.bCanEverTick = true;

	//생성자 위치에서 Capsule의 변수를 설정해줌
	Capsule = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule"));
	Capsule->SetCapsuleHalfHeight(20.f);
	Capsule->SetCapsuleRadius(15.f);
	// 폰의 최상위 컴포넌트를 Capsule로 바꿔준다.
	SetRootComponent(Capsule);


}
// 이하 생략

 

함수 Exposing

UPROPERTY처럼 함수도 블루프린트로 노출시킬 수 있다. 

Item.h

	UFUNCTION(BluePrintPure)
	float TransformdedSin(float value);

	UFUNCTION(BluePrintCallable)
	float TransformdedSin2(float value);

 

Item.cpp

float AItem::TransformdedSin(float value)
{
	return Amplitude * FMath::Sin(value * TimeConstant);;


}

float AItem::TransformdedSin2(float value)
{
	return Amplitude * FMath::Sin(value * TimeConstant);;
}

 

BluePrintPure를 옵션으로 넣어주면 실행 핀이 없는 함수가 만들어지며 Callable을 옵션으로 준다면 실행핀이 존재하느 함수가 만들어진다 sin함수의 계산은 실행핀이 필요 없기때문에 Pure옵션이 적절해 보인다.

 


탬플릿 함수

탬플릿 함수는 JAVA의 제너릭과 유사해보인다.

 

Item.h

template<typename T>
T Avg(T First, T Second);

 

T 타입을 반환 하고 T 타입의 변수 First, Second를 받는다.

Item.cpp

template<typename T>
T Avg(T First, T Second)
{
	return (First + Second) /2;
}

 

위와 같이 함수를 만들면 해당 함수는 int32 파라미터, float 파라미터, FVector 타입도 받아 처리할 수 있다.

단 FVector의 객체의 경우 내부에 + 연산과 / 연산이 별도로 오버라이드 되어 있다고 한다.

추가한 멤버변수의 변경을 위해 매번 컴파일 하는 작업은 비효율적이다. 많은 작업은 블루프린트나 언리얼의 뷰포트에서 진행 될 것이다. 또한 시나리오 작업하는 사람이 C++의 코드를 수정하는 방법을 모를 수도 있다. 이를 위해 추가한 변수를 블루프린트, 뷰포트에서 변경할수 있도록 변수를 노출 시킬 것이다.

 

Item.h

private:
	float RunningTime;

	UPROPERTY(EditDefaultsOnly)
	float Amplitude = 0.5f;
	
	UPROPERTY(EditInstanceOnly)
	float TimeConstant = 5.f;
};

 

EditDefaultOnly는 블루프린트에 표기가 되며 모든 인스턴스들이 공유하기 때문에 전부 같은 값을 적용받는다.

우측에 Amplitude 값이 보인다.

Aplitude가 0.3으로 변경된다면 모든 Item의 Actor들은 동일하게 0.3의 값을 가지게된다.

 

각각의 Actor들이 다른 값을 가지게 하고싶다면 EditInstanceOnly의 값을 사용한다.

 

우측에 TimeCantant를 확인 할수 있다.

 

위와같이 각각의 ACtor 매쉬마다 다른 값을 가질 수 있게 할 수 있다 이를 통해 두개의 Item 매쉬는 다른 속도로 Sin함수 그래프를 그린다.

 

float DeltaZ = Amplitude * FMath::Sin(RunningTime * TimeConstant);

 

 

 

 

  1. EditAnywhere: 블루브린트, 에디터, c++ 어디서든 수정이 가능하다.
  2. VisibleDefaultsOnly: 블루프린트 화면에 표기는 하지만 수정할 수 없다.
  3. VisibleInstanceOnly: 해당 값은 인스턴스에서 볼 수있지만 수정할 수없다.
  4. VisibleAnywhere: 블루프린트, 인스턴스에서 모두 확인 할 수있지만 수정 할 수 없다.

 

 


블루프린터에서의 사용

Item.h

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	UPROPERTY(EditAnyWhere, BlueprintReadWrite)
	float Amplitude = 0.5f;

BlueprintReadWrite와 같은 일부 옵션들은 private에서 동작할 수 없다. protected로 이동시킨다.

해당 옵션을 사용하게되면 Bluplrint에서 Node로 사용할 수 있다. (BluePrintReadOnly옵션도 있다.)

 

UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category="Sin Parameter")
float Amplitude = 0.5f;

Category옵션으로 여러 변수, 함수들을 묶어서 볼 수 있다.

+ Recent posts