TopDown - Pangea 생성하기
Pangea프로젝트를 만들자 게임을 선택하고
게임프레임워크 클래스에 대해 설명한다.
모든 클래스는 UObject에서 파생된다.
씬컴포넌트는 위치를 갖는 여러가지 컴포넌트로 파생된다.
게임액터클래스 생성하기
AActor->APawn->ACharactor클래스가 있는데 게임레벨안에서 인스턴스화 하는 용도로 자주 사용된다.
APawn은 입력을 받을수 있고
ACharacter는 AnimationBlueprint를 이용할수 있다.
게임을 위해 ADefenseTower, AProjectile, APlayerAvater를 만들어볼 것이다.
ADefenseTower클래스 생성하기
디펜스 타워는 플레이어가 공격범위안에 들어오면 탄환을 발사해 플레이어를 공격한다.
디펜스 타워도 생명포인트를 갖고 플레이어가 타워를 공격하면 줄어들고 0이 되면 파괴된다.
C++클래스를 만들자 AActor를 부모라 하고 이름은 DefenseTower로 한다.
https://github.com/AcornPublishing/ue5-game-development
GitHub - AcornPublishing/ue5-game-development: C++ 스크립트를 활용한 언리얼 엔진 5 게임 개발 [C++ 기초부터
C++ 스크립트를 활용한 언리얼 엔진 5 게임 개발 [C++ 기초부터 실제 게임 개발까지, 언리얼 게임 개발의 모든 것] - AcornPublishing/ue5-game-development
github.com
Head와 CPP코드이다.
변수와
int HealthPoints = 100; //the tower's max health points
int ShellDefense = 2; //the tower's shell defense value
float AttackRange = 15.0f; //the tower's attack range
float ReloadInterval = 1.0f; //the tower's reload interval
int _HealthPoints; //the tower's current health points
float _ReloadCountingDown; //reload counting down
함수를 선언한다.
int GetHealthPoints(); //get current life point
bool IsDestroyed(); //check if the tower has been destroyed
bool CanFire(); //check if the tower can fire
void Fire(); //fire a project tile
void Hit(int damage); //process when the tower is hit
void DestroyProcess(); //process when the tower is destroyed
DefenseTower.h 전체코드
UCLASS(Blueprintable) 는 블루프린트 클래스를 만들게 해준다.
FORCEINCLUE은 주소에 기반해 함수를 호출하는 대신 컴파일러에 코드를 복사해 붙여 넣는 언리얼엔진 매크로다
const키워드를 함수 선언뒤에 붙여 함수가 내부 포인터나 참조를 통해 매개변수나 클래스 멤버를 수정하지 못하게 보장한다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "DefenseTower.generated.h"
UCLASS(Blueprintable)
class PANGAEA_API ADefenseTower : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ADefenseTower();
UPROPERTY(EditAnywhere, Category = "Tower Params")
int HealthPoints = 100; //the tower's max health points
UPROPERTY(EditAnywhere, Category = "Tower Params")
int ShellDefense = 2; //the tower's shell defense value
UPROPERTY(EditAnywhere, Category = "Tower Params")
float AttackRange = 15.0f; //the tower's attack range
UPROPERTY(EditAnywhere, Category = "Tower Params")
float ReloadInterval = 1.0f; //the tower's reload interval
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
int _HealthPoints; //the tower's current health points
float _ReloadCountingDown; //reload counting down
private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Tower Component", meta = (AllowPrivateAccess = "true"))
UBoxComponent* _BoxComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Tower Component", meta = (AllowPrivateAccess = "true"))
UStaticMeshComponent* _MeshComponent;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
FORCEINLINE UBoxComponent* GetBoxComponent() const { return _BoxComponent; }
FORCEINLINE UStaticMeshComponent* GetMeshComponent() const { return _MeshComponent; }
UFUNCTION(BlueprintCallable, Category = "Pangaea|Defense Tower", meta = (DisplayName = "GetHP"))
int GetHealthPoints(); //get current life point
UFUNCTION(BlueprintCallable, Category = "Pangaea|Defense Tower")
bool IsDestroyed(); //check if the tower has been destroyed
UFUNCTION(BlueprintCallable, Category = "Pangaea|Defense Tower")
bool CanFire(); //check if the tower can fire
void Fire(); //fire a project tile
void Hit(int damage); //process when the tower is hit
protected:
void DestroyProcess(); //process when the tower is destroyed
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "DefenseTower.h"
// Sets default values
ADefenseTower::ADefenseTower()
{
// 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;
_BoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("Box Collision"));
SetRootComponent(_BoxComponent);
_MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Static Mesh"));
_MeshComponent->SetupAttachment(_BoxComponent);
}
// Called when the game starts or when spawned
void ADefenseTower::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ADefenseTower::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
int ADefenseTower::GetHealthPoints()
{
return _HealthPoints;
}
bool ADefenseTower::IsDestroyed()
{
return (_HealthPoints > 0.0f);
}
bool ADefenseTower::CanFire()
{
return (_ReloadCountingDown <= 0.0f);
}
void ADefenseTower::Fire()
{
}
void ADefenseTower::Hit(int damage)
{
}
void ADefenseTower::DestroyProcess()
{
}
Projectile.h
속도, 라이프스팬, 데미지 3개의 변수를 정의했다.
float Speed = 100.0f; //the projectile's speed
float Lifespan = 5.0f; //the projectile's lifespan
float Damage = 10.0f; //the projectile's damage points
Projectile.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Projectile.generated.h"
UCLASS(Blueprintable)
class PANGEA_API AProjectile : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AProjectile();
UPROPERTY(EditAnywhere, Category = "Projectile Params")
float Speed = 100.0f; //the projectile's speed
UPROPERTY(EditAnywhere, Category = "Projectile Params")
float Lifespan = 5.0f; //the projectile's lifespan
UPROPERTY(EditAnywhere, Category = "Projectile Params")
float Damage = 10.0f; //the projectile's damage points
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Visual, meta = (AllowPrivateAccess = "true"))
UBoxComponent* _BoxComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Visual, meta = (AllowPrivateAccess = "true"))
UStaticMeshComponent* _MeshComponent;
float _LifeCountingDown;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
FORCEINLINE UBoxComponent* GetBoxComponent() const { return _BoxComponent; }
FORCEINLINE UStaticMeshComponent* GetMeshComponent() const { return _MeshComponent; }
};
Projectile.cpp // 아무것도 구현한게 없다.
// Fill out your copyright notice in the Description page of Project Settings.
#include "Projectile.h"
// Sets default values
AProjectile::AProjectile()
{
// 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;
_BoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("Box Collision"));
SetRootComponent(_BoxComponent);
_MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Static Mesh"));
_MeshComponent->SetupAttachment(_BoxComponent);
}
// Called when the game starts or when spawned
void AProjectile::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AProjectile::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
PlayerAvata.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "PlayerAvatar.generated.h"
UCLASS()
class PANGEA_API APlayerAvatar : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
APlayerAvatar();
UPROPERTY(EditAnywhere, Category = "PlayerAvatar Params")
int HealthPoints = 500; //the character's max health points
UPROPERTY(EditAnywhere, Category = "PlayerAvatar Params")
float Strength = 10; //the character's attack strength
UPROPERTY(EditAnywhere, Category = "PlayerAvatar Params")
float Armer = 3; //the character's defense armer
UPROPERTY(EditAnywhere, Category = "PlayerAvatar Params")
float AttackRange = 6.0f; //the character's attack range
UPROPERTY(EditAnywhere, Category = "PlayerAvatar Params")
float AttackInterval = 1.2f; //the character's attack invertal
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
int _HealthPoints;
float _AttackCountingDown;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
UFUNCTION(BlueprintCallable, Category = "Pangaea|PlayerCharacter", meta = (DisplayName = "Get HP"))
int GetHealthPoints(); //get current health points
UFUNCTION(BlueprintCallable, Category = "Pangaea|PlayerCharacter")
bool IsKilled(); //check if the character has been killed
UFUNCTION(BlueprintCallable, Category = "Pangaea|PlayerCharacter")
bool CanAttack(); //check if the character can attack
void Attack(); //attack
void Hit(int damage); //process when the character is hit
FORCEINLINE class UCameraComponent* GetCameraComponet() const { return _cameraComponent; }
FORCEINLINE class USpringArmComponent* GetSringArmComponet() const { return _springArmComponent; }
protected:
void DieProcess(); //process when the character is killed
private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", meta = (AllowPrivateAccess = "true"))
USpringArmComponent* _springArmComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", meta = (AllowPrivateAccess = "true"))
UCameraComponent* _cameraComponent;
};
PlayerAvatar.cpp
PlayeravataAnimInstance는 아직 정의하지 안았으므로 코멘트 쳐준다.
// Fill out your copyright notice in the Description page of Project Settings.
#include "PlayerAvatar.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "PlayerAvatarAnimInstance.h"
// Sets default values
APlayerAvatar::APlayerAvatar()
{
PrimaryActorTick.bCanEverTick = true;
// Don't rotate character to camera direction
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// Configure character movement
auto characterMovement = GetCharacterMovement();
characterMovement->bOrientRotationToMovement = true; // Rotate character to moving direction
characterMovement->RotationRate = FRotator(0.f, 640.f, 0.f);
characterMovement->bConstrainToPlane = true;
characterMovement->bSnapToPlaneAtStart = true;
// Create the camera spring arm
_springArmComponent = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
_springArmComponent->SetupAttachment(RootComponent); //Attach to the character root
_springArmComponent->SetUsingAbsoluteRotation(true); //Don't rotate the arm with the character
_springArmComponent->TargetArmLength = 800.f; //Set the arm's length
_springArmComponent->SetRelativeRotation(FRotator(-60.f, 0.f, 0.f));
//Set the arm's rotation (60 degree up)
_springArmComponent->bDoCollisionTest = false; //No collision test
// Create the camera
_cameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
_cameraComponent->SetupAttachment(_springArmComponent, USpringArmComponent::SocketName);
//Attach to the spring arm
_cameraComponent->bUsePawnControlRotation = false; //Camera rotation is not controllable
}
// Called when the game starts or when spawned
void APlayerAvatar::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void APlayerAvatar::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
auto animInst = Cast<UPlayerAvatarAnimInstance>(GetMesh()->GetAnimInstance());
animInst->Speed = GetCharacterMovement()->Velocity.Size2D();
}
// Called to bind functionality to input
void APlayerAvatar::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
}
int APlayerAvatar::GetHealthPoints()
{
return _HealthPoints;
}
bool APlayerAvatar::IsKilled()
{
return (_HealthPoints <= 0.0f);
}
bool APlayerAvatar::CanAttack()
{
return (_AttackCountingDown <= 0.0f);
}
void APlayerAvatar::Attack()
{
}
void APlayerAvatar::Hit(int damage)
{
}