3. 입력에 반응하는 C++ 코드 작성

입력으로 폰과 카메라를 제어하는 코드를 구현합니다.

Windows
MacOS
Linux
On this page
  1. 이제 우리 게임에 사용할 수 있는 입력 매핑이 생겼으니, 받은 데이터를 저장할 멤버 변수를 구성해 줍시다. 업데이트 도중, 이동 및 마우스 방향 축 값을 알아야 하며, 둘 다 2차원 벡터입니다. 줌인 또는 줌아웃 뷰로 이동중인지도 알아야 하며, 현재 그 두 상태의 어디쯤 와 있는지도 알아야 합니다. 그러기 위해서는 PawnWithCamera.h 의 클래스 정의에 다음 코드를 추가해 줘야 합니다:

    //입력 변수
    FVector2D MovementInput;
    FVector2D CameraInput;
    float ZoomFactor;
    bool bZoomingIn;
  2. 그런 다음 입력에 대한 기록을 유지할 함수를 만들어 줘야 하니, PawnWithCamera.h 의 클래스 정의에 다음의 코드줄 역시도 추가해 줍시다:

    //입력 함수
    void MoveForward(float AxisValue);
    void MoveRight(float AxisValue);
    void PitchCamera(float AxisValue);
    void YawCamera(float AxisValue);
    void ZoomIn();
    void ZoomOut();

    이제 PawnWithCamera.cpp 의 이 함수를 다음 코드로 채워주면 됩니다:

    //입력 함수
    void APawnWithCamera::MoveForward(float AxisValue)
    {
        MovementInput.X = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
    }
    
    void APawnWithCamera::MoveRight(float AxisValue)
    {
        MovementInput.Y = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
    }
    
    void APawnWithCamera::PitchCamera(float AxisValue)
    {
        CameraInput.Y = AxisValue;
    }
    
    void APawnWithCamera::YawCamera(float AxisValue)
    {
        CameraInput.X = AxisValue;
    }
    
    void APawnWithCamera::ZoomIn()
    {
        bZoomingIn = true;
    }
    
    void APawnWithCamera::ZoomOut()
    {
        bZoomingIn = false;
    }

    아직 ZoomFactor 관련 아무 작업도 하지 않았습니다. 그 변수는 PawnTick 함수 도중 업데이트될텐데, 그 값은 버튼 상태에 따라 계속해서 변하기 때문입니다.

  3. 입력 데이터를 저장할 코드가 생겼으니, 이제 언리얼 엔진 더러 그 코드 호출 시점을 알려주기만 하면 됩니다. Pawn 에 대한 입력 이벤트에 함수를 바인딩하는 것은, 다음과 같이 APawnWithCamera::SetupPlayerInputComponent 에 바인딩 코드를 추가해 주기만 하면 됩니다:

    //"ZoomIn" 에 대한 이벤트를 걸어줍니다
    InputComponent->BindAction("ZoomIn", IE_Pressed, this, &APawnWithCamera::ZoomIn);
    InputComponent->BindAction("ZoomIn", IE_Released, this, &APawnWithCamera::ZoomOut);
    
    //네 축에 대한 매 프레임 처리를 걸어줍니다
    InputComponent->BindAxis("MoveForward", this, &APawnWithCamera::MoveForward);
    InputComponent->BindAxis("MoveRight", this, &APawnWithCamera::MoveRight);
    InputComponent->BindAxis("CameraPitch", this, &APawnWithCamera::PitchCamera);
    InputComponent->BindAxis("CameraYaw", this, &APawnWithCamera::YawCamera);
  4. 마지막으로 Tick 함수에서 그 값을 사용하여 매 프레임 PawnCamera 를 업데이트하면 됩니다. PawnWithCamera.cppAPawnWithCamera::Tick 에 다음 코드 블록을 모두 추가하면 될 것입니다:

    //ZoomIn 버튼이 눌렸으면 줌인, 아니면 도로 줌아웃 시킵니다
    {
        if (bZoomingIn)
        {
            ZoomFactor += DeltaTime / 0.5f;         //0.5 초에 걸쳐 줌인
        }
        else
        {
            ZoomFactor -= DeltaTime / 0.25f;        //0.25 초에 걸쳐 줌아웃
        }
        ZoomFactor = FMath::Clamp<float>(ZoomFactor, 0.0f, 1.0f);
        //ZoomFactor 에 따라 스프링 암의 길이와 카메라의 시야 블렌딩
        OurCamera->FieldOfView = FMath::Lerp<float>(90.0f, 60.0f, ZoomFactor);
        OurCameraSpringArm->TargetArmLength = FMath::Lerp<float>(400.0f, 300.0f, ZoomFactor);
    }

    이 코드에서는 줌 인 / 아웃에 따라 0.5 초 / 0.25 초 시간, 90 도 / 60 도 시야, 400 유닛 / 300 유닛 거리 등 하드코딩된 값을 여럿 사용합니다. 이러한 값은 일반적으로 UPROPERTY(EditAnywhere) 를 붙여 에디터에 변수로 노출시키면 프로그래머가 아닌 사람도 변경할 수 있게 되고, 프로그래머도 코드 리컴파일 없이 그 값을 변경하거나, 에디터에서 게임 플레이 도중에도 변경할 수 있게 됩니다.

    //액터의 요를 회전, 붙어있는 카메라도 따라서 회전됩니다
    {
        FRotator NewRotation = GetActorRotation();
        NewRotation.Yaw += CameraInput.X;
        SetActorRotation(NewRotation);
    }
    
    //카메라의 피치를 회전하지만, 항상 아래를 보도록 제한시킵니다
    {
        FRotator NewRotation = OurCameraSpringArm->GetComponentRotation();
        NewRotation.Pitch = FMath::Clamp(NewRotation.Pitch + CameraInput.Y, -80.0f, -15.0f);
        OurCameraSpringArm->SetWorldRotation(NewRotation);
    }

    이 코드 블록은 Pawn 의 요를 마우스 X 축으로 직접 회전시키되, 카메라 시스템은 마우스 Y 축의 피치 변화에만 반응합니다. 액터 나 그 서브클래스를 회전시키면 실제로 루트 레벨의 컴포넌트 가 회전되어, 거기 붙어있는 모든 것에 간접적으로 영향을 끼칩니다.

    //"MoveX" 와 "MoveY" 축에 따라 이동을 처리합니다
    {
        if (!MovementInput.IsZero())
        {
            //이동 입력 축 값에 초당 100 유닛 스케일을 적용합니다
            MovementInput = MovementInput.SafeNormal() * 100.0f;
            FVector NewLocation = GetActorLocation();
            NewLocation += GetActorForwardVector() * MovementInput.X * DeltaTime;
            NewLocation += GetActorRightVector() * MovementInput.Y * DeltaTime;
            SetActorLocation(NewLocation);
        }
    }

    GetActorForwardVectorGetActorRightVector 를 사용하면 액터가 향하는 방향을 기준으로 이동하는 것이 가능합니다. 카메라가 액터와 같은 쪽을 향하고 있기 때문에, 전방 키가 항상 플레이어가 바라보는 것을 기준으로 앞이 되도록 해 줍니다.

  5. 코딩 작업이 끝났습니다. 이제 코드를 컴파일한 다음 콘텐츠 브라우저 에서 새로 만든 클래스를 끌어 언리얼 엔진 에디터의 레벨 에디터 창에 놓는 것으로 새 인스턴스를 만들 수 있습니다.

    ClassInContentBrowser.png

    스태틱 메시 나 다른 비주얼 컴포넌트 를 추가하든, 아니면 없이든 자유롭게 플레이해 보세요. 카메라가 레벨을 따라다니면서 부드럽게 가속되고 감속되지만, 회전은 약간 빡빡하고 즉각적인 느낌이 들 것입니다. SpringArmComponent 의 프로퍼티 몇 가지, 이를테면 "Camera Rotation Lag" (카메라 회전 랙)을 추가하거나 "Camera Lag" (카메라 랙)을 높이고 낮춰서 조작감에 어떤 영향을 끼치는지 살펴보세요.

    SpringArmValues.png

  6. 원뿔 메시를 회전시킨 최종 제품 모습은 다음과 같습니다:

    FinalScreen.png

완성 코드

PawnWithCamera.h

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "PawnWithCamera.generated.h"

UCLASS()
class HOWTO_PLAYERCAMERA_API APawnWithCamera : public APawn
{
    GENERATED_BODY()

public:
    // Sets default values for this pawn's properties
    APawnWithCamera();

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;

protected:
    UPROPERTY(EditAnywhere)
    USpringArmComponent* OurCameraSpringArm;
    UCameraComponent* OurCamera;

    //Input variables
    FVector2D MovementInput;
    FVector2D CameraInput;
    float ZoomFactor;
    bool bZoomingIn;

    //Input functions
    void MoveForward(float AxisValue);
    void MoveRight(float AxisValue);
    void PitchCamera(float AxisValue);
    void YawCamera(float AxisValue);
    void ZoomIn();
    void ZoomOut();
};

PawnWithCamera.cpp

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#include "HowTo_PlayerCamera.h"
#include "PawnWithCamera.h"

// Sets default values
APawnWithCamera::APawnWithCamera()
{
    // 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;

    //Create our components
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    OurCameraSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArm"));
    OurCameraSpringArm->SetupAttachment(RootComponent);
    OurCameraSpringArm->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), FRotator(-60.0f, 0.0f, 0.0f));
    OurCameraSpringArm->TargetArmLength = 400.f;
    OurCameraSpringArm->bEnableCameraLag = true;
    OurCameraSpringArm->CameraLagSpeed = 3.0f;
    OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("GameCamera"));
    OurCamera->SetupAttachment(OurCameraSpringArm, USpringArmComponent::SocketName);

    //Take control of the default Player
    AutoPossessPlayer = EAutoReceiveInput::Player0;
}

// Called when the game starts or when spawned
void APawnWithCamera::BeginPlay()
{
    Super::BeginPlay();

}

// Called every frame
void APawnWithCamera::Tick( float DeltaTime )
{
    Super::Tick(DeltaTime);

    //Zoom in if ZoomIn button is down, zoom back out if it's not
    {
        if (bZoomingIn)
        {
            ZoomFactor += DeltaTime / 0.5f;         //Zoom in over half a second
        }
        else
        {
            ZoomFactor -= DeltaTime / 0.25f;        //Zoom out over a quarter of a second
        }
        ZoomFactor = FMath::Clamp<float>(ZoomFactor, 0.0f, 1.0f);
        //Blend our camera's FOV and our SpringArm's length based on ZoomFactor
        OurCamera->FieldOfView = FMath::Lerp<float>(90.0f, 60.0f, ZoomFactor);
        OurCameraSpringArm->TargetArmLength = FMath::Lerp<float>(400.0f, 300.0f, ZoomFactor);
    }

    //Rotate our actor's yaw, which will turn our camera because we're attached to it
    {
        FRotator NewRotation = GetActorRotation();
        NewRotation.Yaw += CameraInput.X;
        SetActorRotation(NewRotation);
    }

    //Rotate our camera's pitch, but limit it so we're always looking downward
    {
        FRotator NewRotation = OurCameraSpringArm->GetComponentRotation();
        NewRotation.Pitch = FMath::Clamp(NewRotation.Pitch + CameraInput.Y, -80.0f, -15.0f);
        OurCameraSpringArm->SetWorldRotation(NewRotation);
    }

    //Handle movement based on our "MoveX" and "MoveY" axes
    {
        if (!MovementInput.IsZero())
        {
            //Scale our movement input axis values by 100 units per second
            MovementInput = MovementInput.SafeNormal() * 100.0f;
            FVector NewLocation = GetActorLocation();
            NewLocation += GetActorForwardVector() * MovementInput.X * DeltaTime;
            NewLocation += GetActorRightVector() * MovementInput.Y * DeltaTime;
            SetActorLocation(NewLocation);
        }
    }
}

// Called to bind functionality to input
void APawnWithCamera::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
    Super::SetupPlayerInputComponent(InputComponent);

    //Hook up events for "ZoomIn"
    InputComponent->BindAction("ZoomIn", IE_Pressed, this, &APawnWithCamera::ZoomIn);
    InputComponent->BindAction("ZoomIn", IE_Released, this, &APawnWithCamera::ZoomOut);

    //Hook up every-frame handling for our four axes
    InputComponent->BindAxis("MoveForward", this, &APawnWithCamera::MoveForward);
    InputComponent->BindAxis("MoveRight", this, &APawnWithCamera::MoveRight);
    InputComponent->BindAxis("CameraPitch", this, &APawnWithCamera::PitchCamera);
    InputComponent->BindAxis("CameraYaw", this, &APawnWithCamera::YawCamera);
}

//Input functions
void APawnWithCamera::MoveForward(float AxisValue)
{
    MovementInput.X = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}

void APawnWithCamera::MoveRight(float AxisValue)
{
    MovementInput.Y = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}

void APawnWithCamera::PitchCamera(float AxisValue)
{
    CameraInput.Y = AxisValue;
}

void APawnWithCamera::YawCamera(float AxisValue)
{
    CameraInput.X = AxisValue;
}

void APawnWithCamera::ZoomIn()
{
    bZoomingIn = true;
}

void APawnWithCamera::ZoomOut()
{
    bZoomingIn = false;
}
Select Skin
Light
Dark

Welcome to the new Unreal Engine 4 Documentation site!

We're working on lots of new features including a feedback system so you can tell us how we are doing. It's not quite ready for use in the wild yet, so head over to the Documentation Feedback forum to tell us about this page or call out any issues you are encountering in the meantime.

We'll be sure to let you know when the new system is up and running.

Post Feedback