Choose your operating system:
Windows
macOS
Linux
-
이제 Visual Studio 로 돌아가 커스텀 Pawn Movement Component 프로그래밍을 하면 됩니다. 작성해야 할 것은 ( Actor 의 Tick 함수와 비슷하게) TickComponent 함수로 각 프레임 어떻게 이동할지를 알려주면 됩니다.
CollidingPawnMovementComponent.h
에서, 클래스 정의의 TickComponent 를 덮어써야 합니다:public: virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
이 함수를
CollidingPawnMovementComponent.cpp
에 정의해 줍니다:void UCollidingPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); // 모든 것이 아직 유효한지, 이동 가능한지 확인합니다. if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime)) { return; } // ACollidingPawn::Tick 에 설정한 무브먼트 벡터를 구(한 뒤 소거)합니다. FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * 150.0f; if (!DesiredMovementThisFrame.IsNearlyZero()) { FHitResult Hit; SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit); // 무언가에 부딛혔으면, 돌아가 봅니다. if (Hit.IsValidBlockingHit()) { SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit); } } };
이 코드는 적합한 면을 미끄러져 다니며 월드를 부드럽게 움직이도록 폰 을 이동시킵니다. 폰 에는 중력이 적용되지 않으며, 최대 속력은 초당 150 언리얼 유닛 으로 하드코딩되어 있습니다.
이 TickComponent 함수는 UPawnMovementComponent 클래스에 제공되는 강력한 기능을 몇 가지 활용하고 있습니다.
ConsumeInputVector 는 미동 입력을 저장하는 데 사용할 내장 변수 값을 보고 및 소거합니다.
SafeMoveUpdatedComponent 는 언리얼 엔진 피직스를 사용하여 입체 장애물을 피해 Pawn Movement Component 를 이동시킵니다.
SlideAlongSurface 는 이동하다가 충돌 발생시 그냥 제자리에 멈춰 벽이나 경사로에 "달라붙기" 보다는, 그 표면을 타고 부드럽게 미끄러지도록 하는 데 관련된 물리 계산 처리를 합니다.
Pawn Movement Components 에는 설명할 만한 기능이 더 있지만, 이 튜토리얼 범위에는 필요치 않습니다. Floating Pawn Movement , Spectator Pawn Movement , Character Movement Component 같은 다른 클래스를 살펴보면 추가적인 용례나 개념을 찾아볼 수 있습니다.
Pawn Movement Component 작동방식을 정의했으니, 이제 모든 것을 커스텀 Pawn 클래스에 묶을 코드를 작성하면 됩니다.
작업중 코드
CollidingPawn.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/Pawn.h"
#include "CollidingPawn.generated.h"
UCLASS()
class HOWTO_COMPONENTS_API ACollidingPawn : public APawn
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
ACollidingPawn();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
UParticleSystemComponent* OurParticleSystem;
};
CollidingPawn.cpp
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "HowTo_Components.h"
#include "CollidingPawn.h"
// Sets default values
ACollidingPawn::ACollidingPawn()
{
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// Our root component will be a sphere that reacts to physics
USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
RootComponent = SphereComponent;
SphereComponent->InitSphereRadius(40.0f);
SphereComponent->SetCollisionProfileName(TEXT("Pawn"));
// Create and position a mesh component so we can see where our sphere is
UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
SphereVisual->SetupAttachment(RootComponent);
static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
if (SphereVisualAsset.Succeeded())
{
SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
SphereVisual->SetWorldScale3D(FVector(0.8f));
}
// Create a particle system that we can activate or deactivate
OurParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
OurParticleSystem->SetupAttachment(SphereVisual);
OurParticleSystem->bAutoActivate = false;
OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
if (ParticleAsset.Succeeded())
{
OurParticleSystem->SetTemplate(ParticleAsset.Object);
}
// Use a spring arm to give the camera smooth, natural-feeling motion.
USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
SpringArm->SetupAttachment(RootComponent);
SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);
SpringArm->TargetArmLength = 400.0f;
SpringArm->bEnableCameraLag = true;
SpringArm->CameraLagSpeed = 3.0f;
// Create a camera and attach to our spring arm
UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);
// Take control of the default player
AutoPossessPlayer = EAutoReceiveInput::Player0;
}
// Called when the game starts or when spawned
void ACollidingPawn::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ACollidingPawn::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
}
// Called to bind functionality to input
void ACollidingPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
Super::SetupPlayerInputComponent(InputComponent);
}
CollidingPawnMovementComponent.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/PawnMovementComponent.h"
#include "CollidingPawnMovementComponent.generated.h"
/**
*
*/
UCLASS()
class HOWTO_COMPONENTS_API UCollidingPawnMovementComponent : public UPawnMovementComponent
{
GENERATED_BODY()
public:
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
};
CollidingPawnMovementComponent.cpp
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "HowTo_Components.h"
#include "CollidingPawnMovementComponent.h"
void UCollidingPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// Make sure that everything is still valid, and that we are allowed to move.
if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
{
return;
}
// Get (and then clear) the movement vector that we set in ACollidingPawn::Tick
FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * 150.0f;
if (!DesiredMovementThisFrame.IsNearlyZero())
{
FHitResult Hit;
SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit);
// If we bumped into something, try to slide along it
if (Hit.IsValidBlockingHit())
{
SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
}
}
};