컴포넌트 시스템 이해와 활용
액터 기능을 컴포넌트로 조합하는 원리를 이해하고 C++에서 생성·부착·초기화하는 방법을 익힙니다.
입력 처리(Input Mapping)에서 캐릭터가 입력에 반응하게 만들었지만, 아직 화면은 캐릭터를 따라가지 않습니다. 카메라를 붙이려면 캐릭터 클래스에 카메라 코드를 직접 넣어야 할까요? 그렇게 하면 나중에 차량이나 드론에도 같은 카메라가 필요할 때 코드를 다시 써야 합니다. 언리얼은 이런 기능을 컴포넌트(component)라는 부품으로 나눠, 필요한 액터에 붙여 쓰도록 설계되어 있습니다.
이 글에서는 컴포넌트 시스템이 무엇이고 UActorComponent와 USceneComponent가 어떻게 다른지 살펴본 뒤, 앞 글의 AMyCharacter에 스프링 암과 카메라를 붙입니다. 그 과정에서 컴포넌트를 만들고 부착하는 방법과, 같은 시점 회전을 몸체와 카메라가 각각 어떻게 쓰는지를 정리합니다.
기능을 부품으로 나누기
언리얼 엔진의 컴포넌트 시스템은 액터의 기능을 모듈로 나누고 재사용하기 위한 설계 방식입니다. 액터가 가진 복잡한 기능을 액터 클래스에 모두 직접 구현하는 대신, 기능마다 작은 부품을 만들고 필요한 부품을 액터에 붙여 기능을 조립합니다.
플레이어 캐릭터를 생각해 봅시다. 이 캐릭터는 3D 모델을 보여 주고, 움직이고, 점프하고, 충돌을 감지하고, 소리를 내야 합니다. 이 기능을 캐릭터 클래스 하나에 모두 구현하면 코드가 몹시 복잡해지고, 차량이나 드론처럼 다른 액터를 만들 때 그 기능을 다시 쓰기 어렵습니다. 컴포넌트로 나누면 다음처럼 됩니다.
| 기능 | 컴포넌트 |
|---|---|
| 눈에 보이는 모습 | UStaticMeshComponent(정적 메시), USkeletalMeshComponent(스켈레탈 메시) |
| 이동 | UCharacterMovementComponent(캐릭터 전용), UFloatingPawnMovement(범용) |
| 충돌 감지 | UCapsuleComponent, UBoxComponent, USphereComponent |
| 사운드 재생 | UAudioComponent |
| 이펙트 | UNiagaraComponent |
| 카메라 시점 | UCameraComponent, USpringArmComponent |
각 컴포넌트는 정해진 한 가지 기능을 맡고, 액터는 필요한 컴포넌트를 붙여 그 기능을 갖게 됩니다.
위치가 필요한 컴포넌트와 필요 없는 컴포넌트
모든 컴포넌트의 최상위 부모는 UActorComponent입니다. UActorComponent는 로직과 데이터를 가질 수 있지만, 3D 월드의 위치, 회전, 크기 같은 트랜스폼은 가지지 않습니다. 예를 들어 체력을 관리하는 UHealthComponent는 공간 좌표가 필요 없으므로 UActorComponent로 충분합니다.
USceneComponent는 UActorComponent를 상속하면서 3D 트랜스폼을 더한 타입입니다. 그래서 USceneComponent 계열은 다른 컴포넌트에 붙어 특정 위치에 존재할 수 있고, 부모가 움직이면 함께 움직입니다. UStaticMeshComponent, UCapsuleComponent, USpringArmComponent가 대표적인 예입니다.
캐릭터에 카메라 붙이기
이제 앞 글에서 만든 AMyCharacter를 넓혀, 캐릭터 뒤에서 따라오는 3인칭 카메라를 붙여 봅니다. 카메라는 두 부품으로 이루어집니다. 스프링 암(USpringArmComponent)은 캐릭터에서 일정한 거리를 두고 카메라를 받쳐 주는 팔이고, 카메라(UCameraComponent)는 그 팔 끝에서 실제로 화면을 비춥니다.
헤더에 컴포넌트 선언하기
MyCharacter.h에 두 컴포넌트를 가리키는 포인터를 선언합니다. 아래 헤더의 생략 주석은 앞 글의 코드를 지우라는 뜻이 아닙니다. 기존의 InputActionValue.h include, 입력 속성, 콜백 선언은 그대로 두고, 두 컴포넌트의 전방 선언과 멤버만 기존 클래스에 더합니다. cpp 파일에서도 생성자 하나에 카메라 설정을 합치고, 기존 BeginPlay의 매핑 컨텍스트 등록과 입력 바인딩 함수는 그대로 둡니다.
// MyCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputAction.h"
#include "InputActionValue.h"
#include "MyCharacter.generated.h"
// 미리 선언 (Forward Declarations) - include보다 가볍습니다.
class UInputMappingContext;
class UInputAction;
class USpringArmComponent; // 스프링 암 컴포넌트 추가
class UCameraComponent; // 카메라 컴포넌트 추가
UCLASS()
class MYFIRSTCPPPROJECT_API AMyCharacter : public ACharacter
{
GENERATED_BODY()
public:
AMyCharacter();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
protected:
// ... 기존 입력 관련 UPROPERTY 변수들 ...
// 새로운 컴포넌트들을 UPROPERTY로 선언
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
USpringArmComponent* CameraBoom; // 카메라를 팔처럼 지지하는 컴포넌트
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
UCameraComponent* FollowCamera; // 실제 카메라 렌더링을 담당하는 컴포넌트
// ... 기존 입력 액션 바인딩 함수들 ...
};헤더에서는 GameFramework/SpringArmComponent.h와 Camera/CameraComponent.h를 include하지 않고, class USpringArmComponent;처럼 클래스 이름만 미리 알리는 전방 선언(forward declaration)을 썼습니다. 헤더는 포인터만 가지고 있으므로 클래스의 전체 정의가 필요 없기 때문입니다. 이렇게 하면 이 헤더를 include하는 다른 파일들이 컴포넌트 헤더까지 함께 읽지 않아 컴파일 시간이 줄어들고, 컴포넌트의 멤버 함수를 실제로 호출하는 .cpp 파일에서만 해당 헤더를 include하면 됩니다.
생성자에서 만들고 부착하기
MyCharacter.cpp의 생성자에서 컴포넌트를 만들고, 액터의 컴포넌트 계층에 맞게 부착(attach)합니다. 생성자는 세 부분으로 나눠 읽으면 됩니다. 앞부분은 캐릭터 몸체가 어떤 방식으로 회전할지를 정하고, // 1. 주석 아래는 스프링 암을 만들어 루트에 붙이며, // 2. 주석 아래는 카메라를 스프링 암 끝에 붙입니다. 헤더에서 전방 선언만 했던 두 컴포넌트의 헤더는 멤버를 실제로 쓰는 이 파일의 위쪽에서 include합니다.
// MyCharacter.cpp
#include "MyCharacter.h"
#include "EnhancedInputSubsystems.h"
#include "EnhancedInputComponent.h"
#include "GameFramework/SpringArmComponent.h" // UPROPERTY로 선언한 컴포넌트의 헤더를 여기서 포함
#include "Camera/CameraComponent.h" // UPROPERTY로 선언한 컴포넌트의 헤더를 여기서 포함
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/PlayerController.h"
#include "Engine/LocalPlayer.h"
AMyCharacter::AMyCharacter()
{
PrimaryActorTick.bCanEverTick = true;
// 몸체 회전은 CharacterMovement가 처리합니다.
bUseControllerRotationYaw = false;
// RotationRate에 따라 몸체를 컨트롤러의 목표 회전 쪽으로 보간합니다.
GetCharacterMovement()->bUseControllerDesiredRotation = true;
GetCharacterMovement()->bOrientRotationToMovement = false;
// 1. CameraBoom (USpringArmComponent) 생성 및 부착
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent); // 캐릭터의 루트 컴포넌트 (CapsuleComponent)에 부착
CameraBoom->TargetArmLength = 300.0f; // 카메라와 캐릭터 사이의 거리
CameraBoom->bUsePawnControlRotation = true; // 컨트롤러 회전(ControlRotation)을 따르도록 설정, 반영할 축은 아래 bInherit*가 정함
CameraBoom->bInheritPitch = true; // 컨트롤러의 피치(Pitch) 회전을 상속받도록 설정
CameraBoom->bInheritYaw = true; // 컨트롤러의 야(Yaw) 회전을 상속받도록 설정
CameraBoom->bInheritRoll = true; // 컨트롤러의 롤(Roll) 회전을 상속받도록 설정
CameraBoom->bDoCollisionTest = true; // 카메라와 장애물 충돌 시 카메라가 당겨지도록 설정
// 2. FollowCamera (UCameraComponent) 생성 및 부착
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // CameraBoom에 부착
FollowCamera->bUsePawnControlRotation = false; // 카메라 자체는 컨트롤러 회전을 따르지 않음 (스프링암이 따르므로)
// ... 기존 변수 기본값 설정 ...
}
// ... BeginPlay(), Tick(), SetupPlayerInputComponent() 등 기존 함수들 ...CreateDefaultSubobject<T>(TEXT("Name"))는 액터의 기본 컴포넌트를 만드는 함수로, T는 만들 컴포넌트의 타입이고 TEXT("Name")은 에디터에 보일 컴포넌트의 이름입니다. 이 함수는 생성자에서만 불러야 합니다. 클래스의 기본 객체를 만들 때 함께 생성되어야 하기 때문입니다.
SetupAttachment(ParentComponent, SocketName)는 아직 등록되지 않은 씬 컴포넌트의 부모와 소켓을 정합니다. CameraBoom->SetupAttachment(RootComponent)는 스프링 암을 캐릭터의 루트 컴포넌트, 즉 UCapsuleComponent에 붙이므로 캐릭터가 움직이면 스프링 암도 함께 움직입니다. FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName)은 카메라를 스프링 암에 붙이는데, USpringArmComponent::SocketName은 스프링 암이 카메라를 붙이도록 제공하는 기본 소켓 이름입니다. 이미 등록된 컴포넌트를 실행 중에 붙일 때는 SetupAttachment 대신 AttachToComponent와 트랜스폼 규칙을 씁니다.
만든 컴포넌트의 포인터로 속성도 설정합니다. 예를 들어 TargetArmLength는 카메라와 캐릭터 사이의 목표 거리를 정하고, bDoCollisionTest는 장애물이 있으면 카메라를 당겨 오게 합니다.
생성자의 앞부분은 캐릭터 몸체의 회전 방식을 정합니다. 앞 글에서는 bOrientRotationToMovement를 켜서 캐릭터가 이동 방향으로 몸을 돌렸지만, 여기서는 bUseControllerRotationYaw = false와 GetCharacterMovement()->bOrientRotationToMovement = false로 이동 방향을 따라 도는 동작을 끕니다. 대신 GetCharacterMovement()->bUseControllerDesiredRotation = true로 몸체가 컨트롤러의 회전을 목표로 돌게 했습니다. 이때 몸체는 RotationRate에 따라 목표 방향으로 서서히 돌기 때문에, 매 순간 카메라와 같은 각도일 필요는 없습니다.
스프링 암의 bUsePawnControlRotation = true는 스프링 암이 컨트롤러의 ControlRotation, 즉 플레이어의 시점 회전을 따르게 합니다. 그래서 마우스나 스틱으로 시점을 움직이면 스프링 암이 돌고, 그 끝에 붙은 카메라도 함께 돕니다.
컨트롤러 회전과 몸체 회전 나눠 보기
bUseControllerRotationYaw, bUseControllerRotationPitch, bUseControllerRotationRoll은 APawn의 속성이므로 PlayerController에서 대입하지 않습니다. 위 구성에서는 CharacterMovementComponent와 스프링 암이 ControlRotation을 각자의 방식으로 쓰기 때문에, 카메라 회전만을 위해 별도의 PlayerController 클래스를 만들 필요는 없습니다. 다음은 나중에 컨트롤러에 다른 로직이 필요해졌을 때 쓸 기본 클래스의 뼈대입니다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "MyPlayerController.generated.h"
UCLASS()
class MYFIRSTCPPPROJECT_API AMyPlayerController : public APlayerController
{
GENERATED_BODY()
public:
AMyPlayerController();
protected:
virtual void BeginPlay() override;
};#include "MyPlayerController.h"
AMyPlayerController::AMyPlayerController()
{
// Pawn의 bUseControllerRotation* 속성은 해당 Pawn에서 설정합니다.
}
void AMyPlayerController::BeginPlay()
{
Super::BeginPlay();
// ... 추가 초기화 로직 ...
}이 커스텀 클래스를 실제로 쓸 때만 게임 모드 블루프린트(BP_MyGameMode)의 Player Controller Class를 이 클래스, 또는 이 클래스를 부모로 만든 BP_MyPlayerController로 바꿉니다.
UNREAL C++ · CH3-3
같은 ControlRotation을 몸체와 카메라가 다르게 사용합니다
본문 생성자의 최종 설정을 비교합니다. CharacterMovement는 목표 회전으로 몸체를 돌리고 SpringArm은 시점 회전과 소켓 트랜스폼을 계산합니다.
| 대상 | 원문의 최종 설정 | 결과를 해석할 기준 |
|---|---|---|
| 컨트롤러 | Look 입력으로 ControlRotation 갱신 | 몸체 트랜스폼과 별도의 시점·조준 회전 |
| 캐릭터 이동 | bUseControllerDesiredRotation=true; bOrientRotationToMovement=false | RotationRate로 목표 방향을 향해 몸체 회전 |
| 스프링 암 | bUsePawnControlRotation=true | Pawn의 시점 회전을 사용하고 상속 축 설정 반영 |
| 카메라 | 스프링 암 소켓에 부착; bUsePawnControlRotation=false | 부모가 계산한 소켓 트랜스폼으로 시점 제공 |
- 컨트롤러
- 원문의 최종 설정: Look 입력으로 ControlRotation 갱신결과를 해석할 기준: 몸체 트랜스폼과 별도의 시점·조준 회전
- 캐릭터 이동
- 원문의 최종 설정: bUseControllerDesiredRotation=true; bOrientRotationToMovement=false결과를 해석할 기준: RotationRate로 목표 방향을 향해 몸체 회전
- 스프링 암
- 원문의 최종 설정: bUsePawnControlRotation=true결과를 해석할 기준: Pawn의 시점 회전을 사용하고 상속 축 설정 반영
- 카메라
- 원문의 최종 설정: 스프링 암 소켓에 부착; bUsePawnControlRotation=false결과를 해석할 기준: 부모가 계산한 소켓 트랜스폼으로 시점 제공
TargetArmLength=300은 목표 거리입니다. 충돌 검사를 켰으므로 장애물에 의해 실제 카메라 거리가 짧아질 수 있습니다. 몸체는 RotationRate로 회전하므로 순간적으로 카메라와 같은 각도일 필요는 없습니다.
위 그림은 생성자의 최종 설정을 기준으로, 같은 ControlRotation을 몸체와 카메라가 서로 다르게 쓰는 모습을 보여 줍니다. 실선은 값과 회전의 사용을 나타냅니다. 컨트롤러의 ControlRotation은 CharacterMovement로 전달되어 목표 회전이 되고, CharacterMovement는 RotationRate에 따라 루트 캡슐인 몸체를 돌립니다. 같은 시점 회전은 스프링 암에도 전달되어, 스프링 암이 충돌을 고려해 소켓의 트랜스폼을 계산합니다.
점선은 부모에서 자식으로의 부착 관계입니다. 캡슐에 스프링 암이 붙고 스프링 암에 카메라가 붙어 있으므로, 카메라는 자체 회전을 쓰지 않고 부모 소켓을 따릅니다. 이 점선은 상속이나 권한 전달이 아닙니다.
그림의 표는 대상마다 최종 설정과 결과를 해석하는 기준을 정리합니다. 컨트롤러는 Look 입력으로 ControlRotation을 갱신하며, 이 값은 몸체의 트랜스폼과는 별개인 시점과 조준의 회전입니다. 캐릭터 이동은 bUseControllerDesiredRotation=true, bOrientRotationToMovement=false라서 RotationRate로 목표 방향을 향해 몸체를 돌립니다. 스프링 암은 bUsePawnControlRotation=true라서 폰의 시점 회전을 쓰고 상속 축 설정을 반영하며, 카메라는 스프링 암 소켓에 붙어 있고 bUsePawnControlRotation=false라서 부모가 계산한 소켓 트랜스폼으로 시점을 제공합니다.
그림 아래의 설명처럼 TargetArmLength=300은 목표 거리입니다. 기본 길이 단위로 300cm, 즉 3m이지만 bDoCollisionTest가 켜져 있으므로 장애물이 있으면 스프링 암이 줄어들어 실제 카메라 거리가 더 짧아질 수 있습니다. 또 FollowCamera의 bUsePawnControlRotation=false를 보고 카메라가 부모와도 함께 돌지 않는다고 해석하면 안 됩니다. 카메라 자체가 시점 회전을 직접 읽지 않을 뿐, 부모인 스프링 암이 돌면 그 소켓에 붙은 카메라도 따라 돕니다.
생성자 밖에서 컴포넌트를 다룰 때
생성자에서 만드는 기본 컴포넌트에는 CreateDefaultSubobject를 씁니다. 반면 실행 중에 추가하는 기능은 NewObject로 만든 뒤 소유 액터를 정하고, RegisterComponent로 등록하고, 필요하면 공간 부착을 설정합니다. 이런 컴포넌트는 끝날 때 DestroyComponent로 정리하고, 남아 있는 참조와 델리게이트 연결도 함께 정리해야 합니다.
코드를 빌드하고 에디터에서 Play를 누르면, 이제 캐릭터 뒤에서 카메라가 따라오고 마우스를 움직이면 시점과 카메라가 함께 돌아갑니다. 몸체는 RotationRate에 따라 시점 쪽으로 천천히 돌아섭니다.
더 깊이: 컴포넌트를 등록하면 무엇이 준비될까
컴포넌트를 등록(registration)하면 엔진은 그 컴포넌트를 씬에 연결해 프레임마다 갱신할 수 있게 합니다. 액터가 스폰될 때 하위 객체로 만들어진 컴포넌트는 자동으로 등록되므로, 생성자에서 CreateDefaultSubobject로 만든 스프링 암과 카메라에는 RegisterComponent를 부르지 않았습니다. 등록 과정에서는 재정의할 수 있는 OnRegister가 불리고, 렌더 상태를 초기화하는 CreateRenderState와 물리 상태를 초기화하는 OnCreatePhysicsState가 실행됩니다. 그래서 실행 중에 NewObject로 만들기만 하고 등록하지 않은 메시 컴포넌트는 렌더 상태도 물리 상태도 갖추지 못한 채로 남습니다.
등록을 해제하는 UnregisterComponent는 반대로 OnUnregister, DestroyRenderState, OnDestroyPhysicsState를 거쳐 이 상태들을 정리합니다. 공식 문서는 플레이 중의 등록이 성능에 영향을 줄 수 있으므로 꼭 필요할 때만 하라고 안내합니다. 잠깐 쓰고 버리는 기능을 매번 새 컴포넌트로 만들어 등록하는 설계는 이 준비와 정리를 되풀이하게 됩니다.
등록된 뒤에 렌더링에 쓰이는 값이 바뀌면 컴포넌트는 자신의 렌더 상태를 갱신이 필요한 상태(dirty)로 표시하고, 엔진은 프레임이 끝날 때 이렇게 표시된 컴포넌트의 렌더 데이터를 한꺼번에 갱신합니다. 직접 만든 컴포넌트가 엔진이 모르는 방식으로 렌더 데이터를 바꿨다면 MarkRenderStateDirty로 직접 표시할 수 있습니다.
컴포넌트로 나누면 좋아지는 점
컴포넌트는 한 번 만들면 여러 액터에 붙여 다시 쓸 수 있습니다. UHealthComponent를 플레이어와 적, 파괴할 수 있는 환경 오브젝트에 모두 붙이는 식입니다. 각 컴포넌트가 독립된 기능을 맡으므로, 어떤 기능을 고치거나 더할 때 그 컴포넌트만 고치면 되어 유지 보수도 쉬워집니다.
실행 중에 컴포넌트를 추가하거나 제거할 수 있다는 유연함도 있습니다. 예를 들어 파워업 아이템을 먹으면 UWeaponComponent를 더하고, 특정 상태가 되면 제거할 수 있습니다. 협업에도 유리해서, 프로그래머가 C++로 컴포넌트를 만들면 기획자는 블루프린트에서 그 컴포넌트를 조립하고 속성을 정해 게임플레이를 빠르게 만들 수 있습니다. 되풀이되는 기능을 커스텀 컴포넌트로 나눌 때는 공간 좌표가 필요한지와 소유 액터의 수명을 먼저 정합니다.
정리
- 컴포넌트 시스템은 액터의 기능을 부품으로 나누고, 필요한 부품을 붙여 기능을 조립하는 방식입니다.
UActorComponent는 트랜스폼이 없는 기능 단위이고,USceneComponent는 트랜스폼과 부착 계층을 갖는 컴포넌트입니다.- 헤더에서는 컴포넌트 포인터를 전방 선언으로 두고, 생성자에서
CreateDefaultSubobject로 만든 뒤SetupAttachment로 부모와 소켓을 정합니다. - 스프링 암은
bUsePawnControlRotation=true로 시점 회전을 따르고, 카메라는 그 소켓을 따릅니다. 몸체는bUseControllerDesiredRotation과RotationRate로 서서히 돕니다. TargetArmLength는 목표 거리이며 충돌 검사 때문에 실제 거리가 짧아질 수 있습니다.- 실행 중에 추가하는 컴포넌트는
NewObject,RegisterComponent,DestroyComponent로 만들고 정리합니다.
연습 문제
1. 어느 계열로 만들까
월드 위치가 필요 없는 체력 계산을 여러 액터에서 재사용할 컴포넌트와, 카메라를 받칠 공간 부품을 만들려고 합니다. UActorComponent와 USceneComponent 가운데 각각 어느 계열이 알맞을까요?
해설 보기
체력 계산에는 공간 트랜스폼이 필요 없으므로 UActorComponent로 충분합니다. 카메라를 받치고 다른 컴포넌트를 붙일 위치를 제공하려면 트랜스폼과 부착 계층이 있어야 하므로 USceneComponent 계열을 씁니다. 이 글의 스프링 암이 바로 그런 부품입니다.
2. 카메라가 시점을 따르는 경로
이 글의 설정에서 FollowCamera는 bUsePawnControlRotation=false라서 시점 회전을 직접 쓰지 않습니다. 그런데도 마우스를 움직이면 화면이 돌아갑니다. 카메라의 시점은 어떤 경로로 바뀌는 걸까요?
해설 보기
FollowCamera는 부모인 스프링 암의 소켓에 붙어 있어서, 그 소켓이 계산한 트랜스폼을 따릅니다. 스프링 암이 bUsePawnControlRotation=true로 ControlRotation을 따라 돌면 소켓도 돌고, 그 소켓에 붙은 카메라도 함께 돕니다.
카메라 자체의 시점 회전 옵션을 끈 것과 부모의 변화를 따르지 않는 것은 다른 조건입니다. 카메라는 시점을 직접 읽지 않을 뿐, 부착 관계로 부모의 회전을 이어받습니다.
3. 3m라고 했는데 더 가깝다
TargetArmLength를 300으로 정했는데, 캐릭터가 벽에 붙어 서면 카메라가 3m보다 훨씬 가까이 옵니다. 버그일까요?
해설 보기
버그가 아닙니다. TargetArmLength는 목표 거리이고, 이 예제는 bDoCollisionTest를 켰으므로 스프링 암과 카메라 사이에 장애물이 있으면 스프링 암이 줄어듭니다. 벽에 가까이 서면 카메라가 벽 안으로 들어가지 않도록 앞으로 당겨지는 것이 의도된 동작입니다. 항상 3m를 유지하고 싶다면 충돌 검사를 끌 수 있지만, 그러면 카메라가 벽을 뚫고 지나갈 수 있습니다.
스프링 암의 소켓, 시점 회전, 목표 길이, 충돌 검사 옵션은 Epic의 USpringArmComponent API 문서에서 확인할 수 있습니다.
다음 글 스테이트 머신과 애니메이션 Blueprint 연동에서는 C++ 캐릭터의 이동 상태를 애니메이션 블루프린트에 전달하고, 스테이트 머신으로 포즈를 전환하는 방법을 살펴봅니다.