[UE5] ๐Ÿš€ Project Galaga (6)

Singery00ยท2024๋…„ 5์›” 21์ผ
0

Project Galaga

๋ชฉ๋ก ๋ณด๊ธฐ
6/15
post-thumbnail

๊ฐœ์š”

๐Ÿš€ Project Galaga ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

Unreal C++๊ณผ ๊ฒŒ์ž„์ˆ˜ํ•™์„ ์ ‘๋ชฉํ•ด์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

2024.05.04 ~ 2024.05.21์˜ ๊ฐœ๋ฐœ ๊ธฐ๊ฐ„์œผ๋กœ, ์ด 17์ผ ๋™์•ˆ ๊ฐœ๋ฐœ์„ ์™„๋ฃŒํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๊ฑฐ์ฐฝํ•˜๊ฒŒ ์‹œ์ž‘ํ•œ ํ”„๋กœ์ ํŠธ๋ณด๋‹จ ์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด ๊ฒƒ๋“ค์„ ์ข…ํ•ฉ์ ์œผ๋กœ ์‚ฌ์šฉํ•ด์„œ ๊ฒฐ๊ณผ๋ฌผ์„ ๋งŒ๋“ค์–ด ๋‚ด๋ณด๊ฒ ๋‹ค๋Š” ๋งˆ์Œ์œผ๋กœ ์™„์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

ํ•ด๋‹น ์‹œ๋ฆฌ์ฆˆ์—๋Š” ๊ฐœ๋ฐœ ์ผ์ง€๋ฅผ ์ •๋ฆฌํ•˜๋ฉฐ, ๊ฐœ๋ฐœ ๋‚ด์šฉ์— ๋Œ€ํ•œ ์„ค๋ช…๊ณผ ์ด์Šˆ๋ฅผ ์ •๋ฆฌํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

๐Ÿš€ Project Galaga Github Repository


๋ณธ๋ก 


๊ณต๊ฒฉ ํŒจํ„ด


ํŒจํ„ด ์ „ํ™˜

์ ์˜ ๊ธฐ๋ณธ ํŒจํ„ด ์ƒํƒœ๋Š” 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๋ฅผ ๊ณ„์‚ฐํ•ด์ฃผ๋ฉด ํ•ด๋‹น ๋ฐฉํ–ฅ์œผ๋กœ ๋ฐœ์‚ฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.


์›€์ง์ž„ ํŒจํ„ด

๊ณต๊ฒฉ ํŒจํ„ด ๊ตฌํ˜„์—์„œ ์‚ผ๊ฐ ํ•จ์ˆ˜๊ฐ€ ์‚ฌ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


์žํญํ˜• (Suiside)

์žํญํ˜•์€ ๊ณต๊ฒฉ ์ƒํƒœ ํ•จ์ˆ˜๋ฅผ ๋ณ„๋„๋กœ ์ฒ˜๋ฆฌํ•ด์ฃผ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์›€์ง์ž„ ํŒจํ„ด ์ž์ฒด๊ฐ€ ๊ณต๊ฒฉ ์ƒํƒœ ํ•จ์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.


์ „๋ฐฉํ˜• (Front)

์ฃผ์ฒด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ „๋ฐฉ์œผ๋กœ ์ด์•Œ์„ ์†Œํ™˜ํ•ฉ๋‹ˆ๋‹ค.

// ์ „๋ฐฉ ์ด์•Œ ๋ฐœ์‚ฌ
void UEnemyFSM::FrontAttack()
{
	FActorSpawnParameters param;
	param.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;

	AEnemyBullet* bullet = me->bulletPool->SpawnPooledObject(me->GetActorLocation(), me->GetActorRotation());
	
}


๋Œ€์ƒํ˜• (Target)

ํ”Œ๋ ˆ์ด์–ด์˜ ํ˜„์žฌ ์œ„์น˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ฐฉํ–ฅ์„ ๊ณ„์‚ฐํ•˜์—ฌ 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);
}


ํ™•์‚ฐํ˜• (Spread)

์‹œ์ž‘ ๊ฐ๋„ 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;
	}

}


๊ถค๋„ํ˜• (Orbit)

๊ถค๋„ํ˜•์€ ํ™•์‚ฐํ˜•๊ณผ ๋™์ผํ•œ ์›๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ํ™•์‚ฐํ˜•๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ 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);

	}

}


์ฆ‰๋ฐœํ˜• (Laser)

์ฆ‰๋ฐœํ˜• ๊ณต๊ฒฉ์€ 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);
		}
	}
}

ํŒŒ๋„ํ˜• (Wave)

ํŒŒ๋„ํ˜•์€ ํ˜„์žฌ ๊ฐ๋„๋ฅผ ๋ผ๋””์•ˆ์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ 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;
		}
	}
}


๊ฐ๋„ํ˜• (Angle)

์ฃผ์ฒด๋ฅผ ์ „๋ฐฉ์œผ๋กœ ์ตœ๋Œ€ ๋ฐœ์‚ฌ ๊ฐ๋„๋ฅผ ์ด์•Œ ๊ฐฏ์ˆ˜๋กœ ๋‚˜๋ˆ„์–ด ์ด์•Œ ๋ณ„ ๊ฐ๋„๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์‹œ์ž‘ ๊ฐ๋„์—์„œ๋ถ€ํ„ฐ (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);
	}
}


๋งˆ๋ฌด๋ฆฌ

์—ฌ๊ธฐ๊นŒ์ง€ ์ ์ด ๊ฐ€์ง€๋Š” ๊ฐ ๊ณต๊ฒฉ ํŒจํ„ด์— ๋Œ€ํ•œ ๊ตฌํ˜„๋‚ด์šฉ์ด์˜€์Šต๋‹ˆ๋‹ค.

profile
๊ฒŒ์ž„ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์–ด๋ณด์ž

0๊ฐœ์˜ ๋Œ“๊ธ€