www.udemy.com/course/unreal-engine-the-ultimate-game-developer-course/

 

Unreal Engine C++ The Ultimate Game Developer Course

Learn how to develop, code and package a complete video game in Unreal Engine

www.udemy.com

섹션 1: Introduction to the Course

1. Introduction

 


섹션 2: Download UE4, and Intro to the Engine

2. Installing Unreal Engine and the Unreal Community
3. The Viewport
4. Editor Overview
5. The Level Blueprint
6. Creating Blueprints

 

개요, 뷰포트

Blueprint is Unreal Engine's Visual scripting system.

레벨 블루프린트는 어느 프로젝트에든 하나는 존재한다.

All actors must have atleast one Scene componenet.

Static mesh의 Transform에서 Static / Stational / Movable이 있는데 이는 라이팅에 영향을 준다.

움직이지 않는 물체는 Baked Shadow가 가능하고, 당연히 Dynamic shadows는 비용이 더 비싸다.


섹션 3: Intro to Unreal Engine C++

7. C++ Refresher and UE4 Hierarchy

C++의 상속과 다형성, 캐스팅에 대해서 설명. 

Object <- Actor <- Pawn <- Character

Object : store data
Cannot be placed in the level

Actor :
Can be placed in the level
Can have a visual representation (Mesh)

Pawn : Can be possessed by a controller

Character :
have a character movement component
have functionality appropriate for a character

 

- Child 'is a' Parent, but not vice versa.

actor is a object, object is not an actor

pawn is an actor, but not a character

 

- Outer Class, Inner Class. Inner inner Class

Package has World, World has Level, Level has Actor, Actor has components

A Component is a Subobject of its owning Actor

Meshes and Textures are nested inside the Package, Don't refer to these as Subobject

 

 

8. Class Creation in Unreal Engine

언리얼의 다양한 계층구조의 선언을 직접 찾아볼 수 있다.

 

9. Reflection and Garbage Collection

언리얼에서 제공하는 런타임 검사 시스템 Reflection 

- C++ 데이터를 언리얼 에디터와 병합하고 블루프린트에 노출시킨다.

- Garbage Collection : 프로젝트와 관련된 메모리를 관리하고 자동적으로 사용되지 않는 자원을 제거

 

언리얼 엔진의 GC는 객체가 얼마나 참조되고 있는지 기록하여 더이상 참조되지 않으면 해당 객체를 제거한다.

어느 객체(클래스)를 Reflection이 관리하기 위해선 언리얼이 제공하는 매크로를 사용해야 해야 한다.

 

Reflection에 등록되면 블루프린트에서 사용할 수 있게된다.

프로젝트가 컴파일될때 UHT(Unreal Header Tool)가 해당 매크로를 이용한다.

클래스 : UCLASS()

멤버 : UPROPERTY()

함수 : UFUNCTION()


#include "MainCharacter.generated.h"

*.generated.h라는 헤더 또한 #include 되어야 하는데 reflection system이 클래스에 대한 정보를 수집하는 코드가 들어있다.

 

10. Creating a UObject

//블루프린트에서 사용할 수 있게 해주는 매크로
UCLASS(Blueprintable)
class FIRSTPROJECT_API UMyObject : public UObject
{
	GENERATED_BODY()
public:
	UMyObject();
    
    //변수용 매크로. 읽기 전용은 BlueprintReadOnly
	UPROPERTY(BlueprintReadWrite)
	float MyFloat;
    
    //함수용 매크로
	UFUNCTION(BlueprintCallable)
	void MyFunction();
	
};

 

11. Using UObject in Blueprints

UPROPERTY(BlueprintReadOnly, Category = " ")

UE_LOG(LogTemp, Warning, TEXT("  "));

 

블루프린트에서 오브젝트를 추가했다고 인스턴스가 생긴 것이 아니다.

마치 포인터처럼 레퍼런스일 뿐이므로 필요하다면 Construct Object from Class 로 오브젝트를 만들어 줘야 한다.


섹션 4: The Actor Class

12. Actors and Actor Components

Actor 기반 클래스에는 따로 Blueprintable을 붙일 필요가 없다.

Actor에 이미 달려있기 때문이다.

컴포넌트의 정의:

A component is a special type of UObject designed to be used as SubObject within actors.
this allows for parts of an actor to be swapped out to change its functionality

모든 액터는 기본적인 정보를 담는 Scene 컴포넌트가 있다.

Scene component는 액터 컴포넌트의 일종이고 Actor componenet 는 Scene component의 부모 클래스다.

Scene component는 형체를 가질 수 없지만 transform은 가질 수 있다.

 

Primitive componenet는 Scene component로부터 파생되어 다양한 지오메트리와 충돌정보를 가지고 있다.

Static mesh is a piece of geometry that consists of a static set of polygons.

 

DefaultSceneRoot에 큐브를 추가하면 큐브는 종속되어 SceneRoot의 위치를 따라다니게 된다.

우리가 원한다면 큐브가 Default Root가 되게할 수 있다. (cube is a scene component라서)

Scene component가 DefaultRoot일때는 무형 취급이라 'END'를 눌렀을때 중심점을 기준으로 스내핑 되지만

큐브가 Root가 되면 해당 기하정보를 토대로 표면에 스내핑 된다.

13. Position Vectors
14. The FVector

BeginPlay()에서 SetActorLocation()으로 위치를 설정하면

에디터상 위치와 게임 실행시의 위치가 달라질 수 있다.


15. FVector (continued)

EditInstanceOnly 프로퍼티 지정자를 사용하면 에디터에서 인스턴스별로 값을 설정해줄 수 있다.

말그대로 인스턴스 Only이기 때문에 default 값을 설정할 수 없다.

	//EditInstanceOnly는 생성된 인스턴스의 값을 설정할 수 있다.
	UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = "Floater Variables")
	FVector InitialLocation;

	//VisibleInstanceOnly는 생성된 인스턴스의 값을 보기만 할 수 있다.
	UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "Floater Variables")
	FVector PlacedLocation;

	//EditDefaultsOnly는 블루프린트의 패널에서만 원형의 값을 수정할 수 있다.
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Floater Variables")
	bool bInitializeFloaterLocations;


16. FVector (continued)
17. Intro to Collision

메쉬의 충돌 영역을 설정해줄 수 있다.

블루프린트에서의 AddForce

 


18. Collision (continued)
19. Sweeping

AddActorLocalOffset(InitialDirection, true, &HitResult);

의 2번째 인자는 sweep 옵션을 설정하는데 true라면 충돌을 방지한다.

Collision 탭에서 자세한 충돌 설정을 할 수 있다.

20. Local vs World Offset

AddActorLocalOffset / AddActorWorldOffset

AddActorLocalRotation / AddActorWorldRotation

 

21. Force and Torque

AddForce();
AddTorqueInDegrees();


22. Random Numbers

FMath::FRand()

FMath::FRandRange()


23. The Sine Function

//A : Amplitude (진폭)
//B : Period (주기)
//C : Phase Shift (수평 이동)
//D : Vertical Shift(수직 이동)
NewLocation.Z = BaseZLocation + A* FMath::Sin(B * RunningTime - C) + D;
 


24. Deleting Classes

C++클래스를 제거하려면 폴더에 가서 지우고 Binaries도 삭제한다음 UPROJECT로 다시 빌드하자


 

섹션 5: The Pawn Class

25. The Pawn Class

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

	MyMesh->SetupAttachment(RootComponent);

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

	AutoPossessPlayer = EAutoReceiveInput::Player0;


26. Pawn Movement Input

 

입력 컴포넌트에 폰을 바인딩 하는 방법. 함수를 인자로 받는걸로 보아 콜백으로 호출하나보다.

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

	PlayerInputComponent->BindAxis(TEXT("MoveForward"), this, &ACritter::MoveForward);
	PlayerInputComponent->BindAxis(TEXT("MoveRight"), this, &ACritter::MoveRight);

}
void ACritter::MoveForward(float Value)
{
	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;

}


27. Pawn Movement Input (continued)

 

폰에서 SphereComponent를 추가하고 메쉬를 하드코딩으로 생성하는 방법

ACollider::ACollider()
{
 	// 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("RootComponenet"));

	SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
	SphereComponent->SetupAttachment(GetRootComponent());
	SphereComponent->InitSphereRadius(40.f);
	SphereComponent->SetCollisionProfileName(TEXT("Pawn"));

	MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
	MeshComponent->SetupAttachment(GetRootComponent());

	static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshComponentAsset(TEXT("StaticMesh'/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere'"));
	if (MeshComponentAsset.Succeeded())
	{
		MeshComponent->SetStaticMesh(MeshComponentAsset.Object);
		MeshComponent->SetRelativeLocation({ 0.f,0.f,-40.f });
		MeshComponent->SetWorldScale3D({ 0.8f,0.8f,0.8f });
	}

}


28. Pawn Movement Input (continued)

	SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
	SpringArm->SetupAttachment(GetRootComponent());
	SpringArm->SetRelativeRotation({ -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);


29. Pawn Movement Component

MovementComponent를 직접 작성해본다.

void UColliderMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunc)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunc);

	if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
	{
		return;
	}

	FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.f);

	if (!DesiredMovementThisFrame.IsNearlyZero())
	{
		FHitResult Hit;
		SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit);

		if (Hit.IsValidBlockingHit())
		{
			SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
			UE_LOG(LogTemp, Warning, TEXT("Valid Blocking Hit"));
		}

	}
}

Character에서 무브먼트 컴포넌트를 제공하기에 폰에다 직접 만드는건 어디까지나 학습 목적이다.

UPawnMovementComponent를 상속하고 TickComponent( )를 오버라이딩하여 무브먼트 컴포넌트가 틱당 하는 일을  작성해볼수 있다.

폰에서 AddInputVector로 입력한 벡터들을 무브먼트 컴포넌트에서 ConsumeInputVector로 받아서

SafeMoveUpdatedComponent로 충돌 체크를 하고 Sweep이 켜진 상태로 충돌하면

SlideAlongSurface로 알맞는 충돌 움직임을 설정한다.

 

30. Pawn Movement Component (continued)
31. Pawn Camera Rotation

마우스 입력으로 액터와 암을 회전시켜서 카메라 회전

	FRotator NewRotation = GetActorRotation();
	NewRotation.Yaw += CameraInput.X;
	SetActorRotation(NewRotation);

	FRotator NewSpringArmRotation = SpringArm->GetComponentRotation();
	NewSpringArmRotation.Pitch = FMath::Clamp(NewSpringArmRotation.Pitch += CameraInput.Y, -80.f, -15.f);
	SpringArm->SetWorldRotation(NewSpringArmRotation);


32. Environment Assets

에셋을 Migrate하고 기본 레벨로 설정


섹션 6: The Character Class

33. Character Assets

www.mixamo.com/에서 에셋과 애니메이션을 다운받을 수 있다.

 

34. Character Assets (continued)

캐릭터 클래스는 스켈레톤메쉬, 충돌을 위한 캡슐, 무브먼트 컴포넌트를 내장하고 있고

캡슐 컴포넌트가 항상 루트여야 한다.


35. The Character Class
36. The Character Class (continued)
37. The Character Class (continued)

//컨트롤러의 각으로 매트릭스를 구성하여 움직일 방향벡터를 뽑아낸다
void AMain::MoveRight(float Value)
{
	if (Controller && (Value != 0.f))
	{
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0.f, Rotation.Yaw, 0.f);

		const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
		AddMovementInput(Direction, Value);
	}

}


38. The Character Class (continued)

	bUseControllerRotationYaw = false;
	bUseControllerRotationPitch = false;
	bUseControllerRotationRoll = false;

	// Configure character movement
	GetCharacterMovement()->bOrientRotationToMovement = true; // Characetr moves in the direction of input...
	GetCharacterMovement()->RotationRate = FRotator(0.f, 540.f, 0.f); // ... at the rotation rate
	GetCharacterMovement()->JumpZVelocity = 350.f;
	GetCharacterMovement()->AirControl = 0.2f;

캐릭터가 컨트롤러의 로테이터를 따라가는게 아닌 무브먼트의 로테이트를 따라가게 하여 스무스하게 회전/이동


39. The Animation Blueprint

void UMainAnimInstance::UpdateAnimationProperties()
{
	if (!Pawn)
		Pawn = TryGetPawnOwner();

	if(Pawn)
 	{
		FVector LateralSpeed = Pawn->GetVelocity();
		LateralSpeed.Z = 0.f;

		MovementSpeed = LateralSpeed.Size();
	
		bIsInAir = Pawn->GetMovementComponent()->IsFalling();
	}
}

Event Blueprint Update Animation과 연결할 Update 함수의 작성.

TryGetPawnOwner로 폰의 Z축을 뺀 횡 이동속도를 블렌딩에 사용할 속도로 설정하고 상태 전환을 위해 공중에 있는지의 정보를 받는다.


40. The Animation Blueprint (continued)

기본 애니메이션 BP를 만들고 Reparent로 직접 만든 C++ 애니인스턴스에서 상속되게 바꿀 수 있다.


41. The Animation Blueprint (continued)

AnimGraph에서 스테이트들을 만들어서 Transition Rule로 연결하면 끝


섹션 7: Gameplay Mechanics

42. Floor Switch

ECollision Enabeld::

PhysicsOnly : will not check for overlap events. just detects if something collides with it or something that is related to physics. so it's an optimization that will no longer allow functionality for overlap event. 

QueryAndPhysics : is the most expensive computationally. allow overlap event as well. 

QueryOnly : no Physics, overlap event only.

 

충돌을 감지하는 작은 스위치의 구현 내용.

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

	TriggerBox = CreateDefaultSubobject<UBoxComponent>(TEXT("TriggerBox"));
	RootComponent = TriggerBox;

	/** 트리거 박스의 충돌 대상 설정*/
	TriggerBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
	TriggerBox->SetCollisionObjectType(ECC_WorldStatic);
	TriggerBox->SetCollisionResponseToAllChannels(ECR_Ignore);
	TriggerBox->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
	TriggerBox->SetBoxExtent(FVector{ 62.f, 62.f, 32.f });

	FloorSwitch = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("FloorSwitch"));
	FloorSwitch->SetupAttachment(GetRootComponent());

	Door = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Door"));
	Door->SetupAttachment(GetRootComponent());

}

// Called when the game starts or when spawned
void AFloorSwitch::BeginPlay()
{
	Super::BeginPlay();
	
	TriggerBox->OnComponentBeginOverlap.AddDynamic(this, &AFloorSwitch::OnOverlapBein);
	TriggerBox->OnComponentEndOverlap.AddDynamic(this, &AFloorSwitch::OnOverlapEnd);
}

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

void AFloorSwitch::OnOverlapBein(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, 
				UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	UE_LOG(LogTemp, Warning, TEXT("Overlap Begin"));
}

void AFloorSwitch::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, 
				UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	UE_LOG(LogTemp, Warning, TEXT("Overlap End"));
}

BeginOverlap과 EndOverlap에 AddDynamic으로 델리게이트를 지정하는 모습인데

인자가 끔찍하게 길다.


43. Floor Switch (continued)

	/** 충돌시의 델리게이트로 사용될 함수로, 및의 이벤트 노드들을 호출해준다. */
	UFUNCTION()
	void OnOverlapBein(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

	UFUNCTION()
	void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

	/** 블루프린트의 이벤트 노드로 사용할 함수로 본체를 정의하면 안됨 */
	UFUNCTION(BlueprintImplementableEvent, Category = "Floor Switch")
	void RaiseDoor();

	UFUNCTION(BlueprintImplementableEvent, Category = "Floor Switch")
	void LowerDoor();

	UFUNCTION(BlueprintImplementableEvent, Category = "Floor Switch")
	void RaiseFloorSwitch();

	UFUNCTION(BlueprintImplementableEvent, Category = "Floor Switch")
	void LowerFloorSwitch();

	/** 블루프린트에서 호출될 함수로 문과 바닥의 움직임을 구현 */
	UFUNCTION(BlueprintCallable, Category = "Floor Switch")
	void UpdateDoorLocation(float Value);

	UFUNCTION(BlueprintCallable, Category = "Floor Switch")
	void UpdateFloorLocation(float Value);

타임라인으로 부드러운 느낌을 낼 수 있다.


44. Floor Switch (continued)

//타이머를 두어서 시간차로 문이 닫히게 한다.
void AFloorSwitch::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	UE_LOG(LogTemp, Warning, TEXT("Overlap End"));
	if (bCharactorOnSwitch) bCharactorOnSwitch = false;
	GetWorldTimerManager().SetTimer(SwitchHandle, this, &AFloorSwitch::CloseDoor, SwitchTime);
}


45. Spawn Volume

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Spawning")
	TSubclassOf<class ACritter> PawnToSpawn;

블루프린트에서 보라색(클래스)으로 사용할수 있는 변수

이런식으로 스포너를 만들수 있다.

46. Spawn Volume (continued)

같은 작업을 C++에서 해보자.

	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Spawning")
	void SpawnOurPawn(UClass* ToSpawn, const FVector& Location);

BlueprintNativeEvent를 사용하면 블루프린트에서 이벤트로 사용할 수 있고, 노드로도 사용가능하머, 'Add call to parent function'으로 C++로 구현한 함수를 사용하는것도 가능하다. 대신 함수이름에 _Implementation을 더해서 구현해야 하고 인텔리센스가 이상해지기도 한다.

void ASpawnVolume::SpawnOurPawn_Implementation(UClass* ToSpawn, const FVector& Location)
{
	if (ToSpawn)
	{
		UWorld* World = GetWorld();
		FActorSpawnParameters SpawnParams;

		if(World)
		{
			World->SpawnActor<ACritter>(ToSpawn, Location, FRotator(0.f));
		}
	}
}


47. Floating Platform

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

	if (bInterping)
	{
		FVector CurrentLocation = GetActorLocation();
		FVector Interp = FMath::VInterpTo(CurrentLocation, EndPoint, DeltaTime, InterpSpeed);
		SetActorLocation(Interp);

		float DistanceTraveled = (GetActorLocation() - StartPoint).Size();
		if (Distance - DistanceTraveled <= RemainDistance)
		{
			ToggleInterping();

			GetWorldTimerManager().SetTimer(InterpTimer, this, &AFloatingPlatform::ToggleInterping, InterpTime);
			SwapVector(StartPoint, EndPoint);
		}
	}
}

FMath의 보간 함수와 타이머로 부드러운 움직임을 구현하여 플랫폼이 왔다갔다 하게 만듬

 


48. Pickups
49. Pickups (continued)
50. Pickups (continued)

충돌기능을 갖춘 클래스 하나를 만들고 이를 상속해서 다양한 아이템들을 만들 수 있다.

파티클 시스템 컴포넌트는 파티클 시스템을 갖춘 컴포넌트이다.

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item | Particles")
	class UParticleSystemComponent* IdleParticlesComponent;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item | Particles")
	class UParticleSystem* OverlapParticels;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item | Sounds")
	class USoundCue* OverlapSound;

 


51. HUD (Heads Up Display)

UI는 보통 플레이어 컨트롤러에 넣는다고 한다. 게임중 캐릭터가 재생성되거나 바뀌는 일에 구여받지 않고 게임 내내 유지되기 때문이다.

	/** Reference to the UMG asset in the editor*/
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Widgets")
	TSubclassOf<class UUserWidget> HUDOverlayAsset;
	
	/** Variable to hold the widget after creating it*/
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Widgets")
	UUserWidget* HUDOverlay;
void AMainPlayerController::BeginPlay()
{
	Super::BeginPlay();

	if (HUDOverlayAsset)
	{
		HUDOverlay = CreateWidget<UUserWidget>(this, HUDOverlayAsset);
	}
	HUDOverlay->AddToViewport();
	HUDOverlay->SetVisibility(ESlateVisibility::Visible);
}

이런식으로 해두고 블루프린트에서 표시할 HUD를 선택하면 된다.


52. HUD (continued)
53. HUD (continued)
54. HUD (continued)

progressbar의 Percent에 함수를 바인딩한다.

55. HUD (continued)
56. HUD (continued)
57. HUD (continued)

UENUM(BlueprintType)
enum class EMovementStatus : uint8
{
	EMS_Normal	UMETA(DisplayName = "Normal"),
	EMS_Sprinting	UMETA(DisplayName = "Sprinting"),
	EMS_MAX		UMETA(DisplayName = "DefaultMax")
};

블루프린트에서 사용가능한 enum을 정의하는건 위와 같다.

 


58. Arrays and Debug Spheres

 

TArray와 DrawDebugSphere 등으로 시각적인 디버깅을 할 수 있다.

 

 

TArray<FVector> PickupLocations;

void AMain::ShowPickupLocation()
{
	for (int32 i = 0; i< PickupLocations.Num(); ++i)
		UKismetSystemLibrary::DrawDebugSphere(this, PickupLocations[i], 25.f, 12, FLinearColor::Red, 5.f, 2.f);
}