누적 학습 시간 : 248시간 34분
📅 2026-05-07
C++ 캐릭터 생성
PlayerCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
#include "PlayerCharacter.generated.h"
class UInputMappingContext;
class UInputAction;
class USceneComponent;
UCLASS()
class METAL3D_API APlayerCharacter : public ACharacter
{
GENERATED_BODY()
public:
APlayerCharacter();
protected:
virtual void BeginPlay() override;
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Input")
TObjectPtr<UInputMappingContext> DefaultMappingContext;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Input")
TObjectPtr<UInputAction> MoveAction;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Input")
TObjectPtr<UInputAction> LookAction;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Input")
TObjectPtr<UInputAction> FireAction;
virtual void NotifyControllerChanged() override;
UPROPERTY(EditDefaultsOnly, Category="Combat")
TSubclassOf<class AProjectile> ProjectileClass;
USceneComponent* FindMuzzlePoint() const;
UPROPERTY(EditDefaultsOnly, Category="Combat")
FName MuzzlePointComponentName = TEXT("MuzzlePoint");
UPROPERTY(EditDefaultsOnly, Category="Combat")
float AimTraceDistance = 10000.f;
void Fire();
public:
virtual void Tick(float DeltaTime) override;
private:
void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
};
PlayerCharacter.cpp
#include "Player/PlayerCharacter.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "Projectile/Projectile.h"
#include "GameFramework/PlayerController.h"
#include "Camera/CameraComponent.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/PlayerController.h"
#include "DrawDebugHelpers.h"
APlayerCharacter::APlayerCharacter()
{
PrimaryActorTick.bCanEverTick = true;
}
void APlayerCharacter::BeginPlay()
{
Super::BeginPlay();
}
void APlayerCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent);
if (!EnhancedInputComponent)
{
return;
}
if (MoveAction)
{
EnhancedInputComponent->BindAction(
MoveAction,
ETriggerEvent::Triggered,
this,
&APlayerCharacter::Move
);
}
if (LookAction)
{
EnhancedInputComponent->BindAction(
LookAction,
ETriggerEvent::Triggered,
this,
&APlayerCharacter::Look
);
}
if (FireAction)
{
EnhancedInputComponent->BindAction(
FireAction,
ETriggerEvent::Triggered,
this,
&APlayerCharacter::Fire
);
}
}
void APlayerCharacter::NotifyControllerChanged()
{
Super::NotifyControllerChanged();
APlayerController* PlayerController = Cast<APlayerController>(GetController());
if (!PlayerController)
{
return;
}
ULocalPlayer* LocalPlayer = PlayerController->GetLocalPlayer();
if (!LocalPlayer)
{
return;
}
UEnhancedInputLocalPlayerSubsystem* InputSubsystem = LocalPlayer->GetSubsystem<
UEnhancedInputLocalPlayerSubsystem>();
if (InputSubsystem && DefaultMappingContext)
{
InputSubsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
void APlayerCharacter::Move(const FInputActionValue& Value)
{
const FVector2D MovementVector = Value.Get<FVector2D>();
if (Controller == nullptr)
{
return;
}
const FRotator ControlRotation = Controller->GetControlRotation();
const FRotator YawRotation(0.0f, ControlRotation.Yaw, 0.0f);
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
void APlayerCharacter::Look(const FInputActionValue& Value)
{
const FVector2D LookAxis = Value.Get<FVector2D>();
AddControllerYawInput(LookAxis.X);
AddControllerPitchInput(-LookAxis.Y);
}
USceneComponent* APlayerCharacter::FindMuzzlePoint() const
{
TArray<USceneComponent*> SceneComponents;
GetComponents<USceneComponent>(SceneComponents);
for (USceneComponent* Component : SceneComponents)
{
if (IsValid(Component) && Component->GetName() == MuzzlePointComponentName.ToString())
{
return Component;
}
}
return nullptr;
}
void APlayerCharacter::Fire()
{
if (!ProjectileClass)
{
return;
}
APlayerController* PC = Cast<APlayerController>(GetController());
if (!PC)
{
return;
}
int32 ViewportX = 0;
int32 ViewportY = 0;
PC->GetViewportSize(ViewportX, ViewportY);
const float ScreenCenterX = ViewportX * 0.5f;
const float ScreenCenterY = ViewportY * 0.5f;
FVector WorldLocation;
FVector WorldDirection;
const bool bDeprojected = PC->DeprojectScreenPositionToWorld(
ScreenCenterX,
ScreenCenterY,
WorldLocation,
WorldDirection
);
if (!bDeprojected)
{
return;
}
const FVector TraceStart = WorldLocation;
const FVector TraceEnd = TraceStart + WorldDirection * AimTraceDistance;
FHitResult CameraHit;
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(this);
GetWorld()->LineTraceSingleByChannel(
CameraHit,
TraceStart,
TraceEnd,
ECC_Visibility,
QueryParams
);
const FVector AimTarget = CameraHit.bBlockingHit ? CameraHit.ImpactPoint : TraceEnd;
const USceneComponent* MuzzlePoint = FindMuzzlePoint();
const FVector MuzzleLocation = MuzzlePoint->GetComponentLocation();
const FVector FireDirection = (AimTarget - MuzzleLocation).GetSafeNormal();
const FRotator SpawnRotation = FireDirection.Rotation();
DrawDebugLine(
GetWorld(),
TraceStart,
TraceEnd,
FColor::Green,
false,
2.f,
0,
1.f
);
DrawDebugLine(
GetWorld(),
MuzzleLocation,
MuzzleLocation + FireDirection * 3000.f,
FColor::Red,
false,
2.f,
0,
2.f
);
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
SpawnParams.Instigator = this;
SpawnParams.SpawnCollisionHandlingOverride =
ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
GetWorld()->SpawnActor<AProjectile>(
ProjectileClass,
MuzzleLocation,
SpawnRotation,
SpawnParams
);
}