728x90
반응형
시작하기전 반드시 알아야할 개념이 있다.
로컬 좌표계(Local Coordinate System)
- 객체 자체를 기준으로 한 좌표계
- 객체가 회전하거나 이동해도 **자신의 원점(0,0,0)**을 기준으로 좌표가 유지됨
- 예: 비행기가 기울어진 상태에서 "앞으로" 이동하면, 비행기의 앞 방향을 따라 이동
월드 좌표계(World Coordinate System)
- **씬 전체(또는 절대적인 기준점)**을 기준으로 한 좌표계
- 객체가 회전하더라도 월드 기준의 방향은 변하지 않음
- 예: 지구 위의 좌표계에서 사람이 방향을 바꿔도 북쪽(월드 좌표 기준)은 변하지 않음
차이점
기준로컬 좌표월드 좌표
기준점 | 개별 객체의 원점 | 씬 전체의 원점 |
이동 방향 | 객체의 방향에 따라 변함 | 항상 같은 방향 |
회전 영향 | 회전하면 축도 함께 변함 | 회전해도 축은 고정 |
활용 예시
- 로컬 좌표: 로봇 팔 관절 회전, 캐릭터의 상대적 이동
- 월드 좌표: 맵 배치, 전역적인 위치 계산
로컬 좌표는 개별 객체의 동작을 쉽게 제어하는 데 유용하고, 월드 좌표는 전체적인 배치를 관리하는 데 적합하다.
드론을 구현하기 위해서는 마우스가 움직일때 매쉬와 함께 카메라가 움직일 것인지도 고려해야한다 이를 위한 개념을 살펴보자
1. 컨트롤러와 폰(Pawn) 관계
- bUseControllerRotation: 컨트롤러의 회전을 폰의 회전과 동기화할지 여부를 결정하는 속성
- Character 클래스는 기본적으로 bUseControllerRotationYaw = true
→ 컨트롤러가 회전하면 즉시 폰도 같은 방향으로 회전
2. 폰(Pawn)과 SpringArmComponent 관계
- bUsePawnControlRotation: 폰이 회전할 때 SpringArm도 회전할지 여부
- false: 폰이 회전해도 SpringArm은 움직이지 않음
- 예: 컷씬에서 플레이어가 움직이지 않는 경우에도 카메라 위치를 유지 가능
- bDoCollisionTest: 카메라가 벽을 통과하는지 여부를 결정
- 벽과 충돌하면 카메라를 앞으로 당김
- bInherit: Root Component의 회전을 따라갈지 여부 결정
3. 폰과 CameraComponent 관계
- CameraComponent는 SpringArmComponent의 자식
→ bUsePawnControlRotation이 CameraComponent에 적용되지 않고 SpringArmComponent에 따라감
정리
- 컨트롤러 → 폰: bUseControllerRotation이 true이면 컨트롤러 회전에 따라 폰이 회전
- 폰 → SpringArm: bUsePawnControlRotation이 false이면 폰이 회전해도 SpringArm은 회전하지 않음
- SpringArm → 카메라: SpringArm이 움직이면 카메라도 함께 움직임
즉, 컨트롤러, 폰, SpringArm, 카메라의 관계를 조절하는 여러 속성이 있으며,
각 속성을 조정하면 카메라의 움직임과 캐릭터의 회전 방식을 원하는 대로 변경할 수 있다.
이를 앞서 드론의 구현파일은 다음과 같다
#include "Drone.h"
#include "DroneController.h"
#include "EnhancedInputComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Components/CapsuleComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
const FVector ADrone::Gravity = FVector(0.f, 0.f, -980.f);
ADrone::ADrone()
: bMoveInputIgnored(false)
{
PrimaryActorTick.bCanEverTick = true;
bUseControllerRotationPitch = true;
bUseControllerRotationYaw = true;
bUseControllerRotationRoll = true;
CapsuleComp = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleComp"));
CapsuleComp->InitCapsuleSize(25.f, 25.f);
CapsuleComp->SetCollisionProfileName(UCollisionProfile::Pawn_ProfileName);
CapsuleComp->CanCharacterStepUpOn = ECB_No;
CapsuleComp->SetShouldUpdatePhysicsVolume(true);
CapsuleComp->SetCanEverAffectNavigation(false);
CapsuleComp->bDynamicObstacle = true;
SetRootComponent(CapsuleComp);
DroneMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DroneMeshComp"));
DroneMeshComp->SetupAttachment(RootComponent);
SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComp"));
SpringArmComp->SetupAttachment(RootComponent);
SpringArmComp->TargetArmLength = 300.f;
SpringArmComp->bUsePawnControlRotation = false;
SpringArmComp->bInheritPitch = false;
SpringArmComp->bInheritYaw = false;
SpringArmComp->bInheritRoll = false;
CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComp"));
CameraComp->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName);
CameraComp->bUsePawnControlRotation = false;
NormalSpeed = 500.f;
}
void ADrone::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
FVector GravityDisplacement = Gravity * DeltaTime;
FHitResult HitResult;
AddActorWorldOffset(GravityDisplacement * DeltaTime, true, &HitResult);
if (HitResult.IsValidBlockingHit() == true)
{
bMoveInputIgnored = true;
}
else
{
bMoveInputIgnored = false;
}
}
void ADrone::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(PlayerInputComponent))
{
if (ADroneController* PlayerController = Cast<ADroneController>(GetController()))
{
if (PlayerController->DroneMoveAction)
{
EnhancedInput->BindAction(PlayerController->DroneMoveAction, ETriggerEvent::Triggered, this, &ThisClass::DroneMove);
}
if (PlayerController->DroneLookAction)
{
EnhancedInput->BindAction(PlayerController->DroneLookAction, ETriggerEvent::Triggered, this, &ThisClass::DroneLook);
}
if (PlayerController->MoveupAction)
{
EnhancedInput->BindAction(PlayerController->MoveupAction, ETriggerEvent::Triggered, this, &ThisClass::MoveUp);
}
if (PlayerController->RollAction)
{
EnhancedInput->BindAction(PlayerController->RollAction, ETriggerEvent::Triggered, this, &ThisClass::Roll);
}
}
}
}
void ADrone::DroneMove(const FInputActionValue& InValue)
{
if (IsValid(Controller) == false)
{
return;
}
const FVector2D MoveInput = InValue.Get<FVector2D>();
if (FMath::IsNearlyZero(MoveInput.X) == false)
{
AddActorWorldOffset(GetActorForwardVector() * MoveInput.X);
}
if (FMath::IsNearlyZero(MoveInput.Y) == false)
{
AddActorWorldOffset(GetActorRightVector() * MoveInput.Y);
}
}
void ADrone::DroneLook(const FInputActionValue& InValue)
{
if (IsValid(Controller) == false)
{
return;
}
const FVector2D LookInput = InValue.Get<FVector2D>();
if (FMath::IsNearlyZero(LookInput.X) == false)
{
AddActorWorldRotation(FRotator(0.f, LookInput.X, 0.f));
}
if (FMath::IsNearlyZero(LookInput.Y) == false)
{
SpringArmComp->AddRelativeRotation(FRotator(LookInput.Y, 0.f, 0.f));
}
}
void ADrone::MoveUp(const FInputActionValue& InValue)
{
if (IsValid(Controller) == false)
{
return;
}
const float UpDownInput = InValue.Get<float>();
if (UpDownInput < 0.f && bMoveInputIgnored == true)
{
return;
}
if (FMath::IsNearlyZero(UpDownInput) == false)
{
// 드론의 메쉬(StaticMeshComp)의 회전 기준으로 이동 방향 설정
FRotator DroneRotation = DroneMeshComp->GetComponentRotation();
FVector LocalUpVector = DroneRotation.RotateVector(FVector::UpVector);
AddActorWorldOffset(LocalUpVector * UpDownInput);
}
}
void ADrone::Roll(const FInputActionValue& InValue)
{
const float RollInput = InValue.Get<float>();
if (FMath::IsNearlyZero(RollInput) == false)
{
DroneMeshComp->AddLocalRotation(FRotator(0, 0, RollInput * 2.f)); // Roll 적용
}
}
다소 아쉬운부분: 매쉬의 기즈모기준이 다른곳에 가있어서 그런지 회전축도 다른모습이다...
게다가 look 함수가 제대로 작용하지 않는듯 하여 수정필요
728x90
반응형
'언리얼(Unreal) > 엔진' 카테고리의 다른 글
25.02.10 언리얼엔진으로 성 만들기 (0) | 2025.02.10 |
---|---|
25.01.22 actor의 라이프 사이클 이해 및 thick 함수의 이해 (1) | 2025.01.22 |
25.01.21 언리얼 Actor 클래스의 이해 (0) | 2025.01.21 |
25.01.10 깃허브데스크탑 사용법 (1) | 2025.01.10 |
24.01.07 언리얼에서의 cpp(언리얼과 비쥬얼 스튜디오 호환 에러 해결법) (0) | 2025.01.07 |