개발일지

08-06~08-07 MidBoss SkillAttack2, 3추가, 쉴드 버그 수정, 쉴드 C++화

ksw8596 2024. 8. 7. 18:14

MidBoss SkillAttack2 (물폭풍)

 

BP_CannonArrow

 

CannonArrow

BeginPlay -> Set Life Span 을 설정하여 시간이 되면 삭제

Cannon

TimeLine -> 시간동안 반복을 위해 사용

Arrow에서 Trace를 쏴서 Vector값과 블록인지 아닌지를 체크

벽이면 벽까지 Transform길이를 늘리고 아닌경우는 Distance값 만큼 크기를 늘림

 

Tick

지속형 데미지를 주기위해 사용

 

BeginOverlap, EndOverlap

Player가 닿았는지를 체크

 

BP_MidBoss_Enemy_Water

 

Water본인

특정 Action이 실행되면 WaterCannon을 Point부분에 Spawn한다.

 

 

Cannon 역시 전 Shield처럼 Enemy가 죽으면 삭제한다.

 

실행결과

 

MidBoss SkillAttack3 (물보라) - 차후 수정예정

 

Pooling 기술 사용 - 차후 C++로 변경예정

AC_ObjectPool

 

PoolManager역할

 

 

BP_PooledActor

 

 

BP_Projectile

 

 

Projectile을 Pool 갯수만큼 생성한다.

 

Action_WaterSkillAttack3

 

Count만큼 Projectile을 반복하고 Count에 따라 방향값을 구해 시계방향으로 Projectile이 생성된다.

Pool이 가지고있는 Spawn From Pool을 불러온다.

 

 

실행결과

 

 

참조영상

https://youtu.be/f797l7YTcgc

 

 

Shield

BP_Shield

Player를 밀어주는 부분을 추가하였다.

 

C++

Shiled.cpp

#include "Enemy/FShield.h"
#include "GameFramework/Character.h"
#include "GameFramework/PlayerController.h"
#include "Kismet/KismetSystemLibrary.h"
#include "FGameFunctionLibrary.h"
#include "Player/FAttributeComponent.h"
#include "Buff/FBuffComponent.h"

// Sets default values
AFShield::AFShield()
{
	PrimaryActorTick.bCanEverTick = true;

	StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ShiledMesh"));

	AttributeComp = CreateDefaultSubobject<UFAttributeComponent>(TEXT("AttributeComp"));
	BuffComp = CreateDefaultSubobject<UFBuffComponent>(TEXT("BuffComp"));
}

void AFShield::BeginPlay()
{
	Super::BeginPlay();
	
	PlayerIn();

	SetLifeSpan(LifeTime);

	AttributeComp->OnHealthChanged.AddDynamic(this, &AFShield::OnHealthChanged);

	//데미지형 쉴드인지 아닌지 판별
	if(bDamageShield)
	{
		FTimerHandle Timer;
		GetWorldTimerManager().SetTimer(Timer, this, &AFShield::OnDamage, 8.5f, false);
	}
}

void AFShield::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

void AFShield::OnHealthChanged(AActor* InstigatorActor, UFAttributeComponent* OwningComp, float Delta, float NewHealth)
{
	if (!AttributeComp->IsAlive())
	{
		Destroy();
	}
}

//데미지를 줄 경우
void AFShield::OnDamage()
{
	if (AttributeComp->IsAlive())
	{
		FVector Start = GetActorLocation();
		FVector ForwardVector = GetActorForwardVector();
		FVector End = Start + ForwardVector; 

		TArray<AActor*> ActorsToIgnore;
		ActorsToIgnore.Add(this);

		TArray<FHitResult> OutHits;
		
		float Radius = 400.0f;
		TArray<TEnumAsByte<EObjectTypeQuery>> ObjectTypes;
		ObjectTypes.Add(UEngineTypes::ConvertToObjectType(ECollisionChannel::ECC_Pawn));

		bool bTraceComplex = false;
		
        //Radius 거리만큼 SphereTrace를 생성하여 hit된 특정 object에 데미지를 부여
		auto isHit = UKismetSystemLibrary::SphereTraceMultiForObjects(GetWorld(),Start, End, Radius, ObjectTypes, bTraceComplex, ActorsToIgnore, EDrawDebugTrace::None, OutHits, true);
		if (isHit)
		{
			for(auto&Hit:OutHits)
			{
				AActor* HitActor = Hit.GetActor();
				if (HitActor)
				{
					UActorComponent* TargetComponent = HitActor->GetComponentByClass(UFAttributeComponent::StaticClass());
					if (IsValid(TargetComponent))
					{
						TArray<TSubclassOf<UFBuff>> buffArray;
						UFGameFunctionLibrary::ApplyDamage(GetInstigator(), HitActor, AttributeComp->CharacterStatusInfo.MagicalPower, buffArray);
					}
				}
			}
		}
	}
}

//Player가 들어왔는지 아닌지를 판별 후 밀어냄
void AFShield::PlayerIn()
{
	APlayerController* Player = GetWorld()->GetFirstPlayerController();
	ACharacter* PlayerCharacter = Cast<ACharacter>(Player->GetCharacter());

	FVector PlayerLocation = PlayerCharacter->GetActorLocation();
	FVector ShiledLocation = GetActorLocation();
	
	FVector Distance = (PlayerLocation - ShiledLocation).GetSafeNormal();

	if ((PlayerLocation - ShiledLocation).Size() < 300)
	{
		PlayerCharacter->LaunchCharacter(((PlayerLocation - ShiledLocation).GetSafeNormal()) * 300, true, true);
	}
}