[Unreal] 17일차 - 폭발 이펙트 추가, 적 생성, 프로젝트 빌드, TPS 프로젝트 생성

2024. 8. 7. 02:33Unreal Engine

728x90
반응형

코드 파일 지우는 법

C++ 코드 파일을 지우려면 [Source] 폴더에 가서 헤더파일과 C++ 파일을 지운 후 [Generate Visual Studio project files] 눌러서 다시 솔루션 파일 만든다음 빌드하면 된다.

 

 

 


 

 

Migrate

[Content Browser]에서 필요한 요소만 뽑아서 저장하고 싶으면 [Asset Actions] - [Migrate...] 눌러서 저장하면 된다. 마치 Unity Package를 만드는 것처럼 의존되어 있는 요소도 함께 저장된다.

 

 


 

 

적 죽을 때 폭발 이펙트 C++로 만들기

 

CEnemy.h 헤더파일에 코드 추가

//폭발효과 공장
UPROPERTY(EditAnywhere, Category = "MySettings") //에디터에 노출
class UParticleSystem* bombFactory;

 

전체 코드

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CEnemy.generated.h"

UCLASS()
class SHOOTING_API ACEnemy : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ACEnemy();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

public:
	UPROPERTY(VisibleAnywhere)
	class UBoxComponent* BoxComp;
	UPROPERTY(VisibleAnywhere)
	class UStaticMeshComponent* BodyMesh;

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MySettings")
	float speed = 500;
	UPROPERTY()
	AActor* target;
	FVector dir;

	UFUNCTION()
	void OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

	//폭발효과 공장
	UPROPERTY(EditAnywhere, Category = "MySettings") //에디터에 노출
	class UParticleSystem* bombFactory;
};

 

 

빌드 후 BP_CEnemy를 열어서 [Details]에 mysettings를 검색하면 [Bomb Factory]를 볼 수 있다.

 

 

P_Explosion을 불러오도록 설정한다.

 

 

CEnemy.cpp 파일에서 코드 추가

//폭발 효과 재생
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), bombFactory, GetActorLocation(), FRotator(), true);

 

전체 코드

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


#include "CEnemy.h"
#include "Components/BoxComponent.h"
#include <Kismet/GameplayStatics.h>
#include "CPlayer.h"
#include <Kismet/KismetMathLibrary.h>

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

	BoxComp = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComp"));

	//BoxComp를 Root로 등록
	RootComponent = BoxComp;

	//Collision Profile 설정하기
	BoxComp->SetCollisionProfileName(TEXT("EnemyProfile"));

	//충돌 overlap 처리 이벤트 등록
	BoxComp->OnComponentBeginOverlap.AddDynamic(this, &ACEnemy::OnComponentBeginOverlap);

	//BodyMesh 그릇에 StaticMeshComponent를 만들어서 담으려고 한다.
	BodyMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BodyMesh"));
	BodyMesh->SetupAttachment(BoxComp);
	
	BodyMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);

	//사용할 Static Mesh 데이터를 읽어서 컴포넌트에 할당하기
	ConstructorHelpers::FObjectFinder<UStaticMesh> TempMesh(TEXT("/Script/Engine.StaticMesh'/Game/Drone/Drone_low.Drone_low'"));
	//로딩 성공하면 BodyMesh에 사용할 Static Mesh로 TempMesh를 넣는다.
	if (TempMesh.Succeeded())
	{
		BodyMesh->SetStaticMesh(TempMesh.Object);
		BodyMesh->SetRelativeLocation(FVector(50.0f, 0, 0));
		BodyMesh->SetRelativeRotation(FRotator(0, -90.0f, -90.0f));
	}
}

// Called when the game starts or when spawned
void ACEnemy::BeginPlay()
{
	Super::BeginPlay();
	
	//타겟 찾기
	target = UGameplayStatics::GetActorOfClass(GetWorld(), ACPlayer::StaticClass());

	//아래로 방향 설정
	dir = FVector(0, 0, -1);

	//타겟이 있고 30%의 확률로 타겟쪽으로
	int percent = FMath::RandRange(1, 10);
	if (IsValid(target) && percent <= 3)
	{
		dir = target->GetActorLocation() - GetActorLocation();
		dir.Normalize();
	}
	
	//타겟 방향으로 회전하기
	FRotator rot = UKismetMathLibrary::MakeRotFromXZ(GetActorForwardVector(), dir);
	SetActorRotation(rot);
}

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

	//이동
	FVector P0 = GetActorLocation(); //현재 위치 설정
	FVector vt = dir * speed * DeltaTime;
	FVector P = P0 + vt;
	SetActorLocation(P);
}

void ACEnemy::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	//폭발 효과 재생
	UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), bombFactory, GetActorLocation(), FRotator(), true);
	//상대편 파괴
	OtherActor->Destroy();
	//나 자신 파괴
	Destroy();
}

 

 

빌드 후 실행하면 잘 된다.

 

 


 

 

적을 계속 생성할 EnemyGOD를 C++로 만들기

EnemyGOD C++ Class 생성

 

 

 

 

CEnemyGOD.h 코드 추가

//일정시간에 한 번씩 적 생성
UPROPERTY(EditAnywhere, Category = "MySettings")
float createTime = 2.0f; //생성 시간
float currentTime = 0; //경과 시간
//적 공장
UPROPERTY(EditAnywhere, Category = "MySettings")
TSubclassOf<class ACEnemy> enemyFactory;

 

전체 코드

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CEnemyGOD.generated.h"

UCLASS()
class SHOOTING_API ACEnemyGOD : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ACEnemyGOD();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	//일정시간에 한 번씩 적 생성
	UPROPERTY(EditAnywhere, Category = "MySettings")
	float createTime = 2.0f; //생성 시간
	float currentTime = 0; //경과 시간
	//적 공장
	UPROPERTY(EditAnywhere, Category = "MySettings")
	TSubclassOf<class ACEnemy> enemyFactory;
};

 

 

빌드 후 C++을 블루프린트로 만들기. BP_CEnemyGOD 이름으로 생성했다.

 

 

[Details]에서 enemy를 검색하여 BP_CEnemy를 넣는다.

 

 

CEnemyGOD.cpp에 코드 추가

#include <CEnemy.h>
void ACEnemyGOD::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	//일정시간에 한 번씩 적 생성
	currentTime += DeltaTime;
	if (currentTime > createTime)
	{
		GetWorld()->SpawnActor<ACEnemy>(enemyFactory, GetActorLocation(), FRotator());
		currentTime = 0; //시간 초기화
	}
}

 

전체 코드

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


#include "CEnemyGOD.h"
#include <CEnemy.h>

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

}

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

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

	//일정시간에 한 번씩 적 생성
	currentTime += DeltaTime;
	if (currentTime > createTime)
	{
		GetWorld()->SpawnActor<ACEnemy>(enemyFactory, GetActorLocation(), FRotator());
		currentTime = 0; //시간 초기화
	}
}

 

 

빌드 후 Viewport에서 BP_CEnemyGOD를 드래그 앤 드랍. 위치 맞추면 잘 나온다.

 

 


 

 

배경 넣기

Plane 끌어다 놓고 Transform 설정.

 

 

이전에 만들어놨던 메테리얼을 놓고 Transform을 다시 세팅하면 잘 실행되는 것을 볼 수 있다.

 

 


 

 

프로젝트 빌드

[Platforms] - [Windows] - [Package Project]를 통해서 윈도우 빌드를 할 수 있다.

오류 없이 잘 실행되었다.

슈팅 프로젝트 끝.

 

 


 

 

TPS 프로젝트 생성

Blank, BluePrint 선택, 이름은 "KNUT_TPS"로 생성했다.

 

 

Ctrl + N으로 [Basic] 레벨을 생성한다. 이후 TPSMap 이름으로 레벨 저장.

 

 


 

 

캐릭터 콘텐츠 팩 가져오기

[Add] - [Add Feature or Content Pack..] 선택

 

 

[Third Person]을 선택해서 추가한다.

 

 

BluePrints 폴더를 생성하고 [Character] 블루프린트를 생성한다. 이름은 BP_Player

 

 

BP_Player에서 Hierarchy의 Mesh를 선택하고 SKM_Manny 마네킹을 오른쪽 [Details] - [Mesh]에 끌어 놓으면 형태를 볼 수 있다.

 

 

Transform을 잡아준다.

 

 

화살표 방향이 앞방향이기 때문에 Rotation도 수정한다.

 

 

[Animation] 부분을 보게 되면 [Animation Mode] - [Use Animation Asset]을 선택한 후 아래 [Anim to Play]에서 idle을 검색하여 MF_Idle을 적용해보았다.

 

 


 

 

캐릭터 이동하기

Project Settings의 [Engine] - [Input]을 보면 이미 많은 키가 세팅되어 있는 것을 볼 수 있다.

 

 

BP_Player의 Event Graph에서 아래의 노드 세팅을 한다. 앞뒤로 움직일 수 있도록 [Add Movement Input] - [World Direction]은 (1, 0, 0)으로 수정했다.

 

 

Viewport에 BP_Player 끌어다 놓고 possess 검색하여 [Auto Possess Player] - [Player 0]을 세팅한다.

 

 

좌우 움직임도 마찬가지로 노드를 세팅한다.

 

 

앞뒤좌우로 움직인다.

 

728x90
반응형