일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 미니프로젝트
- 마인크래프트뮤지컬
- 프린세스메이커
- Ajax
- 레베카
- 언리얼프로그래머
- Express
- 으
- 게임개발
- 디자드
- 알고풀자
- Unseen
- EnhancedInput
- flask
- VUE
- node
- 데이터베이스
- 스터디
- 언리얼
- 스마일게이트
- JWT
- 언리얼뮤지컬
- 정글사관학교
- R
- Bootstrap4
- 파이썬서버
- Jinja2
- 카렌
- Enhanced Input System
- 프메
- Today
- Total
Showing
[언리얼] Unreal C++로 Player를 쫓아오는 Enemy 구현 본문
구현 사항
-1- 플레이어가 상하좌우로 움직인다
-2- emeny는 플레이어를 쫓아다니면서 총알을 발사한다.
위의 구현사항에서 emeny는 실시간으로 변경하는 플레이어의 방향으로 움직이고, enemy의 정면은 플레이어를 향하여 총알을 발사하면 플레이어에게 가도록 작성하여야 한다.
따라서, enemy의 위치값 dir과 방향값 rot이 tick 함수 안에서 적절하게 변경되어야 한다.
구현 코드
EnemyActor.cpp
for문 TActorIterator<APlayerPawn>을 통해 target을 찾는 것은 비용이 드므로 Tick이 아닌 Begin에서 단 한번 해주도록 한다.
Tick에서는 begin에서 구한 target의 Position과 Rotation을 이용해 enemy가 취해야 할 dir과 rot 벡터를 구해준다.
move에서 최종적으로 dir과 for을 enemy 액터에게 적용시켜준다.
#include "EnemyActor.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "EngineUtils.h"
#include "PlayerPawn.h"
#include "Components/ArrowComponent.h"
#include "Bullet.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"
AEnemyActor::AEnemyActor()
{
PrimaryActorTick.bCanEverTick = true;
boxComp = CreateDefaultSubobject<UBoxComponent>(TEXT("My Box Component"));
SetRootComponent(boxComp);
meshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("My Static Mesh"));
meshComp->SetupAttachment(boxComp);
FVector boxSize = FVector(50.0f, 50.0f, 50.0f);
boxComp->SetBoxExtent(boxSize);
firePosition = CreateDefaultSubobject<UArrowComponent>(TEXT("FirePos"));
firePosition->SetupAttachment(meshComp);
fireTimerTime = 0;
fireCoolTime = 2.0f;
fireReady = false;
}
void AEnemyActor::BeginPlay()
{
Super::BeginPlay();
// 플레이 중에 실시간으로 플레이어 폰을 인식하려면 월드 공간에 배치된 전체 액터 중에서 플레이어를 검색
for (TActorIterator<APlayerPawn> player(GetWorld()); player; ++player) {
if (player->GetName().Contains(TEXT("BP_PlayerPawn"))) {
target = *player;
}
}
}
// Called every frame
void AEnemyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// setting dir and rot
dir = target->GetActorLocation() - GetActorLocation();
dir.Normalize();
rot = UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), target->GetActorLocation());
Move(DeltaTime);
if (fireReady == false) {
FireCoolTimer(fireCoolTime, DeltaTime);
}
else {
Fire();
}
}
void AEnemyActor::Fire()
{
ABullet* bullet = GetWorld()->SpawnActor<ABullet>(bulletFactory, firePosition->GetComponentLocation()
, firePosition->GetComponentRotation());
fireReady = false;
// fireSound : 월드(어느 월드), 소리낼 사운드, 소리 날 위치
UGameplayStatics::PlaySoundAtLocation(GetWorld(), fireSound, firePosition->GetComponentLocation());
}
void AEnemyActor::FireCoolTimer(float cooltime, float deltaTime)
{
if (fireTimerTime < cooltime) {
fireTimerTime += deltaTime;
}
else
{
fireTimerTime = 0;
fireReady = true;
}
}
void AEnemyActor::Move(float deltaTime)
{
FVector newLocation = GetActorLocation() + dir * moveSpeed * deltaTime;
SetActorLocation(newLocation);
SetActorRotation(rot);
}
EnemyActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "EnemyActor.generated.h"
UCLASS()
class GIT_TEMP_API AEnemyActor : public AActor
{
GENERATED_BODY()
public:
AEnemyActor();
void FireCoolTimer(float cooltime, float deltaTime);
void Move(float DeltaTime);
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
UPROPERTY(EditAnywhere)
class UBoxComponent* boxComp;
UPROPERTY(EditAnywhere)
class UStaticMeshComponent* meshComp;
UPROPERTY(EditAnywhere)
int32 traceRate = 50;
UPROPERTY(EditAnywhere)
float moveSpeed = 100;
UPROPERTY(EditAnywhere)
class UArrowComponent* firePosition;
UPROPERTY(EditAnywhere)
TSubclassOf<class ABullet> bulletFactory;
UPROPERTY(EditAnywhere)
class USoundBase* fireSound;
UPROPERTY(EditAnyWhere)
float fireCoolTime = 2.0f;
UPROPERTY(EditAnywhere)
class APlayerPawn* target;
private:
FVector dir;
FRotator rot;
void Fire();
float fireTimerTime; // 타이머의 시간
bool fireReady; // 타이머 도달 여부
};
필요했던 문법
Iterator
월드 공간에 배치된 모든 액터를 검색하려면, 월드의 모든 액터를 대상으로 처음부터 끝까지 순환해서 조사해보면서 조사중인 액터가 플레이어 폰 클래스인지 알아내야 한다.
월드 공간에 생성되어 있는 모든 액터를 검색하는 반복문은 Iterator
for (TActorIterator<APlayerPawn> player(GetWorld()); player; ++player) {
if (player->GetName().Contains(TEXT("BP_PlayerPawn"))) {
dir = player->GetActorLocation() - GetActorLocation();
dir.Normalize();
}
}
dir = GetActorForwardVector();
UKismetMathLibrary::FindLookAtRotation
Find Look at Rotation in C++ - Development / Programming & Scripting - Epic Developer Community Forums (unrealengine.com) 해당 질문과 그에 달린 답변들을 참고하였다.
기타 참고 사이트
유니티 개발자를 위한 언리얼 엔진 4 | 언리얼 엔진 문서 (unrealengine.com)
UKismetMathLibrary::FindLookAtRotation | Unreal Engine Documentation
'Unreal' 카테고리의 다른 글
[Unreal] 언리얼 좌표계 Pitch, Roll, Yaw 이해와 짐벌락 이슈 해결 (0) | 2023.10.23 |
---|---|
[Unreal] Meta Human 제작 후 언리얼 3D 캐릭터에 입히기 (0) | 2023.10.20 |
「Unreal」 vr painting 프로젝트 사전조사 (1) | 2023.10.18 |
[Unreal] 언리얼 git 세팅 (0) | 2023.10.04 |
[언리얼] p = p0 + vt 이동로직 블루프린트와 c++로 작성하기 (0) | 2023.09.26 |