๐ Project Galaga ์์ํฉ๋๋ค.
Unreal C++๊ณผ ๊ฒ์์ํ์ ์ ๋ชฉํด์ ํ๋ก์ ํธ๋ฅผ ์งํํ์ต๋๋ค.
2024.05.04 ~ 2024.05.21์ ๊ฐ๋ฐ ๊ธฐ๊ฐ์ผ๋ก, ์ด 17์ผ ๋์ ๊ฐ๋ฐ์ ์๋ฃํ์์ต๋๋ค.
๊ฑฐ์ฐฝํ๊ฒ ์์ํ ํ๋ก์ ํธ๋ณด๋จ ์ง๊ธ๊น์ง ๋ฐฐ์ด ๊ฒ๋ค์ ์ข ํฉ์ ์ผ๋ก ์ฌ์ฉํด์ ๊ฒฐ๊ณผ๋ฌผ์ ๋ง๋ค์ด ๋ด๋ณด๊ฒ ๋ค๋ ๋ง์์ผ๋ก ์์ฑํ์์ต๋๋ค.
ํด๋น ์๋ฆฌ์ฆ์๋ ๊ฐ๋ฐ ์ผ์ง๋ฅผ ์ ๋ฆฌํ๋ฉฐ, ๊ฐ๋ฐ ๋ด์ฉ์ ๋ํ ์ค๋ช ๊ณผ ์ด์๋ฅผ ์ ๋ฆฌํ ์์ ์ ๋๋ค.
์ ์ ์์ง์์ ๋ํด์ ์ด๋ป๊ฒ ๊ตฌํํ์๋์ง์ ๋ํด ์ค๋ช ํ๊ฒ ์ต๋๋ค.
์ ์ด ํน์ ํฌ์ธํธ๋ก ์ด๋ํ๊ธฐ ์ํด ์ขํ๊ฐ์ ๋ฐ์ ์ฌ ์ ์๋ ๋ฌด์ธ๊ฐ๊ฐ ํ์ํ๋ค ์๊ฐํ์์ต๋๋ค.
๊ทธ๋์ Collision๋ง์ ๊ฐ์ง๊ณ ์๋ Moving Point๋ฅผ ์์ฑํ์์ต๋๋ค.
๋ด๋ถ์ Moving Point์ ์ธ๋ถ์ Moving Point๋ฅผ ๋ ๋ฒจ์ ๋ฐฐ์นํ์์ต๋๋ค.
Moving Point๋ ์ ๊ณผ ์ถฉ๋ ์ Overlap Event๋ฅผ ๋ฐ์์ํต๋๋ค.
์ด๋ ํด๋น ์ ์ ๋ชฉํ Moving Point๊ฐ ์์ ์ด ๋ง๋์ง ๊ฒ์ฌํ๊ณ , ์ผ์นํ๋ค๋ฉด ํจํด์ ๋ณํ์์ผ ์ค๋๋ค.
์ ๋ค์ ์ด๋ํ๋ฉฐ ๋ชฉํ Moving Point์ ๋์ฐฉํ๊ฒ ๋๋ฉด ์์ง์ ํจํด์ด ์ผ๋จ๋ฝ๋ฉ๋๋ค.
bool AEnemyMovingPoint::CheckTargetMovingPoint(AEnemyBase* enemy)
{
auto targetP = enemy->GetTargetMovingPointByIndex(enemy->GetEnemyTargetMovingPointIndex());
if (targetP)
{
AEnemyMovingPoint* targetMovingPoint = Cast<AEnemyMovingPoint>(targetP);
if (targetMovingPoint != nullptr)
{
if (targetMovingPoint == this)
{
return true;
}
}
}
return false;
}
Moving Point๋ค์ ๊ด๋ฆฌํด์ฃผ๊ธฐ ์ํด์ Enemy Manager Class๋ฅผ ์์ฑํด์ฃผ์์ต๋๋ค.
Enemy Manager๋ ๊ฐ Moving Point๋ฅผ ๊ทธ๋ฃน๋ณ๋ก ์ฐพ์ ์์ ํ๊ฒ ๋ฉ๋๋ค.
public:
// Inner Moving Points
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = SpawnSettings)
TArray<class AActor*> InnerMovingPoints;
// Left Moving Points
UPROPERTY(EditAnywhere, Category = SpawnSettings)
TArray<class AActor*> LeftMovingPoints;
// Right Moving Points
UPROPERTY(EditAnywhere, Category = SpawnSettings)
TArray<class AActor*> RightMovingPoints;
// Bottom Moving Points
UPROPERTY(EditAnywhere, Category = SpawnSettings)
TArray<class AActor*> BottomMovingPoints;
// Top Moving Points
UPROPERTY(EditAnywhere, Category = SpawnSettings)
TArray<class AActor*> TopMovingPoints;
๊ฒ์ ์์ ์ ๋ ๋ฒจ์์ ์ด๋ฆ์ ๊ธฐ๋ฐ์ผ๋ก Moving Point๋ฅผ ๊ฒ์ํ์ฌ ์ ์ฅํฉ๋๋ค.
GetAllActorsOfClass()๋ฅผ ์ฌ์ฉํ์ฌ ์ต์ด ๋ถํ๊ฐ ์ฌํ์ง๋ง ๋ชจ๋ Moving Point๋ค์ ์์์ ์ผ๋ก ๋ฃ์ง ์๊ณ ์๋ํ ์์ ์ ๊ตฌํํ๋ค๋ ๊ฒ์ ์์๋ฅผ ๋์์ต๋๋ค.
void AEnemyManager::FindEnemyLeftMovingPoints()
{
TArray<AActor*>allActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor::StaticClass(), allActors);
for (auto movingP : allActors)
{
if (movingP->GetName().Contains(TEXT("BP_EnemyLeftMovingPoint")))
{
LeftMovingPoints.Add(movingP);
}
}
}
์ ๋ค์ Enemy Manager๋ฅผ ํตํด ์ํ๋ ๊ทธ๋ฃน์ Moving Point์ ๋ํ ์ ๋ณด๋ฅผ ๋ฐ์ ์ฌ ์ ์์ต๋๋ค.
์ด๋ ๊ฒ Enemy Manager๋ฅผ ํตํด ์ ๋ค์๊ฒ ๋ํ ์ผํ ์์ง์ ํจํด์ ๊ตฌ์ฌํ๋๋ก ์ ์ํ ์ ์์ต๋๋ค.
์ ์ ๋ชฉํ Moving Point๋ก ์ด๋ํ ํ ์์ง์ด์ง ์์ต๋๋ค.
// ๊ณ ์ - ์ํ ํ MovingPoint๋ก ์ด๋์์ ์์ง์ด์ง ์์
void UEnemyFSM::FixedMove()
{
if (GetIsMoving())
{
FVector P0 = me->GetActorLocation();
FVector dir = me->GetDirectionToTargetMovingPoint(0);
FVector vt = me->GetEnemySpeed() * GetWorld()->DeltaTimeSeconds * dir;
FVector P = P0 + vt;
me->SetActorLocation(P);
}
}
์ํํ์ ๋ชฉํ Moving Point๋ฅผ 2๊ฐ ๊ฐ์ง๊ณ ์์ต๋๋ค.
๊ฐ ๋ชฉํ์ ๋์ฐฉํ๋ฉด ๋ชฉํ Moving Point๋ฅผ ๋ค๋ฅธ ํ๋๋ก ์ ํํ๊ฒ ๋ฉ๋๋ค.
์ด๋ฅผ ํตํด ๊ฐ Moving Point๋ฅผ ๋ฐ๋ณตํ๋ฉฐ ์ด๋ํ๊ฒ ๋ฉ๋๋ค.
// ํจํธ๋กค - ํน์ MovingPoint ๋ฐ๋ณต
void UEnemyFSM::PatrolMove()
{
FVector P0 = me->GetActorLocation();
if (!isMoving)
{
if (me->GetEnemyTargetMovingPointIndex() == 0)
{
me->SetEnemyTargetMovingPointIndex(1);
}
else
{
me->SetEnemyTargetMovingPointIndex(0);
}
FVector dir = me->GetDirectionToTargetMovingPoint(me->GetEnemyTargetMovingPointIndex());
dir.Normalize();
SetNowMovingDir(dir);
SetIdleState();
currentTime = 0.0f;
}
FVector vt = me->GetEnemySpeed() * GetWorld()->DeltaTimeSeconds * GetNowMovingDir();
FVector P = P0 + vt;
//me->SetActorRotation(FVector::BackwardVector.Rotation());
me->SetActorLocation(P);
}
์ํญํ์ ํ๋ ์ด์ด์๊ฒ ์ง์์ ์ผ๋ก ๋ฐ๋ผ ๋ถ์ผ๋ฉฐ ์ด๋ํฉ๋๋ค.
ํ๋ ์ด์ด์ ์ถฉ๋ํ๋ฉด ๊ฐ์ฒด๊ฐ ํ๊ดด๋ฉ๋๋ค.
// ์ํญ - ํ๋ ์ด์ด์๊ฒ ๋์ง
void UEnemyFSM::SuisideMove()
{
FVector P0 = me->GetActorLocation();
FVector dir = GetDirection();
FVector vt = me->GetEnemySpeed() * GetWorld()->DeltaTimeSeconds * dir;
FVector P = P0 + vt;
me->SetActorRotation(dir.Rotation());
me->SetActorLocation(P);
}
ํจ์คํ์ ์ธ๋ถ Moving Point๋ผ๋ฆฌ ์ด๋ํ๊ฒ ๋ฉ๋๋ค.
์ธ๋ถ > ๋ด๋ถ > ์ธ๋ถ๋ก ์ด๋ํ๋ฉฐ ์ง๋๊ฐ๋ ํจํด์ ๋๋ค.
์ธ๋ถ๋ก ๋๊ฐ๋ฉด ์กํฐ๋ ํ๊ดด๋๋๋ก ํ์์ต๋๋ค.
// ํต๊ณผ - ์ํ์์น์์ ๋ชฉํ MovingPoint๋ก ์ด๋ ํ ์๋ฉธ
void UEnemyFSM::PassByMove()
{
FVector P0 = me->GetActorLocation();
FVector dir = me->GetDirectionToTargetMovingPoint(0);
FVector vt = me->GetEnemySpeed() * GetWorld()->DeltaTimeSeconds * dir;
FVector P = P0 + vt;
me->SetActorLocation(P);
}
๋ฌด์์ํ์ Enemy Manager๊ฐ ๊ฐ์ง๊ณ ์๋ Inner Moving Point์์ ๋ฌด์์๋ก ๋์์ ๋ฐ์์ ์์ง์์ ๋ฐ๋ณตํฉ๋๋ค.
// ๋๋ค - ๋๋ค MovingPoint๋ก ๊ณ์ํด์ ์ด๋
void UEnemyFSM::RandomMove()
{
FVector P0 = me->GetActorLocation();
if (!isMoving)
{
SetIsMoving(true);
FVector targetLocation = enemyManager->GetRandomInnerMovingPoint();
FVector dir = targetLocation - me->GetActorLocation();
dir.Normalize();
SetNowMovingDir(dir);
}
FVector vt = me->GetEnemySpeed() * GetWorld()->DeltaTimeSeconds * GetNowMovingDir();
FVector P = P0 + vt;
me->SetActorLocation(P);
}
์ถ์ ํ์ ํ๋ฒ ๋ชฉํ ์์น๋ก ๋ค์ด์จ ํ ๋ค๋ก ๋๊ฐ๊ฒ ๋ฉ๋๋ค.
๊ตฌํํ๊ธฐ ๊ฐ์ฅ ๊น๋ค๋ก์ ๋ ์์ง์ ํจํด์ผ๋ก, ์ค์ง 4 Stage๋ง์ ์ํด ๊ตฌํํ์์ต๋๋ค.
๋ชฉํ ์์น๋ก ์ด๋ ํ ์ค์ ํ ์๊ฐ๋์ ๋๊ธฐํ๋ค, ํ์ง์ผ๋ก ์ด๋ํ๋ฉฐ ํ๊ดด๋ฉ๋๋ค.
Timer๋ฅผ ์ฌ์ฉํ์ฌ ๋ค์ Move์ํ๋ก ์ ํ, ๊ณต๊ฒฉ, ํ๊ดด๋ฅผ ์์ฐจ์ ์ผ๋ก ์งํํ๋๋ก ํ์์ต๋๋ค.
์ ํ ์๊ฐ๋์ ์ ์ ๊ณต์ธ์์ ๋ฒํฐ๊ธฐ ์ํด ์ฐ์ถ์ ์ธ ํจ๊ณผ๋ฅผ ์ํ ์์ง์ ํจํด์ ๋๋ค.
void UEnemyFSM::InOutMove()
{
if (GetIsMoving())
{
FVector P0 = me->GetActorLocation();
if (isIn)
{
FVector dir = me->GetDirectionToTargetMovingPoint(0);
FVector vt = me->GetEnemySpeed() * GetWorld()->DeltaTimeSeconds * dir;
FVector P = P0 + vt;
me->SetActorLocation(P);
}
else {
FVector dir = -me->GetActorForwardVector();
dir.Normalize();
FVector vt = me->GetEnemySpeed() * GetWorld()->DeltaTimeSeconds * dir;
FVector P = P0 + vt;
me->SetActorLocation(P);
}
}
else {
FTimerHandle MoveTimer;
me->GetWorldTimerManager().SetTimer(MoveTimer, [this]() {
SetIsMoving(true);
}, (me->GetInOutDelayTime()+4.0f), false);
FTimerHandle AttackTimer;
me->GetWorldTimerManager().SetTimer(AttackTimer, [this]() {
me->SetEnemyAttackSpeed(50.0f);
}, me->GetInOutDelayTime(), false);
FTimerHandle DieTimer;
me->GetWorldTimerManager().SetTimer(DieTimer, [this]() {
me->Destroy();
}, (me->GetInOutDelayTime() + 8.0f), false);
}
}
์ฌ๊ธฐ๊น์ง ์ด 6๊ฐ์ง์ ์์ง์ ํจํด์ด์์ต๋๋ค.