'언리얼 엔진 > 정보' 카테고리의 다른 글
애니메이션 몽타주 재생 관련 (0) | 2022.09.13 |
---|---|
UMG와 관련된 내용들 (0) | 2022.09.08 |
가비지 컬렉션 (0) | 2022.09.06 |
안까먹으려고 작성해두는 언리얼 기능들 (0) | 2022.09.05 |
언리얼 참고 문서 (0) | 2022.09.01 |
애니메이션 몽타주 재생 관련 (0) | 2022.09.13 |
---|---|
UMG와 관련된 내용들 (0) | 2022.09.08 |
가비지 컬렉션 (0) | 2022.09.06 |
안까먹으려고 작성해두는 언리얼 기능들 (0) | 2022.09.05 |
언리얼 참고 문서 (0) | 2022.09.01 |
[39. The Animation Blueprint #1]
블렌드 스페이스: 애니메이션을 섞는다.
1D는 축이 한개이다.
좌측부터 Idle, Walk, Running을 배치하고 가로 축 이름은 Speed, 최대값은 375로 설정한다.
애니메이션 그래프에서는 매 틱마다 무언가를 할 수 있다.
AnimInstance를 상속하는 MainAnimInstance C++ 클래스를 만들어준다.
참고로 애니메이션 인스턴스는 틱 함수가 없지만 비슷한 것은 있다.
// MainAnimInstance.h
class FIRSTPROJECT_API UMainAnimInstance : public UAnimInstance
{
public:
virtual void NativeInitializeAnimation() override;
UFUNCTION(BlueprintCallable, Category = AnimationProperties)
void UpdateAnimationProperties();
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Movement)
float MovementSpeed;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Movement)
bool bIsInAir;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Movement)
class APawn* Pawn;
};
// MainAnimInstance.cpp
#include "GameFramework/CharacterMovementComponent.h"
void UMainAnimInstance::NativeInitializeAnimation()
{
if (Pawn == nullptr)
{
// 이 애니메이션 인스턴스의 소유자를 가져옴
Pawn = TryGetPawnOwner();
}
}
void UMainAnimInstance::UpdateAnimationProperties()
{
if (Pawn == nullptr)
{
Pawn = TryGetPawnOwner();
}
if (Pawn)
{
FVector Speed = Pawn->GetVelocity();
FVector LateralSpeed = FVector(Speed.X, Speed.Y, 0.f);
MovementSpeed = LateralSpeed.Size();
bIsInAir = Pawn->GetMovementComponent()->IsFalling();
}
}
우선은 코드를 작성하고 끝내도록 한다. 아직 바뀌는건 아무것도 없다.
NativeInitializeAnimation은 AnimInstance에 선언 및 정의가 되어있기는 한데 아무것도 구현되어있지 않다.
필요하면 재정의 해서 쓰라고 만들어 둔 듯 하다.
[40. The Animation Blueprint #2]
MainAnimInstance기반의 애니메이션 블루프린트 클래스를 하나 만들어 준다. 이름은 MainAnim_BP이다.
이번에는 따로 코드를 작성하지 않고 블루프린트로만 애니메이션이 작동하게 만든다.
먼저, 이벤트그래프에서 만들어둔 UpdateAnimationProperties 함수를 이벤트에 연결시킨다.
해당 이벤트는 매 틱 실행되어 캐릭터의 속도의 크기를 MovementSpeed에 대입한다.
AnimGraph에서 스테이트 머신을 만들고 안으로 들어간다.
Idle/Walk/Run이라는 스테이트를 만들어서 엔트리와 연결시켜준다.
다시 Idle/Walk/Run 안으로 들어간다.
애셋 브라우저에서 기존에 만들었던 블렌드 스페이스 1D를 가져와서 출력 애니메이션 포즈에 연결시키고 MovementSpeed를 매개변수로 넘겨준다.
UpdateAnimationProperties 함수가 매 틱 실행되어 캐릭터의 속도에 따라 MovementSpeed 값이 변하기 때문에 블렌드 스페이스에서 설정한 값(0~375)에 따라 애니메이션 블렌드가 적절하게 이뤄진다.
이제 실행 후 캐릭터를 이리저리 움직여보면 어느정도 의도한 대로 애니메이션이 재생된다.
[41. The Animation Blueprint #3]
점프 애니메이션을 추가할 때이다.
스테이트 머신을 위와 같이 설정한다.
JumpStart에는 Jumping_Up, InAir에는 Falling_Idle, JumpEnd에는 Jumping_Down 애니메이션을 연결시켜준다.
이제 현재 상태나 애니메이션 재생 비율에 따라 장면 전환을 정하면 된다.
어느정도 의도한 대로 애니메이션 전환이 잘 이뤄진다.
원하는 동작이 복잡해질수록 전환 규칙을 많이 정의해야 한다.
[UE C++] Gameplay Mechanics #2 (0) | 2022.09.07 |
---|---|
[UE C++] Gameplay Mechanics #1 (0) | 2022.09.06 |
[UE C++] The Character Class #1 (0) | 2022.09.05 |
[UE C++] The Pawn Class #2 (0) | 2022.09.05 |
[UE C++] The Pawn Class #1 (0) | 2022.09.04 |
[33-34. Character Assets]
https://www.mixamo.com/#/?page=1&query=castle&type=Character
위의 링크에서 Castle Guard 01을 다운로드 받고 프로젝트에 모두 임포트 한다.
필요한 애니메이션도 모두 받아서 Import 하는데 스켈레탈 메시는 캐슬 가드를 골라주도록 한다.
[35. The Character Class #1]
Character를 상속받은 C++ 클래스를 Main 이라는 이름으로 생성한다.
ACharacter를 상속받게 되는데, 이 부모 클래스에 USkeletalMeshComponent* 타입의 Mesh라는 변수가 이미 선언되어있다.
그 외에 CharacterMovement, CapsuleComponent도 같이 선언되어 있고 CapsuleComponent가 루트 컴포넌트가 된다.
루트 컴포넌트가 될 수 있는것은 CapsuleComponent 뿐이다.
private:
/** The main skeletal mesh associated with this Character (optional sub-object). */
UPROPERTY(Category=Character, VisibleAnywhere, BlueprintReadOnly, meta=(AllowPrivateAccess = "true"))
USkeletalMeshComponent* Mesh;
UPROPERTY 속성의 meta=(Allow..) 키워드는 private임에도 에디터에서 보이게 한다.
블루프린트 내부에서 접근할 수 있지만 외부에서는 접근할 수 없다.
// Main.h
public:
/* Camera boom positioning the camera behind the player */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess=true))
class USpringArmComponent* CameraBoom;
/* Follow Camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = true))
class UCameraComponent* FollowCamera;
// Main.cpp
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
AMain::AMain()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// Create Camera Boom (pulls towards the player if there's a collision)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(GetRootComponent());
CameraBoom->TargetArmLength = 600.f; // Camera follows at this distance
CameraBoom->bUsePawnControlRotation = true; // Rotate arm based on controller
// Create Follow Camera
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
// Attach the camera to the end of the boom and let the boom adjust to match
// the controller orientation
FollowCamera->bUsePawnControlRotation = false;
}
bUsePawnControlRotation는 컨트롤러의 회전을 따라갈 것인가를 정하는 것이다.
스프링 암은 컨트롤러를 따라가는 것이 좋고, 카메라는 스프링암을 따라가야 하기 때문에 각각 true/false로 둔다.
빌드를 하고 블루프린트 클래스로 만들어둔다.
[36. The Character Class #2]
스켈레탈 메시를 우리가 받은것으로 설정하고 캡슐 컴포넌트의 크기도 캐릭터와 맞게 대략적으로 맞춰준다.
메시의 y축과 화살표의 방향도 맞춰준다.
조작을 구현할 차례이다.
이전의 폰에서 마우스 움직임으로 회전을 구현했을 때는 마우스 이동시 캐릭터가 회전하기 때문에 하위 계층의 스프링 암과 카메라가 움직이는 방식이었다면 이번에는 카메라만 회전시키는 방식으로 구현해 본다.
부모 클래스인 ACharacter에 포함된 CharacterMovementComponent에 접근하여 사용한다.
사용하기 전에 nullptr 여부를 검사하고 사용하는 것이 좋다.
// Main.h
public:
/* Base turn rates to scale turning functions for the camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
float BaseTurnRate;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
float BaseLookUpRate;
// Main.cpp
AMain::AMain()
{
// Set our turn rates for input
BaseTurnRate = 65.f;
BaseLookUpRate = 65.f;
}
/* Called for forwards/backwards input */
void AMain::MoveForward(float Value)
{
if ((Controller != nullptr) && (Value != 0.0f))
{
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0.f, Rotation.Yaw, 0.f);
// FRotationMatrix: FRotator를 회전행렬로 변환한다.
// GetUnitAxis: 특정 축을 정규화 한것을 반환한다.
const FVector Direction = FRotationMatrix(OUT YawRotation).GetUnitAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
}
/* Called for side to side input */
void AMain::MoveRight(float Value)
{
if ((Controller != nullptr) && (Value != 0.0f))
{
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0.f, Rotation.Yaw, 0.f);
const FVector Direction = FRotationMatrix(OUT YawRotation).GetUnitAxis(EAxis::Y);
AddMovementInput(Direction, Value);
}
}
FRotationMatrix로 Rotator를 회전 행렬로 변환하고 거기서 X축에 대한 정규 벡터를 반환받고 이것이 방향 벡터가 된다.
유의해야 하는 점은 Direction은 메시의 방향벡터가 아닌 컨트롤러(마우스)의 방향벡터이다.
차후 마우스 움직임과 메시의 회전을 분리시키고 이동 자체는 마우스의 방향을 따라가도록 하기 위함이다.
[37. The Character Class #3]
키보드 조작과 마우스 조작 둘 다 구현해본다.
// Main.h
/* Called via input to turn at a given rate
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
*/
void TurnAtRate(float Rate);
/* Called via input to look up/down at a given rate
* @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired look up/down rate
*/
void LookUpAtRate(float Rate);
// Main.cpp
void AMain::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
check(PlayerInputComponent);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
PlayerInputComponent->BindAxis("MoveForward", this, &AMain::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AMain::MoveRight);
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis("TurnRate", this, &AMain::TurnAtRate);
PlayerInputComponent->BindAxis("LookUpRate", this, &AMain::LookUpAtRate);
}
void AMain::TurnAtRate(float Rate)
{
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}
void AMain::LookUpAtRate(float Rate)
{
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}
전체 코드. 여기서 눈여겨 볼 부분이 있다.
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis("TurnRate", this, &AMain::TurnAtRate);
PlayerInputComponent->BindAxis("LookUpRate", this, &AMain::LookUpAtRate);
위의 두개는 Pawn의 AddControllerYawInput을 바인딩 하고 아래 두개는 따로 만든것을 바인딩한다.
하지만 따로 만든 함수의 내용도 보면 똑같이 AddControllerYawInput을 호출한다.
차이점이 있다면 위의 두개는 마우스 이동을 위해서 상위 클래스의 함수를 바로 바인딩 한거고 아래 두개는 키보드로 회전을 하려면 별도의 계수가 필요하므로 계산식을 추가한 것이다.
BaseTurnRate, BaseLookUpRate가 클수록 회전 속도는 빠르게 될 것이다. 부드러운 회전을 위해 델타타임을 곱해주면 부드럽게 회전이 된다.
프로젝트 세팅의 바인딩도 수정해 주어야 한다.
추가로 BindAxis는 매 틱마다 함수를 호출하는데 비해 BindAction은 이벤트가 발생할 때만 호출되기 때문에 조금 더 최적화가 되어있다.
Pawn의 컨트롤을 구현할 때와는 달리 별다른 코딩을 하지 않고 상위 클래스의 컨트롤러를 상속받아 사용했다.
만약 추가적인 구현이 필요하다면 오버라이딩이 가능한지 확인 후 오버라이딩 해서 사용하면 될 것이다.
아직은 카메라만 따로 회전하는게 아니라 캐릭터가 회전하고 있지만 곧 수정하게 될 것이다.
[38. The Character Class #4]
가끔 C++ 코드를 변경했음에도 기반으로 하는 블루프린트 클래스가 변화하지 않는다면 에디터를 껐다가 켜보고 그럼에도 안되면 블루프린트 클래스를 삭제했다가 재생성하는 방법을 이용해야 한다.
다만 블루프린트 클래스의 값이 변하지 않았다고 무작정 삭제하기 전에 값 되돌리기 버튼을 눌러서 하드코딩한 값이 적용되는지 확인하고 진행해야 한다.
이제 카메라의 회전과 캐릭터의 회전을 분리시켜보도록 한다. 매우 간단하다.
// Main.cpp
AMain::AMain()
{
// Don't rotate when the controller rotates.
// Let that just affect the camera.
bUseControllerRotationYaw = false;
bUseControllerRotationPitch = false;
bUseControllerRotationRoll = false;
}
특정 축만 false로 만들수도 있지만 지금은 모든 축에 대해 컨트롤러 조작시 캐릭터가 회전하지 않도록 한다.
실행 시 카메라(정확하게는 스프링 암)의 회전과 캐릭터의 회전이 분리가 되었고 W키를 입력시 카메라가 바라보는 방향으로 이동한다.
상속받은 Character 클래스에 포함된 CharacterMovementComponent는 굉장히 많은 변수와 기능이 포함되어있다.
AMain::AMain()
{
// Configure character movement
GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
GetCharacterMovement()->RotationRate = FRotator(0.f, 540.f, 0.f); // ...at this rotation rate
GetCharacterMovement()->JumpZVelocity = 650.f;
GetCharacterMovement()->AirControl = 0.2f;
}
bOrientRotationToMovement는 true시 RotationRate를 회전 변경 속도로 사용하여 캐릭터를 가속 방향으로 회전시킨다.
W키를 누른 상태에서 카메라를 돌리거나 ASD키로 움직일 시 캐릭터가 회전하게 된다.
즉, 회전 보간이 적용된다.
RotationRate는 회전의 속도라고 보면 된다.
JumpZVelocity는 점프의 가속도, AirControl은 캐릭터의 공중 조작의 자유도라고 보면 된다.
AirControl의 수치가 클수록 방향전환이 잘되고 작을수록 안된다.
[UE C++] Gameplay Mechanics #1 (0) | 2022.09.06 |
---|---|
[UE C++] The Character Class #2 (0) | 2022.09.06 |
[UE C++] The Pawn Class #2 (0) | 2022.09.05 |
[UE C++] The Pawn Class #1 (0) | 2022.09.04 |
[UE C++] The Actor Class #2 (0) | 2022.09.04 |
[29. Pawn Movement Component #1]
일반적으로 Movement Component를 만들지 않고 자동으로 제작된 컴포넌트를 이용한다. (라고 하는데 확실치 않음)
우선 PawnMovementComponent를 상속받는 C++ 클래스를 하나 만든다. (ColliderMovementComponent)
// Collider.h
UPROPERTY(VisibleAnywhere, Category = "Movement")
class UColliderMovementComponent* OurMovementComponent;
// Collider.cpp
#include "ColliderMovementComponent.h"
ACollider::ACollider()
{
// .. 생략
OurMovementComponent = CreateDefaultSubobject<UColliderMovementComponent>(TEXT("OurMovementComponent"));
OurMovementComponent->UpdatedComponent = RootComponent; // RootComponent를 제어함
}
그리고 Collider에서 해당 컴포넌트를 사용하기 위해 변수로 선언한다.
UpdatedComponent에 RootComponent를 대입하면 OurMovementComponent가 RootComponent를 제어한다는 의미이다.
AddMovementInput 함수의 정의를 보면 GetMovementComponent 함수를 받아서 사용하는데, 폰은 기본적으로 MovementComponent가 없어서 null을 반환받게된다. (찾아보니 그렇다는것 같음)
캐릭터와 기본 폰은 받아서 사용하지만 따로 만든 폰은 그렇지 않다.
우선은 이동을 구현하기 위해 MoveForward와 MoveRight에 작성해 두었던 AddInputMovement 함수를 삭제하고 다른 함수를 사용해보자.
// Collider.cpp
void ACollider::MoveForward(float Value)
{
FVector Forward = GetActorForwardVector();
if (OurMovementComponent)
{
OurMovementComponent->AddInputVector(Forward * Value);
}
}
void ACollider::MoveRight(float Value)
{
FVector Right = GetActorRightVector();
if (OurMovementComponent)
{
OurMovementComponent->AddInputVector(Right * Value);
}
}
그리고 우리가 만든 ColliderMovementComponent를 사용해야 하기 때문에 GetMovementComponent 메소드를 재정의 한다.
// Collider.h
virtual UPawnMovementComponent* GetMovementComponent() const override;
// Collider.cpp
UPawnMovementComponent* ACollider::GetMovementComponent() const
{
return OurMovementComponent;
}
아직까지도 별다른 변화는 없다.
이제 ColliderMovementComponent를 만들어 줄 때이다.
// ColliderMovementComponent.h
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
// ColliderMovementComponent.cpp
void UColliderMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
{
return;
}
FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.f);
if (!DesiredMovementThisFrame.IsNearlyZero())
{
FHitResult Hit;
SafeMoveUpdatedComponent(OUT DesiredMovementThisFrame, OUT UpdatedComponent->GetComponentRotation(), true, OUT Hit);
// if we bump into something, slide along the side of it
if (Hit.IsValidBlockingHit())
{
SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
}
}
}
폰이나 액터 클래스 생성시 기본으로 있던 Tick 함수를 똑같이 오버라이딩 한 것과 같다.
컴포넌트이기때문에 함수명이 TickComponent이다.
마찬가지로 Super의 TickComponent를 호출해주고 내용을 작성한다.
MovementComponent가 폰을 소유하지 않거나, MovementComponent가 제어할 컴포넌트가 없거나, 스킵해야 하는 경우 Tick을 즉시 종료한다.
DesiredMovementThisFrame에 대입하는 ConsumInputVector는 Collider에서 정의했던 MoveForward, MoveRight에 의해서 ControllInputVector라는 변수에 값이 계속 누적된다. (다만 입력이 없으면 0이 계속 곱해지기 때문에 0만 더해짐)
ConsumInputVector를 호출하면 누적된 값을 반환받고 ControllInputVector는 0으로 초기화된다.
그 뒤, 최대 크기가 1.f로 고정된 벡터의 복사본을 통해 값을 보정한다.
해당 값이 0에 근접하지 않다면 움직이게 된다.
마지막으로 만약 물체와 부딪혔다면, 해당 물체에 충돌한 상태로 가만히 있는게 아니라 조금씩 미끄러져서 움직이게 한다.
[30. Pawn Movement Component #2]
우선 마켓플레이스에서 Infinity Blade: Adversaries를 다운 받는다.
이제 Critter에 Static Mesh 대신 Skeletal Mesh를 사용할 것이기 때문에 StaticmeshComponent를 SkeletalMeshComponent로 변경해준다.
그리고 더이상 컨트롤하지 않을 것이기 때문에 AutoPossessPlayer도 주석처리를 해준다.
그럼에도 불구하고 실행 시 해당 폰으로 빙의가 된다면 블루프린트 클래스에서 해당 옵션이 설정되어 있는지 확인 후 비활성화 시켜준다.
메시는 SK_Greater_Spider, 애니메이션은 ExoGame_Greater_Spider_Idle로 설정한다.
크기는 너무 큰것 같으니 0.3으로 줄이고 월드에 몇개 배치한다.
아직 Critter는 콜리전 처리를 하지 않았기 때문에 따로 충돌이 일어나지는 않는다.
[31. Pawn Camera Rotation]
마우스 이동에 따라 회전을 구현하기 위해 우선 축 매핑을 해준다.
마우스 X, Y는 키 매핑과 다르게 따로 음수를 설정하지 않아도 값이 알아서 들어간다.
// Collider.h
private:
void YawCamera(float AxisValue);
void PitchCamera(float AxisValue);
FVector2D CameraInput;
// Coliider.cpp
ACollider::ACollider()
{
// .. 생략
CameraInput = FVector2D(0.f, 0.f);
}
void ACollider::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
FRotator NewRotation = GetActorRotation();
NewRotation.Yaw += CameraInput.X;
SetActorRotation(NewRotation);
}
void ACollider::YawCamera(float AxisValue)
{
CameraInput.X = AxisValue;
}
void ACollider::PitchCamera(float AxisValue)
{
CameraInput.Y = AxisValue;
}
실행 후 마우스를 좌우로 움직이면 잘 움직인다.
SetActorRotation을 통해 액터 자체를 회전시킨 것이기 때문에 SpringArm으로 붙어있던 카메라도 같이 회전되어서 좌우로 움직인 것이다.
이번에 위아래로 움직이는 것은 액터 대신 카메라만 움직이도록 한다.
void ACollider::Tick(float DeltaTime)
{
// .. 생략
FRotator NewSpringArmRotation = SpringArm->GetComponentRotation();
NewSpringArmRotation.Pitch += CameraInput.Y;
SpringArm->SetWorldRotation(NewSpringArmRotation);
}
이제 위아래도 잘 움직인다.
다만 문제점은 스프링 암은 오브젝트에 충돌시 길이가 짧아지고 다시 복구되기 때문에 아래를 보거나 위를 보면 스프링 암의 위치가 틀어져서 조작이 엉망이 된다.
NewSpringArmRotation.Pitch = FMath::Clamp(NewSpringArmRotation.Pitch + CameraInput.Y, -80.f, -15.f);
Clamp를 통해 각의 범위를 보정해주면 문제를 해결할 수 있다.
[32. Environment Assets]
우선 Sun Temple이라는 예제 프로젝트를 다운로드 받는다.
영상에서는 Learn에 있지만 현재는 마켓플레이스에서 받으면 된다.
이 프로젝트의 리소스를 FirstProject에서 사용할 것이기 때문에 이주(Migrate) 시키도록 한다.
경로는 FirstProject의 Content 폴더이다.
현재까지 작업한 레벨을 저장하려면 [파일 - 다른 이름으로 레벨 저장] 을 눌러서 TestMaps로 저장해준다.
앞으로 SunTemple에서 작업할 것이므로 해당 레벨로 설정해준다.
[UE C++] The Character Class #2 (0) | 2022.09.06 |
---|---|
[UE C++] The Character Class #1 (0) | 2022.09.05 |
[UE C++] The Pawn Class #1 (0) | 2022.09.04 |
[UE C++] The Actor Class #2 (0) | 2022.09.04 |
[UE C++] The Actor Class #1 (0) | 2022.09.03 |
구글과 공식 API 문서를 가까이 하자.
[기타등등]
[Class]
TSubclassOf<>
UClass 유형의 안전성을 보장해주는 템플릿 클래스.
템플릿에 지정한 유형만 받을 수 있다.
https://docs.unrealengine.com/5.0/ko/typed-object-pointer-properties-in-unreal-engine/
[Property]
AutoPossessPlayer
레벨이 시작될 때나 폰이 생성될 때, 자동으로 폰을 소유해야 하는 플레이어 컨트롤러를 결정한다.
AutoPossessPlayer = EAutoReceiveInput::Player0;
[Method]
GetDeltaSeconds()
DeltaSeconds를 받아온다.
GetWorld()->GetDeltaSeconds()
// GetWorld()는 AActor에 오버라이딩 되어있다
// 만약 헤더파일을 포함해야 한다면 "Engine/World.h"
GetWorldTimerManager().SetTimer(타이머 핸들, 메소드, 간격, 반복 여부, 몇초간 실행할지)
일정 시간 뒤에 재생될 함수를 등록한다.
SpawnActor<>()
월드에 새로운 객체를 생성시킨다.
템플릿 타입에는 어떤 타입을 생성시킬지 알려주는것뿐이다.
그래서 해당 클래스의 생성자에서 메시를 생성시키지 않는 이상,
본질적으로는 보이지 않는 액터가 생성된다.
하지만 UClass 타입에 블루프린트 클래스를 넘겨준다면
블루프린트를 기반으로 새 객체를 만들어준다.
FMath::GetMappedRangeValueClamped(const Vector2D &InputRange,
const Vector2D &OutputRange, const float Value);
Value를 InputRange 범위에 대해서 OutputRange 백분율로 반환해준다.
/* 예시 */
Vector2D Input(0.f, 600.f);
Vector2D Output(0.f, 1.f);
float Result = FMath::GetMappedRangeValueClamped(Input, Output, 150.f);
// Result : 0.25f
[UPROPERTY]
Category
디테일 탭에서 그룹화를 시켜주고 이름을 지정한다
'|' 로 구분하면 오른쪽이 왼쪽의 하위 분류로 들어간다
Catagory="Tag#1 | Tag#2"
meta
AllowPrivateAccess = "true" : private이라도 접근이 가능하게 한다
ClampMin = "0.0", ClampMax = "1.0" : 값을 0~1 사이로 제한한다
UIMin = "0.0", UIMax = "1.0" : 에디터 패널에서 값이 표시되는 비율을 설정한다
[UFUNCTION]
BlueprintPure
const 함수와 같다. 주로 getter에 사용된다
BlueprintCallable
블루프린트 그래프에서 호출이 가능하지만 수정이 불가능하다
주로 setter에 사용
BlueprintImplementableEvent
C++에서 정의 불가. 블루프린트에서 오버라이딩 함
예) 캐릭터가 아이템을 획득 시, 어떤 효과가 발동되는가에 관한 이벤트를 다양하게 만들어줌
BlueprintNativeEvent
BlueprintImplementableEvent와 다르게 C++에서 함수 바디 구현 가능
블루프린트에서도 오버라이드 가능
구현부 함수명 뒤에 _Implementaion을 반드시 붙여주어야 한다
[Object]
CreateDefaultSubobject<T>(TEXT(""));
액터의 컴포넌트들을 생성한다.
실제 레벨에서 스폰 될 때, 매 액터 객체마다 컴포넌트를 설정하는 비용을 줄일 수 있다.
생성자에서 하는 것이 좋다.
MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
[Component]
SetupAttachment(부모 컴포넌트, 소켓 이름=None)
부모 컴포넌트의 자식 계층으로 설정한다.
소켓 이름을 지정한 경우 계층뿐만 아니라 소켓 위치로 원점이 정해진다.
MeshComponent->SetupAttachment(GetRootComponent());
AttachToComponent(부모 컴포넌트, 부착 규칙, 소켓 이름=None)
부모 컴포넌트의 자식 계층으로 설정한다.
소켓 이름을 지정한 경우 계층뿐만 아니라 소켓 위치로 원점이 정해진다.
생성자에서 사용하지 않는다.
[SpringArmComponent]
주로 카메라 컴포넌트의 부모로 설정한다.
장애물을 등지더라도 충돌처리가 이루어져서 카메라가 오브젝트를 뚫지 않는다.
bEnableCameraLag
true인 경우 카메라가 따라갈 때 딜레이를 준다.
SpringArm->bEnableCameraLag = true;
그 외의 속성과 메소드
https://docs.unrealengine.com/4.26/en-US/API/Runtime/Engine/GameFramework/USpringArmComponent/
애니메이션 몽타주 재생 관련 (0) | 2022.09.13 |
---|---|
UMG와 관련된 내용들 (0) | 2022.09.08 |
가비지 컬렉션 (0) | 2022.09.06 |
액터의 수명 주기 (0) | 2022.09.06 |
언리얼 참고 문서 (0) | 2022.09.01 |