Unreal Engine and C++ (2)

프로젝트 구성

파일 생성
Maps 폴더 생성

Ctrl + S로 Map 저장
설정에 들어가서
저장한 Map을 시작 Map으로 저장
Visual Studio의 구성

Hello World

FPSGameModeBase.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "FPSGameModeBase.generated.h"

/**
*
*/
UCLASS()
class FPS_API AFPSGameModeBase : public AGameModeBase
{
GENERATED_BODY()

virtual void StartPlay() override;
};
  • virtual void StartPlay() override;는 AActor 클래스에서 상속된 StartPlay() 함수를 덮어써서 게임플레이가 시작되면 로그 메세지를 출력한다

FPSGameModeBase.Cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
// Fill out your copyright notice in the Description page of Project Settings.

#include "FPSGameModeBase.h"

void AFPSGameModeBase::StartPlay()
{
Super::StartPlay();

if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Hello World, this is FPSGameMode!"));
}
}

C++ 클래스를 블루프린트로 확장
저장
시작 모드 설정
성공

보다시피 여기서 삽질을 매우 많이했다… AFPSGameMode::StartPlay()AFPSGameModeBase::StartPlay()로 고치는 것을 유념한다…


캐릭터 임포트

새 캐릭터 클래스 선언
캐릭터 선택

캐릭터 작동 확인

FPSCharacter.cpp

1
2
3
4
5
6
7
8
9
10
void AFPSCharacter::BeginPlay()
{
Super::BeginPlay();

if(GEngine)
{
// 5 초간 디버그 메시지를 표시합니다. (첫 인수인) -1 "Key" 값은 이 메시지를 업데이트 또는 새로고칠 필요가 없음을 나타냅니다.
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
}
}

Build
캐릭터 클래스 블루프린트로 확장
캐릭터 기본 설정
캐릭터 클래스 작동 확인

W, A, S, D 조작


Input 설정

FPSCharacter.h

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
34
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "FPSCharacter.generated.h"

UCLASS()
class FPS_API AFPSCharacter : public ACharacter
{
GENERATED_BODY()

public:
// Sets default values for this character's properties
AFPSCharacter();

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;

UFUNCTION()
void MoveForward(float Value);

UFUNCTION()
void MoveRight(float value);
};

FPSCharacter.cpp

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Fill out your copyright notice in the Description page of Project Settings.

#include "FPSCharacter.h"

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

}

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

if(GEngine)
{
// 5 초간 디버그 메시지를 표시합니다. (첫 인수인) -1 "Key" 값은 이 메시지를 업데이트 또는 새로고칠 필요가 없음을 나타냅니다.
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
}
}

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

}

// Called to bind functionality to input
void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);

PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
}

void AFPSCharacter::MoveForward(float Value) {
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
AddMovementInput(Direction, Value);
}

void AFPSCharacter::MoveRight(float Value) {
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
AddMovementInput(Direction, Value);
}

꿀팁

1
2
#include "Engine.h" // 를 추가하면 GEngine관련 명령어에서 오류가 안난다
#include "Components/InputComponent.h" // PlayerInputComponent에서 오류를 없애줌

마우스 카메라

Input 설정

SetupPlayerInputComponent아래에 선언(FPSCharacter.cpp)

1
2
PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);

FPSCharacter.cpp

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Fill out your copyright notice in the Description page of Project Settings.

#include "FPSCharacter.h"

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

}

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

if(GEngine)
{
// 5 초간 디버그 메시지를 표시합니다. (첫 인수인) -1 "Key" 값은 이 메시지를 업데이트 또는 새로고칠 필요가 없음을 나타냅니다.
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
}
}

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

}

// Called to bind functionality to input
void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);

PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);
}

void AFPSCharacter::MoveForward(float Value) {
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
AddMovementInput(Direction, Value);
}

void AFPSCharacter::MoveRight(float Value) {
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
AddMovementInput(Direction, Value);
}

Jump 설정

Jump는 Action Mapping에서 설정한다

함수 선언(FPSCharacter.h)

1
2
3
4
5
UFUNCTION()
void StartJump();

UFUNCTION()
void StopJump();

함수 정의(FPSCharacter.cpp)

1
2
3
4
5
6
7
void AFPSCharacter::StartJump() {
bPressedJump = true;
}

void AFPSCharacter::StopJump() {
bPressedJump = false;
}

Jump 액션 바인딩(FPSCharacter.cpp의 SetupPlayerInputComponent)

1
2
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);

Mesh 추가

스켈레탈 메시 설정
그림자가 진다


발사체 구현

Fire Input 설정

발사체 클래스 정의

Actor Class 설정

발사체의 여러 Component 추가

  • USphere Component

FPSProjectile.h(FPSProjectile 인터페이스에 USphereComponent로 레퍼런스 추가)

1
2
UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
USphereComponent* CollisionComponent

FPSProjectile.cpp의 AFPSProjectile 생성자

1
2
3
4
5
6
// 구체를 단순 콜리전 표현으로 사용
CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
// 구체의 콜리전 반경을 설정
CollisionComponent->InitSphereRadius(15.0f);
// 루트 컴포넌트를 콜리전 컴포넌트로 설정
RootComponent = CollisionComponent;
  • Movement Component

FPSProjectile.h

1
2
UPROPERTY(VisibleAnywhere, Category = Movement)
UProjectileMovementComponent* ProjectileMovementComponent;

FPSProjectile.cpp의 AFPSProjectile 생성자

1
2
3
4
5
6
7
ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent"));
ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent);
ProjectileMovementComponent->InitialSpeed = 3000.0f;
ProjectileMovementComponent->MaxSpeed = 3000.0f;
ProjectileMovementComponent->bRotationFollowsVelocity = true;
ProjectileMovementComponent->bShouldBounce = true;
ProjectileMovementComponent->Bounciness = 0.3f;
  • Defining Initial Speed

FPSProjectile.h

1
void FireInDirection(const FVector& ShootDirection); // 빌사체의 속도를 발사 방향으로 초기화하는 함수

FPSProjectile.cpp

1
2
3
4
void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
{
ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
}
  • 발사 입력 액션 Binding

FPSCharacter.h

1
2
UFUNCTION()
void Fire(); // 함수 선언

FPSCharacter.cpp의 SetupPlayerInputComponent

1
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire); // Binding

FPSCharacter.cpp

1
2
3
void AFPSCharacter::Fire() // 함수 정의부
{
}
  • 발사체의 스폰 위치 정의

FPSCharacter.h

1
2
3
4
5
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay // BlueprintReadWrite를 통하여 블루프린트 내에서 총구 오프셋의 값을 구하고 설정할 수 있다
FVector Muzzleoffset; // 카메라 스페이스 오프셋 벡터를 사용해 스폰 위치 결정

UPROPERTY(EditDefaultsOnly, Category = Projectile) // EditDefaultsOnly는 클래스를 블루프린트의 디폴트로만 설정할 수 있다는 뜻
TSubclassOf<class AFPSProjectile> ProjectileClass;

발사체의 발사 구현

  • Fire 함수 구현

FPSCharacter.cpp

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
34
#include "FPSProjectile.h" // 헤더파일 추가

void AFPSCharacter::Fire() // 함수 정의
{
// 프로젝타일 발사를 시도합니다.
if (ProjectileClass)
{
// 카메라 트랜스폼을 구합니다.
FVector CameraLocation;
FRotator CameraRotation;
GetActorEyesViewPoint(CameraLocation, CameraRotation);

// MuzzleOffset 을 카메라 스페이스에서 월드 스페이스로 변환합니다.
FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
FRotator MuzzleRotation = CameraRotation;
// 조준을 약간 윗쪽으로 올려줍니다.
MuzzleRotation.Pitch += 10.0f;
UWorld* World = GetWorld();
if (World)
{
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
SpawnParams.Instigator = Instigator;
// 총구 위치에 발사체를 스폰시킵니다.
AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
if (Projectile)
{
// 발사 방향을 알아냅니다.
FVector LaunchDirection = MuzzleRotation.Vector();
Projectile->FireInDirection(LaunchDirection);
}
}
}
}

블루프린트로 정의
Component 추가
Character 설정
실행 결과

발사체 콜리전 및 수명 구성

새 오브젝트 채널 추가
새 프리셋 추가
새 콜리전 채널 세팅 사용(FPSProjcetile.cpp)

  • 수명 추가

FPSProjectile.cpp

1
InitialLifeSpan = 3.0f; // 3초 후 죽는다