캐릭터 컨트롤
- 일반적으로 컨트롤러, 폰, 카메라, 스프링암, 캐릭터 무브먼트 요소를 사용해 설정
- 컨트롤러 : 입력자의 목표 지점을 지정할 때 사용, ControlRotation 속성
- 폰 : 폰의 트랜스폼 지정
- 카메라 : 화면 구도를 설정하기 위해 사용, 주로 1인칭 시점에서 사용
- 스프링 암 : 화면 구도를 설정하기 위해 사용, 주로 3인칭 시점에서 사용
- 캐릭터 무브먼트 : 캐릭터의 이동과 회전을 조정하는 용도로 사용
Rotation(현재 상태)에서 Desired Rotation(목표 상태)로 한번에 회전하면 부자연스러우므로 각속도로 회전해야 함
폰의 이동 함수
- Look : 마우스 입력으로부터 컨트롤러의 컨트롤 회전을 설정
- Move : 컨트롤러의 컨트롤 회전으로부터 Yaw 값을 참고해 이동 방향을 설정
- 콘솔 커맨드 창(단축키 ~)에서 Control Rotation 값을 확인할 수 있음
- 콘솔 창에 Display 클래스이름 속성이름을 입력하면 속성 값을 확인할 수 있음
폰의 컨트롤 옵션
- Use Controller Rotation (Pitch/Yaw/Roll)
- 폰의 회전을 컨트롤러의 Control Rotation과 동기화됨
- 일정 속도로 회전하는 것이 아닌 바로 동기화
스프링암의 컨트롤 옵션
- Use Pawn Control Rotation : 스프링 암의 회전을 컨트롤러의 Control Rotation과 동기화됨
- Inherit (Yaw/Roll/Pitch) : 부모 컴포넌트의 회전 값을 상속받아 최종 회전을 구함
- Do Collision Test : 카메라와 캐릭터 사이를 지탱해주는 스프링 암 중간에 장애물이 생겼을 때 장애물 앞으로 카메라를 당기는 옵션
카메라의 컨트롤 옵션
- Use Pawn Control Rotation : 스프링 암에 달린 카메라의 회전을 컨트롤러의 Control Rotation과 동기화, 주로 1인칭
캐릭터 무브먼트 옵션
- MovementMode : 각 모드별로 이동이 다름, 이동 기능을 끄고 싶으면 None
- MaxWalkSpeed : 이동 모드에서의 이동 수치
- JumpZVelocity : 점프의 속도
- Rotation Rate : 회전 속도
- Use Controller Desired Rotation : 컨트롤 회전을 목표 회전으로 삼고 지정한 속도로 돌리기
- Orient Rotation To Movement : 캐릭터 이동 방향에 회전을 일치시키기
- 폰의 회전 옵션과 충돌나지 않도록 주의
Look 함수의 AddControllerYaw를 살펴보자
// ABCharacterPlayer.cpp
void AABCharacterPlayer::Look(const FInputActionValue& Value)
{
FVector2D LookAxisVector = Value.Get<FVector2D>();
AddControllerYawInput(LookAxisVector.X);
AddControllerPitchInput(LookAxisVector.Y);
}
// Pawn.cpp
void APawn::AddControllerYawInput(float Val)
{
if (Val != 0.f && Controller && Controller->IsLocalPlayerController())
{
APlayerController* const PC = CastChecked<APlayerController>(Controller);
PC->AddYawInput(Val);
}
}
// PlayerController.cpp
// Input으로 들어온 Yaw 값을 RotationInput에 저장
void APlayerController::AddYawInput(float Val)
{
RotationInput.Yaw += !IsLookInputIgnored() ? Val * (GetDefault<UInputSettings>()->bEnableLegacyInputScales ? InputYawScale_DEPRECATED : 1.0f) : 0.0f;
}
// 저장된 RotationInput로 Delta Rotation을 구하고, 변화한 Rotation 값을 설정
void APlayerController::UpdateRotation( float DeltaTime )
{
// Calculate Delta to be applied on ViewRotation
FRotator DeltaRot(RotationInput);
FRotator ViewRotation = GetControlRotation();
if (PlayerCameraManager)
{
PlayerCameraManager->ProcessViewRotation(DeltaTime, ViewRotation, DeltaRot);
}
...
SetControlRotation(ViewRotation);
...
}
데이터 에셋
- UDataAsset을 상속받은 언리얼 오브젝트 클래스
- 주요 옵션을 모아 에디터에서 에셋 형태로 편리하게 데이터를 관리
- 런타임에 에셋을 교체하여 설정을 변경할 수 있음
- 각 섹션(Pawn, Character Movement, Input, SpirngArm) 별로 데이터를 저장
데이터 에셋으로 카메라 뷰 변경하는 방법
- 데이터 에셋 생성 후 뷰 타입(Quater, Shoulder)에 따른 데이터 에셋을 저장
- 쿼터 뷰일 때 화면을 회전할 필요는 없으므로 Input Mapping Context를 분리
- 키를 입력했을 때 뷰가 변경되어 데이터 에셋에 따른 설정, Input Mapping Context 변경
// UABCharacterControllData.h
// 해당 클래스로 데이터 에셋을 생성 후 뷰에 맞게 설정 변경
class ARENABATTLE_API UABCharacterControllData : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = Pawn)
uint32 bUseControllerRotationYaw : 1;
UPROPERTY(EditAnywhere, Category = CharacterMovement)
uint32 bOrientRotationToMovement : 1;
UPROPERTY(EditAnywhere, Category = CharacterMovement)
uint32 bUseControllerDesiredRotation : 1;
UPROPERTY(EditAnywhere, Category = CharacterMovement)
FRotator RotationRate;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
TObjectPtr<class UInputMappingContext> InputMappingContext;
UPROPERTY(EditAnywhere, Category = SpringArm)
float TargetArmLength = 400.0f;
UPROPERTY(EditAnywhere, Category = SpringArm)
FRotator RelativeRotation;
UPROPERTY(EditAnywhere, Category = SpringArm)
uint32 bUsePawnControlRotation : 1;
UPROPERTY(EditAnywhere, Category = SpringArm)
uint32 bInheritPitch : 1;
UPROPERTY(EditAnywhere, Category = SpringArm)
uint32 bInheritYaw : 1;
UPROPERTY(EditAnywhere, Category = SpringArm)
uint32 bInheritRoll : 1;
UPROPERTY(EditAnywhere, Category = SpringArm)
uint32 bDoCollisionTest : 1;
};
// ABCharacterBase.cpp
void AABCharacterPlayer::BeginPlay()
{
Super::BeginPlay();
SetCharacterControl(CurrentCharacterControlType);
}
void AABCharacterPlayer::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// EnhancedInput을 사용하지 않을 경우 에러를 발생하도록 CastChecked 사용
UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
EnhancedInputComponent->BindAction(ChangeControlAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::ChangeCharacterControl);
EnhancedInputComponent->BindAction(ShoulderMoveAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::ShoulderMove);
EnhancedInputComponent->BindAction(ShoulderLookAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::ShoulderLook);
EnhancedInputComponent->BindAction(QuaterMoveAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::QuaterMove);
}
void AABCharacterPlayer::QuaterMove(const FInputActionValue& Value)
{
FVector2D MovementVector = Value.Get<FVector2D>();
float MovementVectorSize = 1.0f;
float MovementVectorSizeSqaured = MovementVector.SquaredLength();
if (MovementVectorSizeSqaured > 1.0f)
{
MovementVector.Normalize();
MovementVectorSizeSqaured = 1.0f;
}
else
{
MovementVectorSize = FMath::Sqrt(MovementVectorSizeSqaured);
}
const FVector MoveDirection = FVector(MovementVector.X, MovementVector.Y, 0.0f);
GetController()->SetControlRotation(FRotationMatrix::MakeFromX(MoveDirection).Rotator());
AddMovementInput(MoveDirection, MovementVectorSize);
}
void AABCharacterPlayer::ChangeCharacterControl()
{
if (CurrentCharacterControlType == ECharacterControlType::Quater)
{
SetCharacterControl(ECharacterControlType::Shoulder);
}
else if (CurrentCharacterControlType == ECharacterControlType::Shoulder)
{
SetCharacterControl(ECharacterControlType::Quater);
}
}
void AABCharacterPlayer::SetCharacterControl(ECharacterControlType NewCharacterControlType)
{
UABCharacterControllData* NewCharacterControlData = CharacterControlManager[NewCharacterControlType];
check(NewCharacterControlData);
SetCharacterControlData(NewCharacterControlData);
APlayerController* PlayerController = CastChecked<APlayerController>(GetController());
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->ClearAllMappings();
UInputMappingContext* NewMappingContext = NewCharacterControlData->InputMappingContext;
if (NewMappingContext)
{
Subsystem->AddMappingContext(NewMappingContext, 0); // 두 번째 매개변수는 우선순위로 다양한 입력이 겹칠 때 우선 순위 높은 것이 작동
}
}
CurrentCharacterControlType = NewCharacterControlType;
}
Roll : X축을 기준으로 회전반경
Yaw : Z축을 기준으로 회전반경
Pitch : Y축을 기준으로 회전반경
'언리얼 > 언리얼 구현 예제' 카테고리의 다른 글
언리얼5 캐릭터 스탯과 위젯 (0) | 2024.11.05 |
---|---|
언리얼 엔진5 공격 판정 (1) | 2024.09.24 |
언리얼5 캐릭터 콤보 액션 (0) | 2024.09.11 |
언리얼5 캐릭터 애니메이션 설정 (1) | 2024.09.07 |
언리얼5 캐릭터와 입력 시스템 (6) | 2024.09.04 |