[7번 과제] Pawn 클래스로 3D 캐릭터 만들기
처음에는 Move / Look / UpDown / Roll 로 필수 과제에서 구현한 이동, 회전 함수에 추가로 상/하 이동 함수, Roll 회전 함수를 따로 만들었습니다. 구현하던 중 기존 xy에서 z를 추가로 구현한다는 사실을 깨닫고 상/하 이동을 Move 함수에, Roll 회전을 Look 함수에서 구현하도록 변경하였습니다. Input Action 또한 Axis2D에서 Axis3D로 변경했습니다.
도전 과제 이외에도 추가로 지면에 닿았을때 똑바로 서도록, 직립 상태가 되도록 구현했습니다. 이는 지면에 닿았을 때 각도가 기울어져 있으면 이동이 제대로 되지 않아 이렇게 변경하였습니다.
또한, 구현하면서 오래 걸렸던 문제는 낙하는 잘 되는데 shift를 누르면 땅을 뚫고 내려가는 문제였는데, 이는 AddActorLocalOffset에서 bSweep를 true로 하면 충돌을 감지해 이 문제를 해결할 수 있었습니다. AddActorLocalOffset을 처음 써서 문서 5.5를 봤을 때 따로 bSweep에 대한 설명이 없어서 가볍게 넘어갔는데 문제를 해결해 찾던 중 문서 5.0에서 bSweep에 대한 설명을 찾아 해결할 수 있었습니다.
#include "MyPawn.h"
#include "Components/CapsuleComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
AMyPawn::AMyPawn()
{
PrimaryActorTick.bCanEverTick = true;
Capsule = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule"));
RootComponent = Capsule;
Mesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Mesh"));
Mesh->SetupAttachment(Capsule);
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
SpringArm->SetupAttachment(Capsule);
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
Camera->SetupAttachment(SpringArm);
}
void AMyPawn::BeginPlay()
{
Super::BeginPlay();
}
void AMyPawn::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!IsGround())
{
Velocity.X *= AirMoveMultiplier;
Velocity.Y *= AirMoveMultiplier;
Velocity.Z += -980.f * DeltaTime;
AddActorLocalOffset(Velocity * DeltaTime, true);
bHasTouchedGround = false;
}
else
{
FRotator CurrentRotation = GetActorRotation();
if (!bHasTouchedGround)
{
SetActorRotation(FRotator(0.f, CurrentRotation.Yaw, 0.f));
bHasTouchedGround = true;
}
}
}
void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(PlayerInputComponent);
if (EnhancedInput)
{
EnhancedInput->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyPawn::Move);
EnhancedInput->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyPawn::Look);
}
}
void AMyPawn::Move(const FInputActionValue& Value)
{
if (!Controller) return;
const FVector MoveInput = Value.Get<FVector>();
Velocity.Z = MoveInput.Z * MoveSpeed;
AddActorLocalOffset(MoveInput * MoveSpeed * GetWorld()->GetDeltaSeconds(), true);
}
void AMyPawn::Look(const FInputActionValue& Value)
{
const FVector LookInput = Value.Get<FVector>();
FRotator NewRotation = FRotator(LookInput.Y * RotationSpeed, LookInput.X * RotationSpeed, LookInput.Z * RotationSpeed);
AddActorLocalRotation(NewRotation);
}
bool AMyPawn::IsGround() const
{
FVector Start = GetActorLocation();
FVector End = Start + FVector(0.0f, 0.0f, -90.0f);
FHitResult HitResult;
FCollisionQueryParams CollisionParams;
CollisionParams.AddIgnoredActor(this);
bool bHit = GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, ECC_Visibility, CollisionParams);
return bHit;
}
'내일배움캠프 > TIL' 카테고리의 다른 글
[내일배움캠프 Day30] auto (1) | 2025.01.31 |
---|---|
[내일배움캠프 Day29] 정렬 (1) | 2025.01.27 |
[내일배움캠프 Day27] 플레이어 이동 처리하기 (1) | 2025.01.23 |
[내일배움캠프 Day26] C++ 6주차 과제 진행 (1) | 2025.01.22 |
[내일배움캠프 Day25] 액터의 라이프 사이클 (2) | 2025.01.21 |