본문 바로가기

인생언리얼TPS

3.1-8 적Enemy생성하기

Character를 상속받아 C++클래스를 만듭니다. 이름은 Enemy로 합니다.

 

외관데이터 할당하기

ConstructorHelpers::FObjectFinder<>()를 사용해 하드코딩으로 메시를 불러옵니다.

Character를 상속받아 이미 스텔레탈메시컴포넌트가 있는데 GetMesh()로 접근합니다.

적에너미는 적마다 외형이 다르므로 하드코딩보다는 블루프린트를 만들어 지정하는게 유연합니다. 샘플스켈레탈메시도 삼인칭 메시를 사용하였습니다만 이또한 안하는게 편할수도 있습니다.

ThirdPersonTemplete의 SKM_Quinn을 Ctrl-C해서 붙여줍니다. 작은 따옴표 안에만 사용합니다.

책에서는 BP_Enemy 블루프린트 생성하여 외관을 붙여주고 있습니다.

블루프린트에서 메시를 붙여보면 위치가 위에 떠 있고 옆을 바로 보고 있어서 많은 적을 생성할때

GetMesh()->SetRelativeLocationAndRotation(FVector(0, 0, -88), FRotator(0, -90, 0));

로 미리 조정해주는게 편합니다.

ConstructorHelpers::FObjectFinder<USkeletalMesh> tempMesh(TEXT("/Game/Characters/Mannequins/Meshes/SKM_Quinn.SKM_Quinn"));
// 1-1. 데이터 로드 성공하면
if (tempMesh.Succeeded())
{
    //UE_LOG(LogTemp, Warning, TEXT("Enemy Skeletal Mesh Loading"));
    // 1-2. 데이터 할당
    GetMesh()->SetSkeletalMesh(tempMesh.Object);
    // 1-3. 메쉬 위치 및 회전 설정
    GetMesh()->SetRelativeLocationAndRotation(FVector(0, 0, -88), FRotator(0, -90, 0));
    // 1-4. 메쉬 크기 수정
    GetMesh()->SetRelativeScale3D(FVector(0.84f));
    GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}

 

다음 코드는 아직 구현안한 EnemyFSM및 HPBar Widget이 포함되어 있어 이것만으로는 컴파일이 안됩니다. 하시고 싶은분은 Widget부분을 제거해야 합니다.

Enemy.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

UCLASS()
class TPSPROJECT_API AEnemy : public ACharacter
{
	GENERATED_BODY()
	UPROPERTY(EditAnywhere)
	class USphereComponent* sphereComp;
public:
	// Sets default values for this character's properties
	AEnemy();

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;
public:
	// 적 AI 관리 컴포넌트 클래스
	UPROPERTY(VisibleAnywhere, Category = FSM)
	class ATPSPlayer* target;

	// 소유액터
	UPROPERTY()
	class AEnemy* me;
	// 적 AI 관리 컴포넌트 클래스
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = FSMComponent)
	class UEnemyFSM* fsm;
	UPROPERTY(EditAnywhere)
	TSubclassOf<class UHPBar> widgetRef;
	UPROPERTY()
	class UHPBar* hpBar;
	UPROPERTY()
	class UWidgetComponent* widgetComp;

};

Enemy.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Enemy.h"
#include "EnemyFSM.h"
#include "TPSPlayer.h"
#include <Components/CapsuleComponent.h>
#include <Components/SphereComponent.h>
#include <Components/WidgetComponent.h>
#include "Components/widget.h"
#include <Kismet/GameplayStatics.h>
#include <Kismet/KismetMathLibrary.h>
#include "HPBar.h"

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

	// 1. 스켈레탈메쉬 데이터 로드
	//
	ConstructorHelpers::FObjectFinder<USkeletalMesh> tempMesh(TEXT("/Game/Characters/Mannequins/Meshes/SKM_Quinn.SKM_Quinn"));
	// 1-1. 데이터 로드 성공하면
	if (tempMesh.Succeeded())
	{
		//UE_LOG(LogTemp, Warning, TEXT("Enemy Skeletal Mesh Loading"));
		// 1-2. 데이터 할당
		GetMesh()->SetSkeletalMesh(tempMesh.Object);
		// 1-3. 메쉬 위치 및 회전 설정
		GetMesh()->SetRelativeLocationAndRotation(FVector(0, 0, -88), FRotator(0, -90, 0));
		// 1-4. 메쉬 크기 수정
		GetMesh()->SetRelativeScale3D(FVector(0.84f));
		GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	}
	sphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("Collosion"));
	sphereComp->SetRelativeScale3D(FVector(3, 3, 3));
	sphereComp->SetCollisionProfileName(TEXT("EnemyHand"));
	GetCapsuleComponent()->SetCollisionProfileName(TEXT("Enemy"));
	GetMesh()->SetCollisionProfileName(TEXT("NoCollision"));
	// EnemyFSM 컴포넌트 추가
	fsm = CreateDefaultSubobject<UEnemyFSM>(TEXT("FSM"));
	if (fsm) {
		UE_LOG(LogTemp, Warning, TEXT("fsm"));
		//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf( TEXT("FSM: ") );// , fsm->GetFName()));
		//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red,  TEXT("FSM:") );
	// 애니메이션 블루프린트 할당하기
	}
	ConstructorHelpers::FClassFinder<UAnimInstance> tempClass(TEXT("/Game/Anim/ABP_Enemy.ABP_Enemy_C"));
	if (tempClass.Succeeded())
	{
		//UE_LOG(LogTemp, Warning, TEXT("ABP_Enemy Loading"));
		//GetMesh()->SetAnim
		GetMesh()->SetAnimInstanceClass(tempClass.Class);
	}
	widgetComp = CreateDefaultSubobject<UWidgetComponent>(TEXT("HPWidget"));
	widgetComp->SetupAttachment(GetMesh());
	widgetComp->SetRelativeLocation(FVector(0, 0, 300.0f));
	UClass* WidgetCompClass = LoadClass<UUserWidget>(NULL, TEXT("/Game/UI/WBP_HPEnemy.WBP_HPEnemy_C"));
	if (WidgetCompClass) {
		widgetComp->SetWidgetClass(WidgetCompClass);
		widgetComp->SetWidgetSpace(EWidgetSpace::Screen);
	}
	AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
}

// Called when the game starts or when spawned
void AEnemy::BeginPlay()
{
	Super::BeginPlay();
	auto actor = UGameplayStatics::GetActorOfClass(GetWorld(), ATPSPlayer::StaticClass());
	// ATPSPlayer 타입으로 캐스팅
	target = Cast<ATPSPlayer>(actor);
	// 소유객체 가져오기
	//me = Cast<ANPC>(GetOwner());
	me = this;
	widgetComp->InitWidget();
	hpBar = Cast<UHPBar>(widgetComp->GetUserWidgetObject());
	if (IsValid(hpBar)) {
		//FText idText = FText::FromString(FString::FromInt(12));
		FVector _pos = target->GetActorLocation();
		hpBar->ctextId = GetClass()->GetDisplayNameText();
		hpBar->cInfo = FText::FromString(FString::Printf(TEXT("IDLE")));
		//hpBar->ctextPos = FText::FromString(FString::Printf(TEXT("%f %f %f"), _pos.X, _pos.Y, _pos.Z));
		hpBar->cHP = 10;
		hpBar->cMaxHP = 10;
		hpBar->cMP = 100;
		hpBar->cMaxMP = 100;

		/*widget->SetTextID(GetClass()->GetDisplayNameText());
		FVector _pos = target->GetActorLocation();
		widget->SetTextPosition(FText::FromString(FString::Printf(TEXT("%f %f %f"), _pos.X, _pos.Y, _pos.Z)));
		*/
	}
}

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

}

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

}