캐릭터의 무기가 한개가 아닌 경우를 위해서 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);
}
'Unreal Engine 5 > EDR_Project' 카테고리의 다른 글
EDR_Project / SWeaponAxe (0) | 2024.12.12 |
---|---|
EDR_Project / EDR_Boss_Giant (3) 창을 사용하는 보스 (0) | 2024.12.12 |
EDR_Project / EDR_Boss_Knight (4) 피격 사운드 출력 수정 (0) | 2024.12.02 |
EDR_Project / EDR_Enemy_Skeleton (2) | 2024.12.02 |
EDR_Project / AnimInstance (3) 공격 판정 끝 (0) | 2024.12.02 |