[Unreal] 16일차 - C++ 적 생성, 콜라이더 설정, 적 이동, 적이 타겟으로 회전, 충돌 처리

2024. 8. 3. 18:24Unreal Engine

728x90
반응형

UClass : 이 클래스에 메타데이터를 추가적으로 붙여서 새롭게 만들어진 것. 객체 지향에서 Reflection을 사용할 수 있게 해줌.

CDO : 초기값을 미리 설정해놓고 가지고 있다가 복사해서 사용함.

 

 


 

 

적 생성 위한 C++ 만들기

Actor로 "CEnemy" 이름으로 생성

 

 

바로 블루프린트로도 만들기

 

 

이전에 블루프린트로 만들었던 BP_Enemy의 속성들을 참고하여 C++로 만들어본다.

 

 


 

 

CEnemy.h 수정

// 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;
};

 

 

CEnemy.cpp 수정

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


#include "CEnemy.h"
#include "Components/BoxComponent.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;

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

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

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

}

 

 

잘 설정되었다. 에디터를 껐다 켜는게 가장 좋은 방법인 것 같다.

 

 


 

 

회전값, 위치값, 모델링 설정

블루프린트에서 설정되어있는 설정 값들을 C++로 만든다.

 

 

CEnemy.cpp 코드 추가

//사용할 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));
}

 

전체 코드

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


#include "CEnemy.h"
#include "Components/BoxComponent.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;

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

	//사용할 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();
	
}

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

}

 

 

빌드 후 에디터를 껐다 켜면 잘 적용된 것을 볼 수 있다.

 

 


 

 

콜라이더 설정하기

콜라이더는 EnemyProfile, BodyMesh는 NoCollision 설정

CEnemy.cpp 코드 추가

//Collision Profile 설정하기
BoxComp->SetCollisionProfileName(TEXT("EnemyProfile"));
BodyMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);

 

전체 코드

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


#include "CEnemy.h"
#include "Components/BoxComponent.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"));

	//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();
	
}

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

}

 

 

잘 설정되었다.

 

 


 

 

적 이동

Target에 Actor를 담을 수 있도록 설정

 

 

CEnemy.h 코드 추가

UPROPERTY()
AActor* target;

 

전체 코드

// 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;
};

 

 

CEnemy.cpp 코드 추가

#include <Kismet/GameplayStatics.h>
#include "CPlayer.h"
//타겟 찾기
target = UGameplayStatics::GetActorOfClass(GetWorld(), ACPlayer::StaticClass());

UGameplayStatics는 유틸리티 클래스로써 사용하기 편하다.

 

전체 코드

// 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"

// 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"));

	//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());
}

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

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

 

 


 

 

적의 방향을 아래로 설정

CEnemy.h 코드 추가

- dir을 전역 변수로 설정

FVector dir;

 

전체 코드

// 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;
};

 

 

CEnemy.cpp 코드 변경

- 전역 변수를 받아오도록 변경

void ACEnemy::BeginPlay()
{
	Super::BeginPlay();
	
	//타겟 찾기
	target = UGameplayStatics::GetActorOfClass(GetWorld(), ACPlayer::StaticClass());

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

// 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);
}

 

전체 코드

// 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"

// 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"));

	//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);
}

// 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);
}

 

 

빌드 후 BP_CEnemy를 끌어다 놓고 Transform 설정하고 실행하면 아래로 움직이는 것을 볼 수 있다.

 

 


 

 

적이 Target쪽으로 움직이게 하기

CEnemy.cpp 코드 추가

//타겟 쪽으로 이동하도록 설정
dir = target->GetActorLocation() - GetActorLocation();
dir.Normalize();

 

전체 코드

// 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"

// 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"));

	//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);

	//타겟 쪽으로 이동하도록 설정
	dir = target->GetActorLocation() - GetActorLocation();
	dir.Normalize();
}

// 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);
}

 

 

빌드 후 실행하면 첫 방향 그대로 쭉 이동한다.

 

 


 

 

플레이어가 죽었을 때는 아래쪽으로 움직이기

CEnemy.cpp 코드 추가 및 수정

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

//타겟이 있을 때는 타겟쪽으로
if (IsValid(target))
{
	dir = target->GetActorLocation() - GetActorLocation();
	dir.Normalize();
}

 

전체 코드

// 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"

// 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"));

	//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);
    
	//타겟이 있을 때는 타겟쪽으로
	if (IsValid(target))
	{
		dir = target->GetActorLocation() - GetActorLocation();
		dir.Normalize();
	}
}

// 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);
}

 

 


 

 

30% 확률로 아래로 향하기

CEnemy.cpp 코드 추가 및 수정

//타겟이 있고 30%의 확률로 타겟쪽으로
int percent = FMath::RandRange(1, 10);
if (IsValid(target) && percent <= 3)
{
	dir = target->GetActorLocation() - GetActorLocation();
	dir.Normalize();
}

 

전체 코드

// 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"

// 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"));

	//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();
	}
}

// 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);
}

 

 


 

 

플레이어 방향을 보면서 이동하기

 

CEnemy.cpp 코드 추가

#include <Kismet/KismetMathLibrary.h>
//타겟 방향으로 회전하기
FRotator rot = UKismetMathLibrary::MakeRotFromXZ(GetActorForwardVector(), dir);
SetActorRotation(rot);

 

전체 코드

// 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"));

	//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);
}

 

 

플레이어 방향으로 돌려서 오는 것을 볼 수 있다.

 

 


 

 

충돌 처리 하기

 

Enemy가 Player나 Bullet과 충돌하도록 설정

 

CEnemy.h 코드 추가

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

OnComponentBeginOverlap에서 F12 눌러서 나온 코드 줄을 수정했다.

그리고 Ctrl + . 을 눌러서 cpp에 정의를 생성했다.

 

전체 코드

// 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;

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

 

 

CEnemy.cpp 코드 추가

	//충돌 overlap 처리 이벤트 등록
	BoxComp->OnComponentBeginOverlap.AddDynamic(this, &ACEnemy::OnComponentBeginOverlap);
void ACEnemy::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	//상대편 파괴
	OtherActor->Destroy();
	//나 자신 파괴
	Destroy();
}

 

전체 코드

// 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)
{
	//상대편 파괴
	OtherActor->Destroy();
	//나 자신 파괴
	Destroy();
}

 

 

빌드 후 실행해도 파괴가 되지 않는다.

블루프린트에서는 우리가 만든 함수를 읽을 수 없기 때문에

CEnemy.h 헤더 파일에서 UFUNCTION()을 추가한다.

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

 

 

빌드 후 에디터 껐다 켜고 실행하니 잘 된다.

728x90
반응형