Choose your operating system:
Windows
macOS
Linux
이번 단계에서는 게임에 조준선 HUD 요소를 추가하여 조준할 수 있도록 하겠습니다.
조준선 애셋 임포트
시작하기에 앞서, 다음 링크에서 샘플 이미지를 다운로드하고 압축을 풀어주세요:
-
콘텐츠 브라우저의 파일창 안에 우클릭하여 애세 임포트 대화창을 엽니다.
-
/Game... 에 임포트 를 눌러 임포트 대화 상자를 엽니다.
-
crosshair.TGA 이미지 파일을 찾아 선택합니다.
-
열기 를 클릭하여 프로젝트에 이미지 파일 임포트를 시작합니다.
-
저장 버튼을 눌러 임포트된 메시를 저장합니다.
새 HUD 클래스 추가
-
파일 메뉴에서 새 C++ 클래스... 를 선택합니다.
-
부모 클래스 선택 메뉴가 열립니다. 스크롤해 내려가 HUD 를 부모 클래스로 선택하고 다음 을 클릭합니다.
-
새 클래스 이름은 "FPSHUD" 라 하고 생성 을 클릭합니다.
-
Solution Explorer 에서
FPSHUD
클래스 헤더 파일을 위치에서,FPSHUD.h
를 열고 다음 protected 변수를 추가합니다:protected: // 화면 중앙에 그려질 것입니다. UPROPERTY(EditDefaultsOnly) UTexture2D* CrosshairTexture;
-
다음 함수 선언을
FPSHUD.h
에 추가합니다:public: // HUD 에 대한 주요 드로 콜입니다. virtual void DrawHUD() override;
-
이제
FPSHUD.cpp
에DrawHUD
함수를 구현합니다:void AFPSHUD::DrawHUD() { Super::DrawHUD(); if (CrosshairTexture) { // 캔버스 중심을 찾습니다. FVector2D Center(Canvas->ClipX * 0.5f, Canvas->ClipY * 0.5f); // 텍스처 중심이 캔버스 중심에 맞도록 텍스처의 크기 절반 만큼 오프셋을 줍니다. FVector2D CrossHairDrawPosition(Center.X - (CrosshairTexture->GetSurfaceWidth() * 0.5f), Center.Y - (CrosshairTexture->GetSurfaceHeight() * 0.5f)); // 중심점에 조준선을 그립니다. FCanvasTileItem TileItem(CrossHairDrawPosition, CrosshairTexture->Resource, FLinearColor::White); TileItem.BlendMode = SE_BLEND_Translucent; Canvas->DrawItem(TileItem); } }
-
Visual Studio 에서
FPSHUD.h
와FPSHUD.cpp
를 저장합니다. -
Solution Explorer 에서 FPSProject 위치를 찾습니다.
-
FPSProject 에 우클릭하고 Build 를 선택하여 프로젝트를 컴파일합니다.
CPP HUD 클래스를 블루프린트로 확장
이제 CPP HUD 클래스를 블루프린트로 확장하기 좋은 시점입니다. C++ 클래스를 블루프린트로 확장하는 방법 관련 상세 정보는 C++ 와 블루프린트 문서 페이지를 참고하세요.
-
FPSHUD
클래스에 우클릭하여 C++ Class Actions 메뉴를 엽니다. -
FPSHUD 기반 블루프린트 클래스 생성 을 클릭하여 블루프린트 클래스 추가 대화창을 엽니다.
-
블루프린트 클래스 이름을 BP_FPSHUD 라 하고 Blueprints 폴더를 선택한 뒤 블루프린트 클래스 생성 버튼을 누릅니다.
-
이제 Blueprints 폴더 안에 BP_FPSHUD 라는 블루프린트 클래스가 새로 생성되어 있을 것입니다.
-
꼭 BP_FPSHUD 블루프린트를 저장한 뒤 블루프린트 에디터를 닫습니다.
기본 HUD 클래스 설정
-
편집 메뉴에서 프로젝트 세팅 을 클릭합니다.
-
프로젝트 세팅 탭 왼편의 프로젝트 제목줄 아래 맵 & 모드 를 선택합니다.
-
Default HUD (기본 HUD) 드롭다운 메뉴에서 BP_FPSHUD 를 선택합니다.
-
프로젝트 세팅 메뉴를 닫습니다.
-
뒤로 돌아가 BP_FPSHUD 블루프린트 에디터를 엽니다.
-
이제, 블루프린트 에디터의 FPSHUD 섹션에 위치한 드롭다운 메뉴를 클릭하고 조준선 텍스처를 선택합니다.
-
마지막으로, BP_FPSHUD 블루프린트를 저장하고 블루프린트 에디터를 닫습니다.
HUD 확인
-
레벨 에디터 툴바의 플레이 버튼을 클릭합니다. 새로 추가한 조준선으로 조준이 가능할 것입니다.
-
레벨 에디터의 중지 버튼을 클릭하여 에디터에서 플레이 (PIE) 모드를 빠져나옵니다.
완성 섹션 코드
FPSProjectile.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/Actor.h"
#include "FPSProjectile.generated.h"
UCLASS()
class FPSPROJECT_API AFPSProjectile : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties.
AFPSProjectile();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame.
virtual void Tick( float DeltaSeconds ) override;
// Sphere collision component.
UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
USphereComponent* CollisionComponent;
// Projectile movement component.
UPROPERTY(VisibleAnywhere, Category = Movement)
UProjectileMovementComponent* ProjectileMovementComponent;
// Function that initializes the projectile's velocity in the shoot direction.
void FireInDirection(const FVector& ShootDirection);
// Function that is called when the projectile hits something.
void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit);
};
FPSProjectile.cpp
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "FPSProject.h"
#include "FPSProjectile.h"
// Sets default values
AFPSProjectile::AFPSProjectile()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// Use a sphere as a simple collision representation.
CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
CollisionComponent->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);
// Set the sphere's collision radius.
CollisionComponent->InitSphereRadius(15.0f);
// Set the root component to be the collision component.
RootComponent = CollisionComponent;
// Use this component to drive this projectile's movement.
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;
// Die after 3 seconds.
InitialLifeSpan = 3.0f;
}
// Called when the game starts or when spawned.
void AFPSProjectile::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame.
void AFPSProjectile::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Function that initializes the projectile's velocity in the shoot direction.
void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
{
ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
}
// Function that is called when the projectile hits something.
void AFPSProjectile::OnHit(AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit)
{
if (OtherActor != this && OtherComponent->IsSimulatingPhysics())
{
OtherComponent->AddImpulseAtLocation(ProjectileMovementComponent->Velocity * 100.0f, Hit.ImpactPoint);
}
}
FPSCharacter.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/Character.h"
#include "FPSCharacter.generated.h"
UCLASS()
class FPSPROJECT_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 DeltaSeconds ) override;
// Called to bind functionality to input.
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
// Handles input for moving forward and backward.
UFUNCTION()
void MoveForward(float Value);
// Handles input for moving right and left.
UFUNCTION()
void MoveRight(float Value);
// Sets jump flag when key is pressed.
UFUNCTION()
void StartJump();
// Clears jump flag when key is released.
UFUNCTION()
void StopJump();
// Function that handles firing projectiles.
UFUNCTION()
void Fire();
// FPS camera.
UPROPERTY(VisibleAnywhere)
UCameraComponent* FPSCameraComponent;
// First-person mesh (arms), visible only to the owning player.
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
USkeletalMeshComponent* FPSMesh;
// Gun muzzle's offset from the camera location.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
FVector MuzzleOffset;
// Projectile class to spawn.
UPROPERTY(EditDefaultsOnly, Category = Projectile)
TSubclassOf<class AFPSProjectile> ProjectileClass;
};
FPSCharacter.cpp
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "FPSProject.h"
#include "FPSCharacter.h"
#include "FPSProjectile.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;
// Create a first person camera component.
FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
// Attach the camera component to our capsule component.
FPSCameraComponent->SetupAttachment(GetCapsuleComponent());
// Position the camera slightly above the eyes.
FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));
// Allow the pawn to control camera rotation.
FPSCameraComponent->bUsePawnControlRotation = true;
// Create a first person mesh component for the owning player.
FPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
// Only the owning player sees this mesh.
FPSMesh->SetOnlyOwnerSee(true);
// Attach the FPS mesh to the FPS camera.
FPSMesh->SetupAttachment(FPSCameraComponent);
// Disable some environmental shadowing to preserve the illusion of having a single mesh.
FPSMesh->bCastDynamicShadow = false;
FPSMesh->CastShadow = false;
// The owning player doesn't see the regular (third-person) body mesh.
GetMesh()->SetOwnerNoSee(true);
}
// Called when the game starts or when spawned.
void AFPSCharacter::BeginPlay()
{
Super::BeginPlay();
if (GEngine)
{
// Put up a debug message for five seconds. The -1 "Key" value (first argument) indicates that we will never need to update or refresh this message.
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);
// Set up "movement" bindings.
PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
// Set up "look" bindings.
PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);
// Set up "action" bindings.
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire);
}
void AFPSCharacter::MoveForward(float Value)
{
// Find out which way is "forward" and record that the player wants to move that way.
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
void AFPSCharacter::MoveRight(float Value)
{
// Find out which way is "right" and record that the player wants to move that way.
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
AddMovementInput(Direction, Value);
}
void AFPSCharacter::StartJump()
{
bPressedJump = true;
}
void AFPSCharacter::StopJump()
{
bPressedJump = false;
}
void AFPSCharacter::Fire()
{
// Attempt to fire a projectile.
if (ProjectileClass)
{
// Get the camera transform.
FVector CameraLocation;
FRotator CameraRotation;
GetActorEyesViewPoint(CameraLocation, CameraRotation);
// Transform MuzzleOffset from camera space to world space.
FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
FRotator MuzzleRotation = CameraRotation;
// Skew the aim to be slightly upwards.
MuzzleRotation.Pitch += 10.0f;
UWorld* World = GetWorld();
if (World)
{
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
SpawnParams.Instigator = Instigator;
// Spawn the projectile at the muzzle.
AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
if (Projectile)
{
// Set the projectile's initial trajectory.
FVector LaunchDirection = MuzzleRotation.Vector();
Projectile->FireInDirection(LaunchDirection);
}
}
}
}
FPSHUD.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/HUD.h"
#include "FPSHUD.generated.h"
/**
*
*/
UCLASS()
class FPSPROJECT_API AFPSHUD : public AHUD
{
GENERATED_BODY()
public:
// Primary draw call for the HUD.
virtual void DrawHUD() override;
protected:
// This will be drawn at the center of the screen.
UPROPERTY(EditDefaultsOnly)
UTexture2D* CrosshairTexture;
};
FPSHUD.cpp
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "FPSProject.h"
#include "FPSHUD.h"
void AFPSHUD::DrawHUD()
{
Super::DrawHUD();
if (CrosshairTexture)
{
// Find the center of our canvas.
FVector2D Center(Canvas->ClipX * 0.5f, Canvas->ClipY * 0.5f);
// Offset by half of the texture's dimensions so that the center of the texture aligns with the center of the Canvas.
FVector2D CrossHairDrawPosition(Center.X - (CrosshairTexture->GetSurfaceWidth() * 0.5f), Center.Y - (CrosshairTexture->GetSurfaceHeight() * 0.5f));
// Draw the crosshair at the centerpoint.
FCanvasTileItem TileItem(CrossHairDrawPosition, CrosshairTexture->Resource, FLinearColor::White);
TileItem.BlendMode = SE_BLEND_Translucent;
Canvas->DrawItem(TileItem);
}
}
축하합니다! 지금까지 배우신 내용은 다음과 같습니다:
✓ 게임에 프로젝타일 추가 ✓ 발사 구현 ✓ 프로젝타일 콜리전과 수명 구성 ✓ 프로젝타일이 월드와 상호작용하도록 만들기 ✓ 뷰포트에 조준선 추가
다음 섹션에서는 캐릭터 애니메이션 방법을 배워보도록 하겠습니다.