๐ Project Galaga ์์ํฉ๋๋ค.
Unreal C++๊ณผ ๊ฒ์์ํ์ ์ ๋ชฉํด์ ํ๋ก์ ํธ๋ฅผ ์งํํ์ต๋๋ค.
2024.05.04 ~ 2024.05.21์ ๊ฐ๋ฐ ๊ธฐ๊ฐ์ผ๋ก, ์ด 17์ผ ๋์ ๊ฐ๋ฐ์ ์๋ฃํ์์ต๋๋ค.
๊ฑฐ์ฐฝํ๊ฒ ์์ํ ํ๋ก์ ํธ๋ณด๋จ ์ง๊ธ๊น์ง ๋ฐฐ์ด ๊ฒ๋ค์ ์ข ํฉ์ ์ผ๋ก ์ฌ์ฉํด์ ๊ฒฐ๊ณผ๋ฌผ์ ๋ง๋ค์ด ๋ด๋ณด๊ฒ ๋ค๋ ๋ง์์ผ๋ก ์์ฑํ์์ต๋๋ค.
ํด๋น ์๋ฆฌ์ฆ์๋ ๊ฐ๋ฐ ์ผ์ง๋ฅผ ์ ๋ฆฌํ๋ฉฐ, ๊ฐ๋ฐ ๋ด์ฉ์ ๋ํ ์ค๋ช ๊ณผ ์ด์๋ฅผ ์ ๋ฆฌํ ์์ ์ ๋๋ค.
์ ์ ๊ธฐ๋ณธ ํจํด ์ํ๋ Move State์ ๋๋ค.
์ํ ์งํ ์ค currentTime์ด ์ง์์ ์ผ๋ก DeltaTime๊ณผ ๋์ ํฉ ๋ฉ๋๋ค.
์ด๋ currentTime์ด Stat๋ณ๋ก ์ค์ ๋ EnemyAttackSpeed๋ณด๋ค ์ปค์ง๊ฒ ๋๋ค๋ฉด Attack State๋ก ์ ํ๋ฉ๋๋ค.
void UEnemyFSM::MoveState()
{
// ์๋ต
if (currentTime >= me->GetEnemyAttackSpeed())
{
SetAttackState();
}
}
์ด์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ค์ ๋ ์ด๋ ์๋๋งํผ ์ ๋ฐฉ์ผ๋ก๋ง ์ด๋ํฉ๋๋ค.
void AEnemyBullet::BulletForwardMove(float DeltaTime)
{
FVector P = GetActorLocation() + DeltaTime * BulletSpeed * GetActorForwardVector();
SetActorLocation(P);
}
์ฆ, ์ด์ ์ํ ์ Rotation๋ฅผ ๊ณ์ฐํด์ฃผ๋ฉด ํด๋น ๋ฐฉํฅ์ผ๋ก ๋ฐ์ฌ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
๊ณต๊ฒฉ ํจํด ๊ตฌํ์์ ์ผ๊ฐ ํจ์๊ฐ ์ฌ์ฉ๋์์ต๋๋ค.
์ํญํ์ ๊ณต๊ฒฉ ์ํ ํจ์๋ฅผ ๋ณ๋๋ก ์ฒ๋ฆฌํด์ฃผ์ง ์์ต๋๋ค.
์์ง์ ํจํด ์์ฒด๊ฐ ๊ณต๊ฒฉ ์ํ ํจ์์ด๊ธฐ ๋๋ฌธ์
๋๋ค.
์ฃผ์ฒด๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ฐฉ์ผ๋ก ์ด์์ ์ํํฉ๋๋ค.
// ์ ๋ฐฉ ์ด์ ๋ฐ์ฌ
void UEnemyFSM::FrontAttack()
{
FActorSpawnParameters param;
param.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
AEnemyBullet* bullet = me->bulletPool->SpawnPooledObject(me->GetActorLocation(), me->GetActorRotation());
}
ํ๋ ์ด์ด์ ํ์ฌ ์์น๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ฐฉํฅ์ ๊ณ์ฐํ์ฌ Direction์ ์์ ๋ ๋๋ค.
Direction์ ์ ๊ทํํ๋ฉฐ ๋ฐฉํฅ๋ฒกํฐ๋ก ๋ง๋ค์ด์ ์ด์์ ์ํํ๋ฉด ํ๋ ์ด์ด๋ฅผ ์กฐ์คํ ํจ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค.
// ํ๋ ์ด์ด์๊ฒ ์ด์ ๋ฐ์ฌ
void UEnemyFSM::TargetAttack()
{
FActorSpawnParameters param;
param.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
FVector dir = GetDirection();
dir.Normalize();
FRotator rotation = dir.Rotation();
AEnemyBullet* bullet = me->bulletPool->SpawnPooledObject(me->GetActorLocation(), rotation);
}
์์ ๊ฐ๋ 0๋๋ฅผ ๊ธฐ์ค์ผ๋ก 10๋์ฉ ์ฆ๊ฐํ๋ฉฐ ํ์ฌ ๊ฐ๋๋ฅผ ๊ณ์ฐํฉ๋๋ค.
ํ์ฌ ๊ฐ๋๊ฐ์ ๋ผ๋์์ผ๋ก ๋ณํํ์ฌ X, Y์ Sin() Cos()์ ๊ต์ฐจ๋ก ์ฌ์ฉ ์ ์์ ๋๋ ๋ฅผ ๋ฐ๋ผ ์์น๊ฐ์ ์ป์ ์ ์์ต๋๋ค.
์ด์์ ๋ฐฉํฅ์ ํด๋น ํ์ฌ ์กํฐ์ ๋กํ ์ด์ ์ ๊ธฐ์ค์ผ๋ก Yaw๊ฐ์ ํ์ฌ ๊ฐ๋๋ฅผ ๋นผ์ฃผ์์ต๋๋ค.
๋ํ ์ผ๊ฐํจ์ ๊ทธ๋ํ๋ ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ง๋ฉฐ ๋ฐ๋ณต๋๋ฏ๋ก ์ํ๋ ๊ฐ๋๋งํผ ๊ณ์ํด์ ๋์ ํฉ์ด ๋์ด๋ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค.
// ์์ ๊ทธ๋ฆฌ๋ฉฐ 360๋๋ก ํ์ฐ ๋ฐ์ฌ
void UEnemyFSM::SpreadAttack()
{
if (!GetIsMoving())
{
static double standardAngle = 0;
// ์ด์ ์ํ Location
double angleRad = FMath::DegreesToRadians(standardAngle);
FVector bulletSpawnLocation = me->GetActorLocation() + FVector(sin(angleRad), cos(angleRad), 0);
// ์ด์ ์ํ Rotation
FRotator bulletSpawnRotation = me->GetActorRotation();
bulletSpawnRotation.Yaw -= standardAngle;
AEnemyBullet* bullet = me->bulletPool->SpawnPooledObject(bulletSpawnLocation, bulletSpawnRotation);
standardAngle += 10;
}
}
๊ถค๋ํ์ ํ์ฐํ๊ณผ ๋์ผํ ์๋ฆฌ๋ฅผ ์ฌ์ฉํ์์ต๋๋ค.
ํ์ง๋ง ํ์ฐํ๊ณผ๋ ๋ค๋ฅด๊ฒ 360๋์ ์ด์๋ค์ ์ผ์์ ์ผ๋ก ์ํํ์ฌ ํ๋ฒ์ ํผ์ง๋๋ก ๊ตฌํํ์์ต๋๋ค.
// 360๋๋ก ์ํ์ผ๋ก ์ด์์ ๋ฐ์ฌ
void UEnemyFSM::OrbitAttack()
{
// ๊ฐ๋
double angleBetweenBullets = 360/60;
for (int i = 0; i < 60; i++)
{
// ํ์ฌ๊ฐ๋
double angle = i * angleBetweenBullets;
// ๋ผ๋์ ๋ณํ
double angleInRadians = FMath::DegreesToRadians(angle);
FRotator bulletSpawnRotation = me->GetActorRotation();
bulletSpawnRotation.Yaw -= angle;
// ์ด์์ด ๋๊ฐ ์์น๋ฅผ ๊ณ์ฐํฉ๋๋ค.
FVector bulletSpawnLocation = me->GetActorLocation() + FVector(sin(angleInRadians), cos(angleInRadians), 0) * 300;
AEnemyBullet* bullet = me->bulletPool->SpawnPooledObject(bulletSpawnLocation, bulletSpawnRotation);
}
}
์ฆ๋ฐํ ๊ณต๊ฒฉ์ SweepSingleByChannel์ ์ฌ์ฉํ์ฌ ๊ตฌํํ์์ต๋๋ค.
๊ณต๊ฒฉ ์ ์ถฉ๋ํ ๊ฐ์ฒด์๊ฒ Player ํ๊ทธ๊ฐ ์๋ค๋ฉด ApplyDamage()๋ฅผ ํธ์ถํ์ฌ ํผ๊ฒฉ์ด ๋๋๋ก ๊ตฌํํ์์ต๋๋ค.
// Sweep By Single Channel์ ์ฌ์ฉํ์ฌ ์ฆ๋ฐํ ๊ณต๊ฒฉ ๊ตฌํ
void UEnemyFSM::LaserAttack()
{
FVector StartPos = me->GetActorLocation();
FVector EndPos = StartPos + me->GetActorForwardVector() * 4500.f;
FHitResult HitResult;
bool Result = GetWorld()->SweepSingleByChannel
(
HitResult,
StartPos,
EndPos,
FQuat::Identity,
ECollisionChannel::ECC_GameTraceChannel1,
FCollisionShape::MakeBox(FVector(60,60,60))
);
if (Result && HitResult.GetActor())
{
if (HitResult.GetActor()->ActorHasTag("Player"))
{
UGameplayStatics::ApplyDamage(HitResult.GetActor(), 1, NULL, NULL, NULL);
}
}
}
ํ๋ํ์ ํ์ฌ ๊ฐ๋๋ฅผ ๋ผ๋์์ผ๋ก ๋ณํํ์ฌ Y๊ฐ์ Sin()๋ฅผ ์ฌ์ฉํ์์ต๋๋ค.
Sin()๊ฐ์ amplitude๋ฅผ ๊ณฑํ์ฌ ์งํญ์ ์ฃผ์ด ํ๋ํ ๊ณต๊ฒฉ์ ์ผ๋ ์์ ๋ํ์์ต๋๋ค.
๋ํ WaveRange()๊ฐ์ ํฉ,์ฐจ ๊ฐ์ ์ฃผ์ด ํํ์ด๋ํด์ฃผ์ด ์ ์์ผ๋ก ์ง๋๋ ํ๋ํ ๊ณต๊ฒฉ์ ๊ตฌํํ์์ต๋๋ค.
// Sin ๊ทธ๋ํ ๋ชจ์์ผ๋ก ์ด์์ด ๋ฐ์ฌ
void UEnemyFSM::WaveAttack()
{
if (!GetIsMoving())
{
// ํ์ฌ ๊ฐ๋
static double angle = 0;
// ํ๋์ ์ฃผ๊ธฐ
double waveLength = 360.0;
// ํ๋์ ๋์ด
double amplitude = 500.0;
// ๊ฐ๋๋ฅผ ๋ผ๋์์ผ๋ก ๋ณํ
double angleInRadians = FMath::DegreesToRadians(angle);
FRotator bulletSpawnRotation = me->GetActorRotation();
// ์ด์์ด ๋๊ฐ ์์น๋ฅผ ๊ณ์ฐํฉ๋๋ค.
FVector bulletSpawnLocationLeft = me->GetActorLocation() + FVector(0, (FMath::Sin(angleInRadians) * amplitude)+me->GetWaveRange(), 0);
FVector bulletSpawnLocationRight = me->GetActorLocation() + FVector(0, (FMath::Sin(angleInRadians) * amplitude)- me->GetWaveRange(), 0);
AEnemyBullet* bulletLeft = me->bulletPool->SpawnPooledObject(bulletSpawnLocationLeft, bulletSpawnRotation);
AEnemyBullet* bulletRight = me->bulletPool->SpawnPooledObject(bulletSpawnLocationRight, bulletSpawnRotation);
// ๊ฐ๋๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค.
angle += 5;
if (angle >= waveLength)
{
angle -= waveLength;
}
}
}
์ฃผ์ฒด๋ฅผ ์ ๋ฐฉ์ผ๋ก ์ต๋ ๋ฐ์ฌ ๊ฐ๋๋ฅผ ์ด์ ๊ฐฏ์๋ก ๋๋์ด ์ด์ ๋ณ ๊ฐ๋๋ฅผ ์ป์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ์์ ๊ฐ๋์์๋ถํฐ (index * angleBetweenBullets) ๊ฐ์ ๋ํ์ฌ ํ์ฌ ์ด์์ ๋ฐ์ฌ ๊ฐ๋๋ฅผ ์ป์์ต๋๋ค.
ํด๋น ๊ฐ๋๋ฅผ ๋ผ๋์์ผ๋ก ๋ณํํ์ฌ X๊ฐ์ Cos()์ ์ฌ์ฉํ๋ฉด 1 ~ -1๋ก ์ด๋ํ๋ฉฐ ๊ฐ๋๊ฐ์ ํฉ, ์ฐจ์ค ํ๋๋ง์ ์ฌ์ฉํ์ฌ๋ ์ ํ์ ์ผ๋ก ์ด์์ ์์ฑํ ์ ์์ต๋๋ค.
// ์ ๋ฐฉ ๊ธฐ์ค Max Angle์ Bullet Count๋งํผ ๋ถํ ํ์ฌ ์ด์ ๋ฐ์ฌ
void UEnemyFSM::AngleAttack()
{
double angleBetweenBullets = 130.0/me->GetBulletCount();
double startAngle = 65.0;
for (int i = 0; i < me->GetBulletCount(); i++)
{
double angle = startAngle - i * angleBetweenBullets;
double angleInRadians = FMath::DegreesToRadians(angle);
FRotator bulletRotation = me->GetActorRotation();
bulletRotation.Yaw += angle;
FVector spawnLocation = me->GetActorLocation() + FVector(cos(angleInRadians), sin(angleInRadians), 0) * 200;
AEnemyBullet* bullet = me->bulletPool->SpawnPooledObject(spawnLocation, bulletRotation);
}
}
์ฌ๊ธฐ๊น์ง ์ ์ด ๊ฐ์ง๋ ๊ฐ ๊ณต๊ฒฉ ํจํด์ ๋ํ ๊ตฌํ๋ด์ฉ์ด์์ต๋๋ค.