2020년 8월 30일 일요일

Unreal - Player 만들기

 1. Class 생성

VS Project 에서 평소에 하듯이 cpp, h 파일을 추가하면 intermediate 파일에 추가된다.

당연한게 Sin 말고 vcsproj 확장자 파일이 어디에 있는지 생각하면 Intermediate 어디인 것이다. 

괜한짓 하지말고 Unreal Editor 내의 New C++ Class 를 사용하자.

나아가 filename.generated.h 파일도 필요하기 때문이다. 


언리얼 클래스 공식문서

언리얼에서 기본으로 제공해주는 클래스가 있는데, 기본이 AActor 이다.

우리는 기본 메시, 기본 충돌체가 있는 Character 를 사용할 것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#pragma once
 
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Gladiator.generated.h"
 
UCLASS()
class BELLZ_API AGladiator : public ACharacter
{
    GENERATED_BODY()
 
public:
    // Sets default values for this character's properties
    AGladiator();
 
protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
 
public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;
 
    // Called to bind functionality to input
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
 
};
cs

위는 Character 클래스를 상속받았을 때 기본으로 해주는 것이다.

Character 클래스를 상속 받은 것이 Pawn 으로 얘는 입출력 부분이 구현되어있다.


1
2
3
4
5
6
7
// Sets default values
AGladiator::AGladiator()
{
     // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
 
}ick.bCanEverTick = true;
cs

생성자의 저 부분은 매우 중요하다. 

주석대로 틱마다 업데이트 할 필요 없으면 false 로 꼭 하자.


이 클래스는 컴파일 때 보통 적용되어 있고, ContentBrowser 에 들어올 때나, 인스턴스 생성시나 온갖때에 생성자가 호출된다.


 2. Class 기반 BluePrint

Class 는 보통 Blueprint 의 기반이 되고, 세세한 설정 등은 Blueprint 에서 처리한다.

아직 Class 도 다 만들지 않았지만, 일단 만들어보자.

그리고 생성한 블루프린트를 더블클릭 등으로 열어서 Full Blueprint Editor 로 들어가보자.

Inherited 된 Mesh 에서 원하는 캐릭터의 Skeleton 을 지정해주자.

그럼 위치랑 바라보는 방향이 다른데, Collision 저놈이랑 하늘색 화살표랑 맞게 조절하자.


3. 클래스를 시작 플레이어로


Player Start 저 녀석이 Play 를 할 때 우리가 스폰되는 지점이다.

그런데 시작하는 Actor 가 어떤 종류인지 어떻게 설정할까?


1
2
3
4
5
6
7
8
9
10
11
#include "ProjNameGameModeBase.h"
 
ProjNameGameModeBase::ProjNameGameModeBase()
{
    // set default pawn class to our Blueprinted character
    static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/MyFolder/Player"));
    if (PlayerPawnBPClass.Class != NULL)
    {
        DefaultPawnClass = PlayerPawnBPClass.Class;
    }
}
cs

모든 프로젝트는 [ProjName]GameModeBase 라는 클래스가 있다.

생성자는 구현이 안되어 있을 수도 있지만 위처럼 구현하면 된다.

위에서 만든, 그리고 만들 클래스를 상속한 블루프린트를 에디터에서 만든 후, 

위에서 경로만 바꿔서 쓰면 된다. 


그리고 보통은 자동으로 되어있는데, Project - Maps & Modes 에 들어가자.

그리고 Default Modes 의 설정한 모드와 Default GameMode가 같은지 확인해보자.


그리고 실행해보면 에디터모드랑 별 다른바가 없을 것이다.

아직 캐릭터에 아무것도 없어서 그렇다.


4. 클래스 수정 - 컴포넌트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public:
    //Camera boom positioning the camera behind the character
    //Camera 이동용
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    //멤버변수의 권한을 부여함
    class USpringArmComponent* CameraBoom;
 
    /** Follow camera */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    class UCameraComponent* FollowCamera;
 
    //The sprite used to draw effect, better and more controllable than using the HUD or Textures
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Effects, meta = (AllowPrivateAccess = "true"))
    class UPaperSpriteComponent* EffectSprite;
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
AMartin::AMartin()
{
     // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
 
    // Set Size for Collision Capsule
    // "Components/CapsuleComponent.h"
    GetCapsuleComponent()->InitCapsuleSize(42.f, 96.f);
 
    // Configure character movement
    // "GameFramework/CharacterMovementComponent.h"
    GetCharacterMovement()->bOrientRotationToMovement = false// Character moves in the direction of input...    
    GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate
    GetCharacterMovement()->JumpZVelocity = 600.f;
    GetCharacterMovement()->AirControl = 0.2f;
 
    // Create a camera boom (pulls in towards the player if there is a collision)
    // "Camera/CameraComponent.h"
    // "GameFramework/SpringArmComponent.h"
    CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
    CameraBoom->SetupAttachment(RootComponent);
    CameraBoom->TargetArmLength = 900.0f; // The camera follows at this distance behind the character    
    CameraBoom->bUsePawnControlRotation = false// Rotate the arm based on the controller
 
    // Create a follow camera
    FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
    FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
    FollowCamera->bUsePawnControlRotation = false// Camera does not rotate relative to arm
 
    // #include "PaperSpriteComponent.h"
    EffectSprite = CreateDefaultSubobject<UPaperSpriteComponent>(TEXT("ClawEffect"));
    EffectSprite->AttachToComponent(CameraBoom, FAttachmentTransformRules::KeepRelativeTransform);
}
cs

클래스의 헤더랑 생성자 구현부에 다음처럼 넣어준다.
지금 하는 것은 블루프린트의 Add Component 버튼을 눌러서 하는 것과 동일하다.
추가 헤더파일 Include 는 위 주석대로 해도 되는데, "Engine.h" 로 처리 가능하다.
외부모듈인 "PaperSpriteComponent.h" 는 에외.

근데 EffectSprite 를 쓸려면 추가 모듈이 필요하다.
Class 에 다른 모듈 Unreal Module 을 추가해서 사용하고 싶다면
ProjName.Build.cs 의 PublicDependencyModuleNames.AddRange() 부분에 모듈을 추가하자.

그리고 헤더파일을 Include 한다.

다시 에디터로 돌아가 빌드하면 캐릭터가 보일 것이다.
구도가 맘에 안들면 블루프린트에서 고치자. 

4. 클래스 수정 - 컨트롤

1
2
3
4
5
6
7
8
9
10
11
12
void AMartin::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
 
    //Set up gameplay key bindings 
    check(PlayerInputComponent);
 
    PlayerInputComponent->BindAction("Jump", IE_Pressed, this&ACharacter::Jump);
    PlayerInputComponent->BindAction("Jump", IE_Released, this&ACharacter::StopJumping);
    //PlayerInputComponent->BindAxis("MoveForward", this, &AMartin::MoveForward);
    //PlayerInputComponent->BindAxis("MoveRight", this, &AMartin::MoveRight);
}
cs

일단 점프만 구현해보자.

그런데 어떻게 구현되는건가?


위 스크린샷에서처럼 정한 Input 의 종류를 함수를 통해서 매핑하므로써 가능하다.

Input 은 버튼 등의 강도가 있느냐에 따라서 Axis, Action 으로 나뉘는데 함수도 그렇다.

BindAction에 넣을 멤버함수는 void() 형식이고, BindAxis 는 void(float) 이 되어야 한다.

일단 부모클래스의 점프만 들고와서 매핑하였다.


근데 꼭 캐릭터 조작에 사용할 필욘 없고, 함수 호출용으로 사용해도 된다. 사실 같은말임.


1
2
3
4
5
6
7
protected:
    //BluePrint Instance 에서 사용할거니까 BlueprintCallable 해야함
    UFUNCTION(BlueprintCallable, Category = "Player Actions")
    void MoveForward(float Value);
 
    UFUNCTION(BlueprintCallable, Category = "Player Actions")
    void MoveRight(float Value);
cs
1
2
3
4
5
6
7
8
9
10
11
12
13
void AMartin::MoveForward(float Value)
{
    if (Controller != NULL && (Value != 0.0f)) //&& IsControlable && !IsAttacking)
    {
        //find out which way is forward
        const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);
 
        //Get Forward Vector
        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
        AddMovementInput(Direction, Value);
    }
}
cs

이제저걸 추가하고 SetUpPlayerInputControl 의 주석을 풀어주자.

그럼 이제 점프뿐만 아니라 왔다갔다도 할 수 있게 된다.


혹시 안된다하면, 생성한 블루프린트를 Compile 하고 Save 도 하자.

앞으로도 꼭 해주자.

그냥 에디터랑 똑같은 상태라면 기본 Player 세팅의 오타를 확인하자.

다른 이유는 잘 모르겠다.

List