EDR_Project / Character (12)

캐릭터의 무기가 한개가 아닌 경우를 위해서 CurrentWeapon을 TArray로 만들어봤었는데, 이 것 때문인지 엉뚱한 곳에서 오류가 계속 발생하였었습니다.

 

해당 오류 사진

다른 팀원이 만든 코드인데 엉뚱한데서 오류가 났다고 잡히는 바람에 해결하는데 애를 먹었습니다.

제가 내린 결론은 CurrentWeapon배열에서 없는데 접근하거나 메모리관련 예외가 발생 할 가능성이 있을때 다른데서 오류가 떴었던 것 같습니다.

그리고 캐릭터가 공격중에 사망했을때 무기에 공격판정이 끝나지 않는 경우가 있었는데 사망했을때 공격 모드를 끄게 하였습니다.

 

틱 함수에 캐릭터 보간 관련함수도 추가하였습니다.

MyCharacter.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once
#include "Sound/SoundCue.h"
#include "Kismet/GameplayStatics.h"
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Components/CapsuleComponent.h"
#include "EDR_Enemy_Weapon.h"
#include "MyCharacter.generated.h"


// 공격 애니메이션이 끝났는지 확인
DECLARE_MULTICAST_DELEGATE(FOnAttackEndDelegate);
DECLARE_MULTICAST_DELEGATE(FOnFightStartEndDelegate);
//DECLARE_MULTICAST_DELEGATE(FOnDeathEndDelegate);


UCLASS()
class EDR_API AMyCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties

	AMyCharacter();
	virtual void PossessedBy(AController* NewController)override;


	// 공격 히트 시 재생할 사운드
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound")
	TArray<TObjectPtr<class USoundCue>> HitSoundCue;

	// 공격 미스 시 재생할 사운드
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound")
	TArray<TObjectPtr<class USoundCue>> MissSoundCue;

	// 공격 시 재생할 사운드
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound")
	TArray<TObjectPtr<class USoundCue>> AttackSoundCue;

	// 스킬 시 재생할 사운드
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound")
	USoundCue* SkillSoundCue;

	// 사망 시 재생할 사운드
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sound")
	USoundCue* DeathSoundCue;

	// 공격 사거리
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attack", Meta = (AllowPrivateAccess = true))
	float AttackRange;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attack", Meta = (AllowPrivateAccess = true))
	float AttackRadius;

	// 목표 방향 계산
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	FVector TargetLocation;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	FVector Direction;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float RotationSpeed = 5.0f; // 원하는 회전 속도
	// 걷는 속도 관련
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float TargetSpeed = 0.0f; // 목표 속도
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float MaxWalkSpeed = 200.0f;  // 가속도
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float Acceleration = 120.0f;  // 가속도
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float Deceleration = 200.0f; // 감속도
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float CurrentSpeed = 0.0f; // 현재 속도
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float StopDistance = 100.0f;
	// 이동중인지 체크
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Movement")
	bool IsMoving = false;

protected:





	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapone")
	TObjectPtr<class AEDR_Enemy_Weapon> CurrentWeapon;
	// 무기
	//TArray<TObjectPtr<class AEDR_Enemy_Weapon>> CurrentWeapon;
	
	//TObjectPtr<class AEDR_Enemy_Weapon> CurrentWeapon2;
	//TObjectPtr<class AEDR_Enemy_Weapon> CurrentWeapon3;


	// 정지 관련 함수
	void StopMovement();

	void ResumeMovement();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	// 받는 데미지 처리 함수
	virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser) override;

	UPROPERTY(BluePrintReadWrite)
	float hp = 100.0f;

	// 보스 캐릭터의 공격 데미지 설정 변수
	UPROPERTY(BluePrintReadWrite)
	float AttackDamage;
	// 보스 캐릭터의 스킬 데미지 설정 변수
	UPROPERTY(BluePrintReadWrite)
	float SkillDamage;



	// 스킬 사거리
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Attack", Meta = (AllowPrivateAccess = true))
	float SkillRange;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Attack", Meta = (AllowPrivateAccess = true))
	float SkillRadius;

	// 공격 애니메이션
	UPROPERTY(EditAnywhere)
	TArray<TObjectPtr<class UAnimMontage>> AttackMontage;

	//  스킬 애니메이션
	UPROPERTY(EditAnywhere)
	TObjectPtr<class UAnimMontage> SkillMontage;

	// 사망 애니메이션
	UPROPERTY(EditAnywhere)
	TObjectPtr<class UAnimMontage> DeathMontage;

	// 전투 시작
	UPROPERTY(EditAnywhere)
	TObjectPtr<class UAnimMontage> FightStartMontage;

	UPROPERTY(EditAnywhere, BluePrintReadWrite)
	bool IsHpZero;




	// 애니메이션 재생을 위해 공격중인지 확인
	UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "Attack", Meta = (AllowPrivateAccess = true))
	bool IsAttacking;


	// 애니메이션 인스턴트 객체
	UPROPERTY()
	class UAnim_EDR_AnimInstance* EDRAnim;

public:
	// 보스 인지 확인
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float DetectRange;


	// 보스 인지 확인
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	bool IsBoss;

	// 전투 시작 애니메이션 확인
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attack", Meta = (AllowPrivateAccess = true))
	bool IsFightStarting = false;

	// Called every frame
	virtual void Tick(float DeltaTime) override;

	UPROPERTY(BluePrintReadWrite)
	bool Death = false;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;



	// hp 반환
	UFUNCTION(BlueprintPure, category = "Player")
	float GetHp() { return hp; }


	// hp 업데이트
	UFUNCTION(BlueprintCallable, category = "Player")
	virtual void UpdateHP(float NewHP);


	// 사망 애니메이션 재생
	UFUNCTION(BlueprintCallable, category = "Player")
	virtual void IsDeath();

	// 공격 애니메이션 끝났는지 체크
	//FOnDeathEndDelegate OnDeathEnd;

	// 전투 시작 애니메이션 끝났을 때 호출할 델리게이트
	FOnFightStartEndDelegate OnFightStartEnd;

	void DestroyCharacter();

	//UFUNCTION()
	//void OnDeathMontageEnded(UAnimMontage* Montage, bool bInterrupted);

	UFUNCTION()
	void OnFightStartMontageEnded(UAnimMontage* Montage, bool bInterrupted);

	// 전투 시작시 애니메이션 재생하는 함수
	void FightStart();


	// 공격 함수
	virtual void Attack();

	// 공격 애니메이션 끝났는지 체크
	FOnAttackEndDelegate OnAttackEnd;



	// 공격 히트 체크
	UFUNCTION()
	void AttackCheck();
	UFUNCTION()
	void AttackCheckEnd();
	UFUNCTION()
	void OnAttackMontageEnded(UAnimMontage* Montage, bool bInterrupted);


	//UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
	//bool bCanAttackSmallMove; //공격미세이동여부. 
	//UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))FVector ExpectedAttackLocation; //공격미세이동목표값.


private:

	FTimerHandle StopMovementTimerHandle;
	int32 RandomValue;
};
반응형

MyCharacter.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyCharacter.h"
#include "Engine/World.h"
#include "Anim_EDR_AnimInstance.h"
#include "Components/CapsuleComponent.h"
#include "DrawDebugHelpers.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Engine/DamageEvents.h"

// Sets default values
AMyCharacter::AMyCharacter()
{
	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	CurrentSpeed = 0.0f;
	PrimaryActorTick.bCanEverTick = true;
	IsAttacking = false;
	IsFightStarting = false;
	// 캡슐컴포넌트가 MyCharacter프리셋을 사용하도록 함
	GetCapsuleComponent()->SetCollisionProfileName(TEXT("Pawn"));
	TargetLocation = GetActorLocation();

}

void AMyCharacter::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);

	// 캐릭터 회전 부드럽게
	bUseControllerRotationYaw = false;
	GetCharacterMovement()->bUseControllerDesiredRotation = false;
	GetCharacterMovement()->bOrientRotationToMovement = true;
	GetCharacterMovement()->RotationRate = FRotator(0.0f, 480.f, 0.0f);
}

// Called when the game starts or when spawned
void AMyCharacter::BeginPlay()
{
	Super::BeginPlay();

}

void AMyCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	FVector CurrentLocation = GetActorLocation();
	Direction = (TargetLocation - CurrentLocation).GetSafeNormal();
	float DistanceToTarget = FVector::Dist(TargetLocation, CurrentLocation);

	if (DistanceToTarget <= StopDistance)
	{
		TargetSpeed = 0.0f;
	}
	else
	{
		TargetSpeed = MaxWalkSpeed;
	}
	
	//// 속도 보간
	CurrentSpeed = FMath::FInterpTo(CurrentSpeed, TargetSpeed, DeltaTime, Acceleration); // Acceleration는 적절한 값으로 설정
	if (CurrentSpeed <= 10.0f)
	{
		CurrentSpeed = 0.0f;
	}
	// 캐릭터 이동 처리
	if (GetCharacterMovement())
	{
		GetCharacterMovement()->MaxWalkSpeed = CurrentSpeed;
	}

	Direction.Z = 0.0f;
	FRotator TargetRotation = FRotationMatrix::MakeFromX(Direction).Rotator();
	FRotator NewRotation = FMath::RInterpTo(GetActorRotation(), TargetRotation, DeltaTime, 2.0f);
	SetActorRotation(NewRotation);
}


// Called to bind functionality to input
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}



// 캐릭터 정지 관련 함수
void AMyCharacter::StopMovement()
{
	// 이동을 멈추기 위해 MaxWalkSpeed를 0으로 설정
	GetCharacterMovement()->MaxWalkSpeed = 0.0f;

	// 2초 후에 이동을 재개
	GetWorld()->GetTimerManager().SetTimer(StopMovementTimerHandle, this, &AMyCharacter::ResumeMovement, 2.0f, false);
}

void AMyCharacter::ResumeMovement()
{
	// 원래의 이동 속도로 복원 (예: 200으로 설정)
	GetCharacterMovement()->MaxWalkSpeed = 200.0f; // 원하는 속도로 설정
}





// 사망 애니메이션



void AMyCharacter::IsDeath()
{
	// 이미 죽어있으면 애니메이션 실행 안함
	if (Death)
	{
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("Death true"));
		return;
	}
	AttackCheckEnd();
	EDRAnim = Cast<UAnim_EDR_AnimInstance>(GetMesh()->GetAnimInstance());
	if (DeathMontage == nullptr)
	{
		return;
	}
	// 애니메이션 몽타주 실행
	PlayAnimMontage(DeathMontage, 1.0f);
	Death = true;
	// 스킬 사운드 재생
	if (DeathSoundCue != nullptr)
	{
		UGameplayStatics::SpawnSoundAttached(
			DeathSoundCue,
			GetRootComponent(),
			NAME_None,
			FVector::ZeroVector,
			EAttachLocation::KeepRelativeOffset,
			false,
			1.0f,  // Volume multiplier
			0.7f   // Pitch multiplier, 0.5로 설정하면 재생 속도가 절반으로 느려짐
		);
	}
	//if (EDRAnim != nullptr)
	//{
	//	EDRAnim->OnMontageEnded.RemoveAll(this);  // 기존 이벤트 제거
	//	EDRAnim->OnMontageEnded.AddDynamic(this, &AMyCharacter::OnDeathMontageEnded);
	//}

}



void AMyCharacter::DestroyCharacter()
{
	//Destroy();
	//CurrentWeapon->Destroy();
}
//void AMyCharacter::OnDeathMontageEnded(UAnimMontage* Montage, bool bInterrupted)
//{
	//// FightStart 애니메이션이 끝났을 때 공격 시작
	//GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Blue, TEXT("asdfasdasdfsdf"));
	//DestroyCharacter();
	//OnDeathEnd.Broadcast();
//}
void AMyCharacter::UpdateHP(float NewHP)
{
	hp += NewHP;

	GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, FString::Printf(TEXT("UpdateHP() - %s HP : %f"), *GetName(), hp));
}




// 전투 시작

void AMyCharacter::FightStart()
{
	if (IsFightStarting)
	{
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("ASDFADSF"));
		return;
	}
	EDRAnim = Cast<UAnim_EDR_AnimInstance>(GetMesh()->GetAnimInstance());
	if (FightStartMontage)
	{
		PlayAnimMontage(FightStartMontage, 1.0f);
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, TEXT("FightStartMontage Played"));
	}

	if (EDRAnim != nullptr)
	{
		EDRAnim->OnMontageEnded.RemoveAll(this);  // 기존 이벤트 제거
		EDRAnim->OnMontageEnded.AddDynamic(this, &AMyCharacter::OnFightStartMontageEnded);
	}
}

// 전투 시작 애니메이션 종료 되면 호출
void AMyCharacter::OnFightStartMontageEnded(UAnimMontage* Montage, bool bInterrupted)
{
	// FightStart 애니메이션이 끝났을 때 공격 시작
	GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Blue, FString::Printf(TEXT("bInterrupted: %s"), bInterrupted ? TEXT("True") : TEXT("False")));
	GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("Fight Start Animation Ended"));
	TargetSpeed = 0.0f;
	CurrentSpeed = 0.0f;
	// 애니메이션 종료 확인
	IsFightStarting = true;
	//StopMovement();
	OnFightStartEnd.Broadcast();
}




// 공격 관련

void AMyCharacter::Attack()
{

	// 공격, 스킬 확률
	RandomValue = FMath::RandRange(0, 100);
	if (Death)
	{
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("is Death true"));
		return;
	}

	if (IsAttacking)
	{
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("is attacking true"));
		return;
	}



	EDRAnim = Cast<UAnim_EDR_AnimInstance>(GetMesh()->GetAnimInstance());
	if (nullptr == EDRAnim)
	{
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("CallAttack"));
		return;
	}
	// 보스 공격 로직
	if (IsBoss)
	{
		// 20%확률로 스킬 발동
		if (RandomValue <= 20)
		{
			GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("Skill!!!!!!!!!!!!!!!!!"));
			PlayAnimMontage(SkillMontage, 1.3f);

			//// 스킬 사운드 재생
			//if (SkillSoundCue != nullptr)
			//{
			//	UGameplayStatics::SpawnSoundAttached(
			//		SkillSoundCue,
			//		GetRootComponent(),
			//		NAME_None,
			//		FVector::ZeroVector,
			//		EAttachLocation::KeepRelativeOffset,
			//		false,
			//		1.0f,  // Volume multiplier
			//		0.5f   // Pitch multiplier
			//	);
			//}
		}
		else
		{
			int aRandom = FMath::RandRange(0, 100);
			GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("Attack!@!@!@!@!@"));
			if (aRandom < 10 && aRandom >= 0)
			{
				if (AttackMontage.IsValidIndex(0))
				{
					GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("0"));
					PlayAnimMontage(AttackMontage[0], 1.0f);
				}
			}
			else if (aRandom < 20 && aRandom >= 10)
			{
				if (AttackMontage.IsValidIndex(1))
				{
					GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("1"));
					PlayAnimMontage(AttackMontage[1], 1.0f);
				}
			}
			else if (aRandom < 30 && aRandom >= 20)
			{
				if (AttackMontage.IsValidIndex(2))
				{
					GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("2"));
					PlayAnimMontage(AttackMontage[2], 1.0f);
				}
			}
			else if (aRandom < 40 && aRandom >= 30)
			{
				if (AttackMontage.IsValidIndex(3))
				{
					GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("3"));
					PlayAnimMontage(AttackMontage[3], 1.0f);
				}
			}
			else if (aRandom < 60 && aRandom >= 50)
			{
				if (AttackMontage.IsValidIndex(4))
				{
					GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("4"));
					PlayAnimMontage(AttackMontage[4], 1.0f);
				}
			}
			else if (aRandom < 70 && aRandom >= 60)
			{
				if (AttackMontage.IsValidIndex(5))
				{
					GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("5"));
					PlayAnimMontage(AttackMontage[5], 1.0f);
				}
			}
			else if (aRandom < 80 && aRandom >= 70)
			{
				if (AttackMontage.IsValidIndex(6))
				{
					GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("6"));
					PlayAnimMontage(AttackMontage[6], 1.0f);
				}
			}
			else if (aRandom < 90 && aRandom >= 80)
			{
				if (AttackMontage.IsValidIndex(7))
				{
					GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("7"));
					PlayAnimMontage(AttackMontage[7], 1.0f);
				}
			}
			else
			{
				if (AttackMontage.IsValidIndex(8))
				{
					GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("8"));
					PlayAnimMontage(AttackMontage[8], 1.0f);
				}
			}
		}
	}
	else // 잡몹 일때
	{
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("asdkjfyhasdfijhgi"));
		// 공격 2개 
		if (RandomValue <= 50)
		{
			if (AttackMontage.IsValidIndex(0))
			{
				GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("0"));
				PlayAnimMontage(AttackMontage[0], 1.0f);
			}
		}
		else
		{
			if (AttackMontage.IsValidIndex(1))
			{
				GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("1"));
				PlayAnimMontage(AttackMontage[1], 1.0f);
			}
		}
	}


	IsAttacking = true;

	//공격 판정 이벤트 중복 등록 방지: 기존에 바인딩된 이벤트가 있으면 제거
	EDRAnim->OnAttackHitCheck.RemoveAll(this);




	//공격 판정 이벤트 바인딩
	EDRAnim->OnAttackHitCheck.AddUObject(this, &AMyCharacter::AttackCheck);

	EDRAnim->OnMontageEnded.RemoveAll(this);  // 중복 바인딩 방지
	//공격 판정 끝 이벤트 바인딩
	EDRAnim->OnAttackEnd.AddUObject(this, &AMyCharacter::AttackCheckEnd);



	// 애니메이션 종료 시 공격 끝 처리
	EDRAnim->OnMontageEnded.RemoveAll(this);  // 중복 바인딩 방지
	EDRAnim->OnMontageEnded.AddDynamic(this, &AMyCharacter::OnAttackMontageEnded);


}



// 공격 애니메이션 종료 처리 함수
void AMyCharacter::OnAttackMontageEnded(UAnimMontage* Montage, bool bInterrupted)
{
	if (!IsAttacking)
	{
		return;
	}

	IsAttacking = false;

	// 공격 종료 시 이벤트 해제
	if (EDRAnim != nullptr)
	{
		EDRAnim->OnAttackHitCheck.RemoveAll(this);  // 중복 호출 방지
	}
	//TargetSpeed = 0.0f;
	//CurrentSpeed = 0.0f;
	GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, TEXT("Attack Animation Ended"));

	OnAttackEnd.Broadcast();
}





//  공격 판정


void AMyCharacter::AttackCheck()
{
	CurrentWeapon->StartAttack();
	GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, TEXT("RightHand"));
	int RandomSound = FMath::RandRange(0, 2);
	// 공격 사운드 재생		
	//if (RandomSound < 33)
	//{
	if (AttackSoundCue[RandomSound] != nullptr)
	{
		UGameplayStatics::SpawnSoundAttached(
			AttackSoundCue[RandomSound],
			GetRootComponent(),
			NAME_None,
			FVector::ZeroVector,
			EAttachLocation::KeepRelativeOffset,
			false,
			1.0f,  // Volume multiplier
			1.0f   // Pitch multiplier, 0.5로 설정하면 재생 속도가 절반으로 느려짐
		);
	}
	//}
	//else if (RandomSound < 66)
	//{
	//	if (AttackSoundCue[1] != nullptr)
	//	{
	//		UGameplayStatics::SpawnSoundAttached(
	//			AttackSoundCue[1],
	//			GetRootComponent(),
	//			NAME_None,
	//			FVector::ZeroVector,
	//			EAttachLocation::KeepRelativeOffset,
	//			false,
	//			2.5f,  // Volume multiplier
	//			0.7f   // Pitch multiplier, 0.5로 설정하면 재생 속도가 절반으로 느려짐
	//		);
	//	}
	//}
	//else
	//{
	//	if (AttackSoundCue[2] != nullptr)
	//	{
	//		UGameplayStatics::SpawnSoundAttached(
	//			AttackSoundCue[2],
	//			GetRootComponent(),
	//			NAME_None,
	//			FVector::ZeroVector,
	//			EAttachLocation::KeepRelativeOffset,
	//			false,
	//			2.5f,  // Volume multiplier
	//			0.7f   // Pitch multiplier, 0.5로 설정하면 재생 속도가 절반으로 느려짐
	//		);
	//	}
	//}

//	// 스킬 판정
//	if (RandomValue <= 30)
//	{
//		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, TEXT("SkillCheck~~~~~~~~~~~~~~~~~~"));
//		FHitResult HitResult;
//		FCollisionQueryParams Params(NAME_None, false, this);
//
//		// 구체의 중심을 캐릭터 발 앞에 설정
//		FVector StartLocation = GetActorLocation() + FVector(0.0f, 0.0f, -GetCapsuleComponent()->GetScaledCapsuleHalfHeight());
//		FVector ForwardLocation = StartLocation + GetActorForwardVector() * SkillRange;
//
//		bool bResult = GetWorld()->SweepSingleByChannel(
//			HitResult, // 물리적 충돌이 탐지된 경우 관련 정보를 담을 구조체
//			StartLocation, // 탐색 시작 위치
//			ForwardLocation,
//			FQuat::Identity,
//			ECollisionChannel::ECC_GameTraceChannel2, // 물리 충돌 감지에 사용할 트레이스 채널
//			FCollisionShape::MakeSphere(SkillRadius), // 탐색 사용할  도형
//			Params);
//
//		// 디버그 출력 정보를 담은 변수
//#if ENABLE_DRAW_DEBUG
//		FVector TraceVec = GetActorForwardVector() * SkillRange;
//		FVector Center = StartLocation + TraceVec * 0.5f;  // 구체의 중심을 설정
//		FQuat CapsuleRot = FRotationMatrix::MakeFromZ(TraceVec).ToQuat();
//		FColor DrawColor = bResult ? FColor::Green : FColor::Red;
//		float DebugLifeTime = 1.0f;
//
//		// 디버그 출력
//		DrawDebugSphere(GetWorld(),
//			ForwardLocation,
//			SkillRadius,
//			12, // 세그먼트 수, 더 많을수록 부드러워짐
//			DrawColor,
//			false,
//			DebugLifeTime);
//
//#endif
//		// 액터 감지시
//		if (bResult)
//		{
//			if (HitResult.GetActor() != nullptr)
//			{
//				// 히트 사운드 재생
//				if (HitSoundCue[1] != nullptr)
//				{
//					UGameplayStatics::SpawnSoundAttached(
//						HitSoundCue[1],
//						GetRootComponent(),
//						NAME_None,
//						FVector::ZeroVector,
//						EAttachLocation::KeepRelativeOffset,
//						false,
//						2.5f,  // Volume multiplier
//						0.7f   // Pitch multiplier, 0.5로 설정하면 재생 속도가 절반으로 느려짐
//					);
//				}
//				// 데미지 정보 전달
//				FDamageEvent DamageEvent;
//				HitResult.GetActor()->TakeDamage(SkillDamage, DamageEvent, GetController(), this);
//			}
//		}
//	}
//	// 공격판정
//	else
//	{
//		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, TEXT("AttackCheck~~~~~~~~~~~~~~~~~~"));
//		FHitResult HitResult;
//		FCollisionQueryParams Params(NAME_None, false, this);
//
//		// 구체의 중심을 캐릭터 발 앞에 설정
//		FVector StartLocation = GetActorLocation() + FVector(0.0f, 0.0f, -GetCapsuleComponent()->GetScaledCapsuleHalfHeight());
//		FVector ForwardLocation = StartLocation + GetActorForwardVector() * AttackRange;
//
//		bool bResult = GetWorld()->SweepSingleByChannel(
//			HitResult, // 물리적 충돌이 탐지된 경우 관련 정보를 담을 구조체
//			StartLocation, // 탐색 시작 위치
//			ForwardLocation,
//			FQuat::Identity,
//			ECollisionChannel::ECC_GameTraceChannel2, // 물리 충돌 감지에 사용할 트레이스 채널
//			FCollisionShape::MakeSphere(AttackRadius), // 탐색 사용할  도형
//			Params);
//
//		// 디버그 출력 정보를 담은 변수
//#if ENABLE_DRAW_DEBUG
//		FVector TraceVec = GetActorForwardVector() * AttackRange;
//		FVector Center = StartLocation + TraceVec * 0.5f;  // 구체의 중심을 설정
//		FQuat CapsuleRot = FRotationMatrix::MakeFromZ(TraceVec).ToQuat();
//		FColor DrawColor = bResult ? FColor::Green : FColor::Red;
//		float DebugLifeTime = 1.0f;
//
//		// 디버그 출력
//		DrawDebugSphere(GetWorld(),
//			ForwardLocation,
//			AttackRadius,
//			12, // 세그먼트 수, 더 많을수록 부드러워짐
//			DrawColor,
//			false,
//			DebugLifeTime);
//
//#endif
//		// 액터 감지시
//		if (bResult)
//		{
//			if (HitResult.GetActor() != nullptr)
//			{
//				// 히트 사운드 재생
//				if (HitSoundCue[0] != nullptr)
//				{
//					UGameplayStatics::SpawnSoundAttached(
//						HitSoundCue[0],
//						GetRootComponent(),
//						NAME_None,
//						FVector::ZeroVector,
//						EAttachLocation::KeepRelativeOffset,
//						false,
//						2.5f,  // Volume multiplier
//						0.7f   // Pitch multiplier, 0.5로 설정하면 재생 속도가 절반으로 느려짐
//					);
//				}
//				// 데미지 정보 전달
//				FDamageEvent DamageEvent;
//				HitResult.GetActor()->TakeDamage(AttackDamage, DamageEvent, GetController(), this);
//			}
//		}
//		else
//		{
//			if (MissSoundCue[0] != nullptr)
//			{
//				UGameplayStatics::SpawnSoundAttached(
//					MissSoundCue[0],
//					GetRootComponent(),
//					NAME_None,
//					FVector::ZeroVector,
//					EAttachLocation::KeepRelativeOffset,
//					false,
//					2.5f,  // Volume multiplier
//					0.7f   // Pitch multiplier, 0.5로 설정하면 재생 속도가 절반으로 느려짐
//				);
//			}
//		}
//	}


}
void AMyCharacter::AttackCheckEnd()
{
		CurrentWeapon->StopAttack();
}



// 데미지 받는 함수
float AMyCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	if (Death)
	{
		return Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
	}


	// 입은 데미지 만큼 hp 차감
	UpdateHP(-DamageAmount);


	// hp가 0이 되었을경우 사망 함수 호출
	if (this->hp <= 0)
	{
		this->hp = 0;
		IsDeath();
	}

	return Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
}