[25. The Pawn Class]

 

// Critter.h
public:
    UPROPERTY(EditAnywhere)
    UStaticMeshComponent* MeshComponent;

    UPROPERTY(EditAnywhere)
    class UCameraComponent* Camera;

// Critter.cpp
#include "Camera/CameraComponent.h"

ACritter::ACritter()
{
    // 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;

    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));

    MeshComponent->SetupAttachment(GetRootComponent());

    Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
    Camera->SetupAttachment(GetRootComponent());
    Camera->SetRelativeLocation(FVector(-300.f, 0.f, 300.f));
    Camera->SetRelativeRotation(FRotator(-45.f, 0.f, 0.f));
}

 

C++ 클래스로 폰을 상속받아 만든 후 블루프린트 클래스로 만들어서 월드에 배치하면 액터를 배치했던 것 처럼 따로 조작하거나 해당 폰의 카메라로 보거나 하지 않는다.

게임 모드가 지정되지 않았기 때문이다.

 

Critter 클래스를 블루프린트 클래스로 만들어주고(Critter_BP) 스태틱 메시를 Shape_Cone으로 설정한다.

그리고 기본적으로 생성되어있던 FirstProjectGameModeBase 클래스를 블루프린트 클래스(CritterGameMode_BP)로 만들어준다.

 

 

Default Pawn Class만 Critter_BP로 바꿔주고 월드 세팅의 게임 모드를 방금 만든 CritterGameMode_BP로 변경해준다.

실행해보면 카메라가 살짝 뒤쪽에 배치되어 대각선으로 원뿔 오브젝트를 바라보게 된다.

 

[26. Pawn Movement Input #1]

 

Scale은 나중에 바인딩 될 함수에 반환되는 값이다.

프로젝트 세팅에서 바인딩을 지정해줬다면 이제 C++ 코드에서 진짜 바인딩을 해야한다.

 

// Critter.h
private:
    void MoveForward(float Value);
    void MoveRight(float Value);

// Critter.cpp
void ACritter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    
    // 프로젝트에 바인딩한 이름, 대상 객체, 대상 객체의 메소드 주소
    PlayerInputComponent->BindAxis(TEXT("MoveForward"), this, &ACritter::MoveForward);
    PlayerInputComponent->BindAxis(TEXT("MoveRight"), this, &ACritter::MoveRight);
}

 

여기까지는 바인딩만 한 것이라서 실행을 해도 아무런 변화가 없다.

 

 

// Critter.h
private:
    void MoveForward(float Value);
    void MoveRight(float Value);

    FVector CurrentVelocity;
    float MaxSpeed;
    
// Critter.cpp
ACritter::ACritter()
{
    CurrentVelocity = FVector(0.f);
    Maxspeed = 100.f
}

void ACritter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    FVector NewLocation = GetActorLocation() + (CurrentVelocity * DeltaTime);
    SetActorLocation(NewLocation);
}

void ACritter::MoveForward(float Value)
{
    // Clamp: 값이 최소, 최대값 범위를 넘으면 최소, 최대값을 반환해준다
    CurrentVelocity.X = FMath::Clamp(Value, -1.f, 1.f) * MaxSpeed;
}

void ACritter::MoveRight(float Value)
{
    CurrentVelocity.Y = FMath::Clamp(Value, -1.f, 1.f) * MaxSpeed;
}

 

이제 wasd 입력시 정상적으로 잘 움직인다.

하드 코딩이 아닌 에디터에서 편집하고 싶다면 maxSpeed를 public 영역으로 빼내고 UPROPERTY 매크로를 붙여서 에디터에서 편집할 수 있다.

 

[27. Pawn Movement Input #2]

 

시작에 앞서 Pawn을 상속받은 C++ 클래스인 Collider를 하나 만들어주자.

 

getter, setter 메소드는 기능이 아주 단순해서 클래스에서 정의와 선언을 한번에 해버리는 경우가 많다.

그런 경우 접두어로 FORCELINE을 붙여주면 된다. 단, 이경우 UFUNCTION과 같이 동작하지 않는다.

 

// Collider.h
UPROPERTY(VisibleAnywhere, Category = "Mesh")
UStaticMeshComponent* MeshComponent;

UPROPERTY(VisibleAnywhere, Category = "Mesh")
class USphereComponent* SphereComponent;

FORCEINLINE UStaticMeshComponent* GetMeshComponent() { return MeshComponent; }
FORCEINLINE void SetMeshComponent(UStaticMeshComponent* Mesh) { MeshComponent = Mesh; }

FORCEINLINE USphereComponent* GetSphereComponent() { return SphereComponent; }
FORCEINLINE void SetMeshComponent(USphereComponent* Sphere) { SphereComponent = Sphere; }

// Collider.cpp
#include "Components/SphereComponent.h"

ACollider::ACollider()
{
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));

    SphereComponent->SetupAttachment(GetRootComponent());
    SphereComponent->InitSphereRadius(40.f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));
    
    AutoPossessPlayer = EAutoReceiveInput::Player0;
}

 

구체 컴포넌트를 만들어서 루트 컴포넌트의 하위 계층에 붙인 뒤, 반지름을 정하고 콜리전 프리셋을 붙인다.

 

 

여기까지만 만들고 C++ 클래스를 Collider_BP 블루프린트 클래스로 만들어서 들어가보면 생각했던 구체는 아닌것이 그려져있다.

어차피 시각화를 위한 구체가 아닌 충돌을 위한 구체이므로 그려지지 않아도 상관없다.

 

Hidden In Game이 체크되어있으면 플레이시 보이지 않고 해제하면 보이게 된다.

Collider_BP를 월드에 배치해서 확인할 수 있다.

 

메시를 하드코딩해서 직접 설정할수도 있지만 일반적으로 좋은 생각은 아니다.

에디터에서 변경하는 것이 좋다.

 

// Collider.h
ACollider::ACollider()
{
    static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshComponentAsset(TEXT("StaticMesh'/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere'"));

    if (MeshComponentAsset.Succeeded())
    {
	MeshComponent->SetStaticMesh(MeshComponentAsset.Object);
	MeshComponent->SetRalativeLocation(FVector(0.f, 0.f, -40.f));
	MeshComponent->SetWorldScald3D(FVector(0.8f, 0.8f, 0.8f));
    }
}

 

만약 하드코딩을 한다면 이렇게 할 수 있다. ConstructorHelpers라는 이름에서 보다시피 생성자에서만 할 수 있다.

 

[28. Pawn Movement Input #3]

 

Collider에 스프링 암과 카메라를 붙여보도록 하자.

 

// Collider.h
UPROPERTY(VisibleAnywhere, Category = "Mesh")
class UCameraComponent* Camera;

UPROPERTY(VisibleAnywhere, Category = "Mesh")
class USpringArmComponent* SpringArm;

// Collider.cpp
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"

ACollider::ACollider()
{
	// .. 생략
    SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
    SpringArm->SetupAttachment(GetRootComponent());
    SpringArm->SetRelativeRotation(FRotator(-45.f, 0.f, 0.f));
    SpringArm->TargetArmLength = 400.f;
    SpringArm->bEnableCameraLag = true;
    SpringArm->CameraLagSpeed = 3.f;

    Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);
}

 

getter, setter도 만들어주자.

 

컴파일 후 블루프린트 클래스에 들어오면 카메라가 스프링 암에 잘 붙어있게된다.

 

 

 

 

 

'언리얼 엔진 > UE4 C++' 카테고리의 다른 글

[UE C++] The Character Class #1  (0) 2022.09.05
[UE C++] The Pawn Class #2  (0) 2022.09.05
[UE C++] The Actor Class #2  (0) 2022.09.04
[UE C++] The Actor Class #1  (0) 2022.09.03
[UE C++] Intro to Unreal Engine C++  (0) 2022.09.03

+ Recent posts