parent
08002c3777
commit
9ef28a708f
|
@ -132,4 +132,6 @@ ManualIPAddress=
|
|||
+FunctionRedirects=(OldName="/Script/D1.CollisionComponent.DisableCollision",NewName="/Script/D1.CollisionComponent.DeactivateCollision")
|
||||
+PropertyRedirects=(OldName="/Script/D1.BaseDualWeapon.RightFoot",NewName="/Script/D1.BaseDualWeapon.RightFootCollisionComponent")
|
||||
+PropertyRedirects=(OldName="/Script/D1.CombatCharacter.ToogleLockOnAction",NewName="/Script/D1.CombatCharacter.ToggleLockOnAction")
|
||||
+ClassRedirects=(OldName="/Script/D1.UI_LockOnComponent",NewName="/Script/D1.LockOnWidgetComponent")
|
||||
+ClassRedirects=(OldName="/Script/D1.UI_LockOnComponent",NewName="/Script/D1.LockOnWidgetComponent")
|
||||
+PropertyRedirects=(OldName="/Script/D1.MasterAI.TargetingWidget",NewName="/Script/D1.MasterAI.TargetingWidgetComponent")
|
||||
+PropertyRedirects=(OldName="/Script/D1.HumanoidEnemy.HealthBar",NewName="/Script/D1.HumanoidEnemy.HealthBarComponent")
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -10,7 +10,8 @@
|
|||
"LoadingPhase": "Default",
|
||||
"AdditionalDependencies": [
|
||||
"Engine",
|
||||
"UMG"
|
||||
"UMG",
|
||||
"AIModule"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "AI/CombatAIController.h"
|
||||
|
||||
#include "MasterAI.h"
|
||||
|
||||
ACombatAIController::ACombatAIController()
|
||||
{
|
||||
}
|
||||
|
||||
void ACombatAIController::OnPossess(APawn* InPawn)
|
||||
{
|
||||
Super::OnPossess(InPawn);
|
||||
|
||||
AMasterAI* AIpawn = Cast<AMasterAI>(InPawn);
|
||||
if(AIpawn)
|
||||
{
|
||||
MasterAI = AIpawn;
|
||||
RunBehaviorTree(MasterAI->GetBeHaviorTree());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AIController.h"
|
||||
#include "CombatAIController.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class D1_API ACombatAIController : public AAIController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ACombatAIController();
|
||||
|
||||
protected:
|
||||
virtual void OnPossess(APawn* InPawn) override;
|
||||
private:
|
||||
TObjectPtr<class AMasterAI> MasterAI;
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "AI/HumanoidEnemy.h"
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Components/WidgetComponent.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "UI/UI_HealthBar.h"
|
||||
|
||||
AHumanoidEnemy::AHumanoidEnemy()
|
||||
{
|
||||
TargetingWidgetComponent->SetRelativeLocation(FVector(0.f, 0.f, 132.f));
|
||||
|
||||
HealthBarComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("HealthBar"));
|
||||
static ConstructorHelpers::FClassFinder<UUserWidget> WidgetRef(TEXT("/Game/CombatSystem/UI/WBP_HealthBar.WBP_HealthBar_C"));
|
||||
if(WidgetRef.Class)
|
||||
{
|
||||
HealthBarComponent->SetWidgetClass(WidgetRef.Class);
|
||||
HealthBarComponent->SetWidgetSpace(EWidgetSpace::Screen);
|
||||
HealthBarComponent->SetDrawSize({150.f, 10.f});
|
||||
HealthBarComponent->SetupAttachment(GetMesh());
|
||||
HealthBarComponent->SetRelativeLocation(FVector(0.f, 0.f, 200.f));
|
||||
HealthBarComponent->SetVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AHumanoidEnemy::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if(APlayerController* playerController = UGameplayStatics::GetPlayerController(GetWorld(), 0))
|
||||
{
|
||||
UUI_HealthBar* HealthBarRef = Cast<UUI_HealthBar>(HealthBarComponent->GetWidget());
|
||||
if(HealthBarRef) //ExposeOnSpawn 대용으로 사용
|
||||
{
|
||||
HealthBarRef->InitializeHealthBar(this->StatsComponent, EStats::Health);
|
||||
HealthBarComponent->SetWidget(HealthBarRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AHumanoidEnemy::OnTargeted(bool bIsTargeted)
|
||||
{
|
||||
Super::OnTargeted(bIsTargeted);
|
||||
HealthBarComponent->SetVisibility(bIsTargeted);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AI/MasterAI.h"
|
||||
#include "HumanoidEnemy.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class D1_API AHumanoidEnemy : public AMasterAI
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
AHumanoidEnemy();
|
||||
|
||||
private:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="UI", meta=(AllowPrivateAccess="true"))
|
||||
TObjectPtr<class UWidgetComponent> HealthBarComponent;
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
protected:
|
||||
// Inherited via ITargetingInterface
|
||||
virtual void OnTargeted(bool bIsTargeted) override;
|
||||
};
|
|
@ -0,0 +1,528 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "AI/MasterAI.h"
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "Components/InputComponent.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "GameFramework/Controller.h"
|
||||
#include "Actor/BaseWeapon.h"
|
||||
#include "Components/CombatComponent.h"
|
||||
#include "Components/StatsComponent.h"
|
||||
#include "Definitions/CombatGameplayTags.h"
|
||||
#include "Engine/DamageEvents.h"
|
||||
#include "NiagaraFunctionLibrary.h"
|
||||
#include "Components/WidgetComponent.h"
|
||||
#include "DamageType/AttackDamageType.h"
|
||||
#include "Kismet/KismetMathLibrary.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
|
||||
AMasterAI::AMasterAI()
|
||||
{
|
||||
// Set size for collision capsule
|
||||
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
|
||||
|
||||
// Don't rotate when the controller rotates. Let that just affect the camera.
|
||||
bUseControllerRotationPitch = false;
|
||||
bUseControllerRotationYaw = false;
|
||||
bUseControllerRotationRoll = false;
|
||||
|
||||
// Configure character movement
|
||||
GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
|
||||
GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); // ...at this rotation rate
|
||||
|
||||
// Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint
|
||||
// instead of recompiling to adjust them
|
||||
GetCharacterMovement()->MaxAcceleration = 1500.f;
|
||||
GetCharacterMovement()->BrakingFrictionFactor = 1.f;
|
||||
GetCharacterMovement()->bUseSeparateBrakingFriction = true; //Sprint 시 애니메이션이 끊기는 거 방지
|
||||
GetCharacterMovement()->JumpZVelocity = 700.f;
|
||||
GetCharacterMovement()->AirControl = 0.35f;
|
||||
GetCharacterMovement()->MaxWalkSpeed = 500.f;
|
||||
GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
|
||||
GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;
|
||||
GetCharacterMovement()->GravityScale = 1.75f;
|
||||
|
||||
GetMesh()->SetRelativeLocationAndRotation(FVector(0.f, 0.f, -100.f), FRotator(0.f, -90.f, 0.f));
|
||||
|
||||
//Setting Widget class is Editor to Blueprint
|
||||
TargetingWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("TargetingWidget"));
|
||||
static ConstructorHelpers::FClassFinder<UUserWidget> WidgetRef(TEXT("/Game/CombatSystem/UI/WBP_LockOn.WBP_LockOn_C"));
|
||||
if(WidgetRef.Class)
|
||||
{
|
||||
TargetingWidgetComponent->SetWidgetClass(WidgetRef.Class);
|
||||
TargetingWidgetComponent->SetWidgetSpace(EWidgetSpace::Screen);
|
||||
TargetingWidgetComponent->SetDrawSize({20.f, 20.f});
|
||||
TargetingWidgetComponent->SetupAttachment(GetMesh());
|
||||
TargetingWidgetComponent->SetRelativeLocation(FVector(0.f, 0.f, 40.f));
|
||||
TargetingWidgetComponent->SetVisibility(false);
|
||||
}
|
||||
|
||||
// Setting CombatComponent
|
||||
CombatComponent = CreateDefaultSubobject<UCombatComponent>(TEXT("CombatComponent"));
|
||||
|
||||
// Setting StateManagerComponent
|
||||
StateManagerComponent = CreateDefaultSubobject<UStateManagerComponent>(TEXT("StateManagerComponent"));
|
||||
StateManagerComponent->OnStateBegin.AddUObject(this, &AMasterAI::CharacterStateBegin);
|
||||
StateManagerComponent->OnStateEnd.AddUObject(this, &AMasterAI::CharacterStateEnd);
|
||||
|
||||
// Setting StatsComponent
|
||||
StatsComponent = CreateDefaultSubobject<UStatsComponent>(TEXT("StatsComponent"));
|
||||
StatsComponent->OnCurrentStatValueUpdated.AddUObject(this, &AMasterAI::CharacterCurrentStatValueUpdated);
|
||||
|
||||
//Setting MovementSpeed
|
||||
MovementSpeedMode = EMovementSpeedMode::Jogging;
|
||||
WalkingSpeed = 200.f;
|
||||
JoggingSpeed = 500.f;
|
||||
SprintSpeed = 700.f;
|
||||
|
||||
PelvisBoneName = TEXT("pelvis");
|
||||
}
|
||||
|
||||
void AMasterAI::BeginPlay()
|
||||
{
|
||||
// Call the base class
|
||||
Super::BeginPlay();
|
||||
|
||||
StatsComponent->InitializeStats();
|
||||
}
|
||||
|
||||
float AMasterAI::TakeDamage(float Damage, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
|
||||
{
|
||||
float fDamage = Super::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser);
|
||||
|
||||
UAttackDamageType* damageTypeClass = Cast<UAttackDamageType>(DamageEvent.DamageTypeClass->GetDefaultObject());
|
||||
if(!IsValid(damageTypeClass))
|
||||
return false;
|
||||
|
||||
if (DamageEvent.IsOfType(FPointDamageEvent::ClassID))
|
||||
{
|
||||
const FPointDamageEvent* PointDamageEvent = static_cast<const FPointDamageEvent*>(&DamageEvent);
|
||||
|
||||
//스텟 관련 처리
|
||||
StatsComponent->TakeDamageOnStat(Damage);
|
||||
|
||||
//앞에서 맞았는지 뒤에서 맞았는지 판별
|
||||
bHitFront = UKismetMathLibrary::InRange_FloatFloat(this->GetDotProductTo(EventInstigator->GetPawn()), -0.1f, 1.f);
|
||||
LastHitInfo = PointDamageEvent->HitInfo;
|
||||
|
||||
//play sound, effect
|
||||
ApplyImpactEffect(damageTypeClass->DamageType);
|
||||
|
||||
if (CanReceiveHitReaction())
|
||||
ApplyHitReaction(damageTypeClass->DamageType);
|
||||
}
|
||||
return fDamage;
|
||||
}
|
||||
|
||||
void AMasterAI::ContinueAttack_Implementation()
|
||||
{
|
||||
if (CombatComponent->GetIsAttackSaved())
|
||||
{
|
||||
CombatComponent->SetIsAttackSaved(false);
|
||||
if (StateManagerComponent->GetCurrentState() == FCombatGameplayTags::Get().Character_State_Attacking)
|
||||
StateManagerComponent->SetCurrentState(FGameplayTag::EmptyTag);
|
||||
AttackEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void AMasterAI::ResetAttack_Implementation()
|
||||
{
|
||||
CombatComponent->ResetAttack();
|
||||
}
|
||||
|
||||
void AMasterAI::ResetCombat_Implementation()
|
||||
{
|
||||
CombatComponent->ResetAttack();
|
||||
StateManagerComponent->ResetState();
|
||||
StateManagerComponent->SetCurrentAction(FGameplayTag::EmptyTag);
|
||||
}
|
||||
|
||||
FRotator AMasterAI::GetDesiredRotation_Implementation()
|
||||
{
|
||||
FVector InputVector = GetCharacterMovement()->GetLastInputVector();
|
||||
if (InputVector.Equals(FVector(0, 0, 0), 0.001f))
|
||||
return GetActorRotation();
|
||||
else
|
||||
return UKismetMathLibrary::MakeRotFromX(GetLastMovementInputVector());
|
||||
}
|
||||
|
||||
bool AMasterAI::CanReceiveDamage_Implementation()
|
||||
{
|
||||
bool result;
|
||||
result = (StateManagerComponent->GetCurrentState() != FCombatGameplayTags::Get().Character_State_Dead);
|
||||
return result;
|
||||
}
|
||||
|
||||
void AMasterAI::SetCanMove_Implementation(bool inputCanMove)
|
||||
{
|
||||
bCanMove = inputCanMove;
|
||||
}
|
||||
|
||||
bool AMasterAI::CanBeTargeted()
|
||||
{
|
||||
return (StateManagerComponent->GetCurrentState() != FCombatGameplayTags::Get().Character_State_Dead);
|
||||
}
|
||||
|
||||
void AMasterAI::OnTargeted(bool bIsTargeted)
|
||||
{
|
||||
TargetingWidgetComponent->SetVisibility(bIsTargeted);
|
||||
}
|
||||
|
||||
void AMasterAI::SetMovementSpeedMode(EMovementSpeedMode NewSpeedMode)
|
||||
{
|
||||
if (NewSpeedMode == MovementSpeedMode)
|
||||
return;
|
||||
|
||||
MovementSpeedMode = NewSpeedMode;
|
||||
|
||||
switch (MovementSpeedMode)
|
||||
{
|
||||
case EMovementSpeedMode::Walking:
|
||||
GetCharacterMovement()->MaxWalkSpeed = WalkingSpeed;
|
||||
break;
|
||||
case EMovementSpeedMode::Jogging:
|
||||
GetCharacterMovement()->MaxWalkSpeed = JoggingSpeed;
|
||||
break;
|
||||
case EMovementSpeedMode::Sprinting:
|
||||
GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AMasterAI::CharacterStateBegin(FGameplayTag CharState)
|
||||
{
|
||||
if (FGameplayTag::EmptyTag == CharState)
|
||||
{/*None*/}
|
||||
else if (FCombatGameplayTags::Get().Character_State_Attacking == CharState)
|
||||
{/*None*/}
|
||||
else if (FCombatGameplayTags::Get().Character_State_Dodging == CharState)
|
||||
{/*None*/}
|
||||
else if (FCombatGameplayTags::Get().Character_State_GeneralActionState == CharState)
|
||||
{/*None*/}
|
||||
else if (FCombatGameplayTags::Get().Character_State_Dead == CharState)
|
||||
{
|
||||
PerformDeath();
|
||||
}
|
||||
else if (FCombatGameplayTags::Get().Character_State_Disable == CharState)
|
||||
{/*None*/}
|
||||
}
|
||||
|
||||
void AMasterAI::CharacterStateEnd(FGameplayTag CharState)
|
||||
{
|
||||
if (FGameplayTag::EmptyTag == CharState)
|
||||
{/*None*/}
|
||||
else if (FCombatGameplayTags::Get().Character_State_Attacking == CharState)
|
||||
{/*None*/}
|
||||
else if (FCombatGameplayTags::Get().Character_State_Dodging == CharState)
|
||||
{/*None*/}
|
||||
else if (FCombatGameplayTags::Get().Character_State_GeneralActionState == CharState)
|
||||
{/*None*/}
|
||||
else if (FCombatGameplayTags::Get().Character_State_Dead == CharState)
|
||||
{/*None*/}
|
||||
else if (FCombatGameplayTags::Get().Character_State_Disable == CharState)
|
||||
{/*None*/}
|
||||
}
|
||||
|
||||
void AMasterAI::CharacterCurrentStatValueUpdated(EStats statType, float value)
|
||||
{
|
||||
if (!(statType == EStats::Health) || value > 0.f)
|
||||
return;
|
||||
|
||||
StateManagerComponent->SetCurrentState(FCombatGameplayTags::Get().Character_State_Dead);
|
||||
}
|
||||
|
||||
void AMasterAI::ToggleCombatEvent()
|
||||
{
|
||||
if (!CanPerformToggleCombat())
|
||||
return;
|
||||
|
||||
if (!CombatComponent->GetCombatEnabled())
|
||||
PerformAction(FCombatGameplayTags::Get().Character_State_GeneralActionState, FCombatGameplayTags::Get().Character_Action_EnterCombat, 0);
|
||||
else
|
||||
PerformAction(FCombatGameplayTags::Get().Character_State_GeneralActionState, FCombatGameplayTags::Get().Character_Action_ExitCombat, 0);
|
||||
}
|
||||
|
||||
void AMasterAI::AttackEvent()
|
||||
{
|
||||
if (!CanPerformAttack())
|
||||
return;
|
||||
|
||||
if (CombatComponent->GetCombatEnabled())
|
||||
PerformAttack(GetDesiredAttackType(), CombatComponent->GetAttackCount());
|
||||
else
|
||||
ToggleCombatEvent();
|
||||
}
|
||||
|
||||
void AMasterAI::ChargeAttackEvent()
|
||||
{
|
||||
if (CanPerformAttack())
|
||||
PerformAttack(FCombatGameplayTags::Get().Character_Action_Attack_ChargedAttack, CombatComponent->GetAttackCount());
|
||||
}
|
||||
|
||||
void AMasterAI::ApplyHitReactionPhysicsVelocity(float InitialSpeed)
|
||||
{
|
||||
if (!GetMesh())
|
||||
return;
|
||||
|
||||
GetMesh()->SetPhysicsLinearVelocity(GetActorForwardVector() * -InitialSpeed, false, PelvisBoneName);
|
||||
}
|
||||
|
||||
void AMasterAI::EnableRagdoll()
|
||||
{
|
||||
GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None, 0); //movement 더 이상 없게 바꿈
|
||||
GetCapsuleComponent()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Ignore); //충돌무시
|
||||
GetCapsuleComponent()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Camera, ECollisionResponse::ECR_Ignore); //카메라 충돌무시
|
||||
FAttachmentTransformRules rules(EAttachmentRule::KeepWorld, EAttachmentRule::KeepWorld, EAttachmentRule::KeepWorld, true);
|
||||
GetMesh()->SetCollisionProfileName(TEXT("ragdoll"), true); //ragdoll 로 변경
|
||||
GetMesh()->SetAllBodiesBelowSimulatePhysics(PelvisBoneName, true, true);
|
||||
GetMesh()->SetAllBodiesBelowPhysicsBlendWeight(PelvisBoneName, 1.f);
|
||||
}
|
||||
|
||||
void AMasterAI::DisableSprint()
|
||||
{
|
||||
UWorld* World = GEngine->GetWorldFromContextObjectChecked(this);
|
||||
if (World)
|
||||
World->GetTimerManager().ClearTimer(StaminaTimerHandle);
|
||||
|
||||
if (GetCombatMovementSpeedMode() == EMovementSpeedMode::Sprinting)
|
||||
SetMovementSpeedMode(EMovementSpeedMode::Jogging);
|
||||
}
|
||||
|
||||
void AMasterAI::SprintStaminaCost()
|
||||
{
|
||||
if (!CanPerformSprint())
|
||||
{
|
||||
DisableSprint();
|
||||
return;
|
||||
}
|
||||
|
||||
StatsComponent->ModifyCurrentStatValue(EStats::Stamina, -2.f);
|
||||
if (StatsComponent->GetCurrentStatValue(EStats::Stamina) < 10.f)
|
||||
{
|
||||
DisableSprint();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AMasterAI::ApplyHitReaction(EDamageType InDamageType)
|
||||
{
|
||||
switch (InDamageType)
|
||||
{
|
||||
case EDamageType::None:
|
||||
PerformHitStun();
|
||||
break;
|
||||
case EDamageType::MeleeDamage:
|
||||
PerformHitStun();
|
||||
break;
|
||||
case EDamageType::KnockdownDamage:
|
||||
PerformKnockdown();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AMasterAI::ApplyImpactEffect(EDamageType InDamageType)
|
||||
{
|
||||
//Play Sound
|
||||
UGameplayStatics::PlaySoundAtLocation(this, HitSound, LastHitInfo.Location);
|
||||
|
||||
//Hit Effect
|
||||
UNiagaraFunctionLibrary::SpawnSystemAtLocation(GetWorld(), HitEmitter, LastHitInfo.Location);
|
||||
}
|
||||
|
||||
void AMasterAI::PerformAttack(FGameplayTag attackType, int32 attackIndex)
|
||||
{
|
||||
TArray<UAnimMontage*> montages = GetActionMontage(attackType);
|
||||
int32 attackIdx = attackIndex;
|
||||
|
||||
if (montages.Num() <= attackIdx)
|
||||
attackIdx = 0;
|
||||
if (!montages.IsValidIndex(attackIdx))
|
||||
return;
|
||||
|
||||
UAnimMontage* attackMontage = montages[attackIdx];
|
||||
if (!IsValid(attackMontage))
|
||||
{
|
||||
FString debugStr = FString::Printf(TEXT("Index %d Is NOT VALID!!"), attackIdx);
|
||||
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, debugStr);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
StateManagerComponent->SetCurrentState(FCombatGameplayTags::Get().Character_State_Attacking);
|
||||
StateManagerComponent->SetCurrentAction(attackType);
|
||||
|
||||
PlayAnimMontage(attackMontage);
|
||||
|
||||
int idx = attackIdx + 1;
|
||||
if (idx >= montages.Num())
|
||||
idx = 0;
|
||||
|
||||
CombatComponent->SetAttackCount(idx);
|
||||
}
|
||||
}
|
||||
|
||||
bool AMasterAI::PerformAction(FGameplayTag characterState, FGameplayTag characterAction, int32 montageIndex)
|
||||
{
|
||||
TArray<UAnimMontage*> montages = GetActionMontage(characterAction);
|
||||
int32 montageIdx = montageIndex;
|
||||
|
||||
if (montages.Num() <= montageIdx)
|
||||
montageIdx = 0;
|
||||
if (!montages.IsValidIndex(montageIdx))
|
||||
return false;
|
||||
|
||||
UAnimMontage* actionMontage = montages[montageIdx];
|
||||
if (IsValid(actionMontage))
|
||||
{
|
||||
StateManagerComponent->SetCurrentState(characterState);
|
||||
StateManagerComponent->SetCurrentAction(characterAction);
|
||||
PlayAnimMontage(actionMontage);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
FString str = FString::Printf(TEXT("Dodge Index %n is NOT VALID!!"), montageIdx);
|
||||
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AMasterAI::PerformDeath()
|
||||
{
|
||||
EnableRagdoll();
|
||||
ApplyHitReactionPhysicsVelocity(2000.f); //충돌을 좀 더 그럴싸하게 하기 위해서 뒷방향으로 충격
|
||||
|
||||
FTimerHandle deathTimer;
|
||||
GetWorld()->GetTimerManager().SetTimer(deathTimer, FTimerDelegate::CreateLambda([&]()
|
||||
{
|
||||
this->Destroy();
|
||||
}), 4.f, false); // 4초 후에 object 삭제
|
||||
}
|
||||
|
||||
bool AMasterAI::PerformHitStun()
|
||||
{
|
||||
UAnimMontage* hitMontage = nullptr;
|
||||
if(bHitFront)
|
||||
hitMontage = HitStunFrontMontage;
|
||||
else
|
||||
hitMontage = HitStunBackMontage;
|
||||
|
||||
if(!IsValid(hitMontage))
|
||||
return false;
|
||||
|
||||
StateManagerComponent->SetCurrentState(FCombatGameplayTags::Get().Character_State_Disable);
|
||||
PlayAnimMontage(hitMontage);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AMasterAI::PerformKnockdown()
|
||||
{
|
||||
UAnimMontage* hitMontage = nullptr;
|
||||
if(bHitFront)
|
||||
hitMontage = KnockdownFrontMontage;
|
||||
else
|
||||
hitMontage = KnockdownBackMontage;
|
||||
|
||||
if(!IsValid(hitMontage))
|
||||
return false;
|
||||
|
||||
StateManagerComponent->SetCurrentState(FCombatGameplayTags::Get().Character_State_Disable);
|
||||
PlayAnimMontage(hitMontage);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AMasterAI::CanPerformToggleCombat()
|
||||
{
|
||||
bool ReturnValue = true;
|
||||
|
||||
FGameplayTagContainer inputContainer;
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Attacking);
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dodging);
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Disable);
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_GeneralActionState);
|
||||
|
||||
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
|
||||
ReturnValue &= !GetCharacterMovement()->IsFalling();
|
||||
return ReturnValue;
|
||||
}
|
||||
|
||||
bool AMasterAI::CanPerformAttack()
|
||||
{
|
||||
bool ReturnValue = true;
|
||||
|
||||
FGameplayTagContainer inputContainer;
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Attacking);
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dodging);
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Disable);
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_GeneralActionState);
|
||||
|
||||
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
|
||||
ReturnValue &= (StatsComponent->GetCurrentStatValue(EStats::Stamina) >= 10.f);
|
||||
|
||||
return ReturnValue;
|
||||
}
|
||||
|
||||
bool AMasterAI::CanJumping()
|
||||
{
|
||||
bool ReturnValue = true;
|
||||
|
||||
FGameplayTagContainer inputContainer;
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dodging);
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Disable);
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_GeneralActionState);
|
||||
|
||||
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
|
||||
ReturnValue &= !GetCharacterMovement()->IsFalling();
|
||||
return ReturnValue;
|
||||
}
|
||||
|
||||
bool AMasterAI::CanReceiveHitReaction()
|
||||
{
|
||||
bool ReturnValue = true;
|
||||
|
||||
FGameplayTagContainer inputContainer;
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
|
||||
|
||||
ReturnValue &= !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
|
||||
return ReturnValue;
|
||||
}
|
||||
|
||||
bool AMasterAI::CanPerformSprint()
|
||||
{
|
||||
return (FMath::IsNearlyEqual(GetVelocity().Length(), 0.f)) == false;
|
||||
}
|
||||
|
||||
FGameplayTag AMasterAI::GetDesiredAttackType()
|
||||
{
|
||||
if (GetCharacterMovement()->IsFalling())
|
||||
return FCombatGameplayTags::Get().Character_Action_Attack_FallingAttack;
|
||||
|
||||
if (GetCombatMovementSpeedMode() == EMovementSpeedMode::Sprinting)
|
||||
return FCombatGameplayTags::Get().Character_Action_Attack_SprintAttack;
|
||||
|
||||
return FCombatGameplayTags::Get().Character_Action_Attack_LightAttack;
|
||||
}
|
||||
|
||||
TArray<UAnimMontage*> AMasterAI::GetActionMontage(FGameplayTag characterAction)
|
||||
{
|
||||
TArray<UAnimMontage*> outputArr;
|
||||
|
||||
if (FCombatGameplayTags::Get().Character_Action_Attack_CloseRange == characterAction)
|
||||
outputArr = CloseRangeAttackMontage;
|
||||
else if (FCombatGameplayTags::Get().Character_Action_Dodge == characterAction)
|
||||
outputArr = DodgeMontage;
|
||||
else if (FCombatGameplayTags::Get().Character_Action_EnterCombat == characterAction)
|
||||
outputArr.Add(EnterCombat);
|
||||
else if (FCombatGameplayTags::Get().Character_Action_ExitCombat == characterAction)
|
||||
outputArr.Add(ExitCombat);
|
||||
|
||||
return outputArr;
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "Interface/CombatInterface.h"
|
||||
#include "Components/StateManagerComponent.h"
|
||||
#include "Components/StatsComponent.h"
|
||||
#include "Components/TargetingComponent.h"
|
||||
#include "Definitions/GameEnums.h"
|
||||
#include "Interface/TargetingInterface.h"
|
||||
#include "MasterAI.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class D1_API AMasterAI : public ACharacter, public ICombatInterface, public ITargetingInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMasterAI();
|
||||
|
||||
protected:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="UI", meta=(AllowPrivateAccess="true"))
|
||||
TObjectPtr<class UWidgetComponent> TargetingWidgetComponent;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components", meta=(AllowPrivateAccess="true"))
|
||||
TObjectPtr<class UCombatComponent> CombatComponent;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components", meta=(AllowPrivateAccess="true"))
|
||||
TObjectPtr<UStateManagerComponent> StateManagerComponent;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Components", meta=(AllowPrivateAccess="true"))
|
||||
TObjectPtr<UStatsComponent> StatsComponent;
|
||||
protected:
|
||||
// APawn interface
|
||||
virtual void BeginPlay() override;
|
||||
virtual float TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override;
|
||||
|
||||
public:
|
||||
// Inherited via ICombatInterface
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
|
||||
void ContinueAttack();
|
||||
virtual void ContinueAttack_Implementation() override;
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
|
||||
void ResetAttack();
|
||||
virtual void ResetAttack_Implementation() override;
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
|
||||
void ResetCombat();
|
||||
virtual void ResetCombat_Implementation() override;
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
|
||||
FRotator GetDesiredRotation();
|
||||
virtual FRotator GetDesiredRotation_Implementation() override;
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
|
||||
bool CanReceiveDamage();
|
||||
virtual bool CanReceiveDamage_Implementation() override;
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
|
||||
void SetCanMove(bool inputCanMove);
|
||||
virtual void SetCanMove_Implementation(bool inputCanMove) override;
|
||||
|
||||
virtual EMovementSpeedMode GetCombatMovementSpeedMode() override { return GetMovementSpeedMode(); }
|
||||
|
||||
// Inherited via ITargetingInterface
|
||||
virtual bool CanBeTargeted() override;
|
||||
virtual void OnTargeted(bool bIsTargeted) override;
|
||||
public:
|
||||
void SetMovementSpeedMode(EMovementSpeedMode NewSpeedMode);
|
||||
FORCEINLINE EMovementSpeedMode GetMovementSpeedMode() const { return MovementSpeedMode; }
|
||||
|
||||
private://Delegate
|
||||
void CharacterStateBegin(FGameplayTag CharState);
|
||||
void CharacterStateEnd(FGameplayTag CharState);
|
||||
void CharacterCurrentStatValueUpdated(EStats statType, float value);
|
||||
|
||||
private:
|
||||
void ToggleCombatEvent();
|
||||
void AttackEvent();
|
||||
void ChargeAttackEvent();
|
||||
void ApplyHitReactionPhysicsVelocity(float InitialSpeed);
|
||||
void EnableRagdoll();
|
||||
void DisableSprint();
|
||||
void SprintStaminaCost();
|
||||
void ApplyHitReaction(EDamageType InDamageType);
|
||||
void ApplyImpactEffect(EDamageType InDamageType);
|
||||
|
||||
private:
|
||||
void PerformAttack(FGameplayTag attackType, int32 attackIndex);
|
||||
bool PerformAction(FGameplayTag characterState, FGameplayTag characterAction, int32 montageIndex);
|
||||
void PerformDeath();
|
||||
bool PerformHitStun();
|
||||
bool PerformKnockdown();
|
||||
|
||||
protected: //Check Func
|
||||
bool CanPerformToggleCombat();
|
||||
bool CanPerformAttack();
|
||||
bool CanJumping();
|
||||
bool CanReceiveHitReaction();
|
||||
bool CanPerformSprint();
|
||||
FGameplayTag GetDesiredAttackType();
|
||||
TArray<UAnimMontage*> GetActionMontage(FGameplayTag characterAction);
|
||||
|
||||
public: //Getter
|
||||
FORCEINLINE class UBehaviorTree* GetBeHaviorTree() {return BehaviorTree;}
|
||||
|
||||
private:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Initialization", meta=(AllowPrivateAccess="true"))
|
||||
TObjectPtr<class UBehaviorTree> BehaviorTree;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hit", meta = (AllowPrivateAccess = "true"))
|
||||
FName PelvisBoneName;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hit", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<class USoundBase> HitSound;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hit", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<class UNiagaraSystem> HitEmitter;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MovementSpeed", meta = (AllowPrivateAccess = "true"))
|
||||
EMovementSpeedMode MovementSpeedMode;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MovementSpeed", meta = (AllowPrivateAccess = "true"))
|
||||
float WalkingSpeed;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MovementSpeed", meta = (AllowPrivateAccess = "true"))
|
||||
float JoggingSpeed;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MovementSpeed", meta = (AllowPrivateAccess = "true"))
|
||||
float SprintSpeed;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Montage|HitReactions", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UAnimMontage> HitStunFrontMontage;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Montage|HitReactions", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UAnimMontage> HitStunBackMontage;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Montage|HitReactions", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UAnimMontage> KnockdownFrontMontage;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Montage|HitReactions", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UAnimMontage> KnockdownBackMontage;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Montage|Attacks", meta = (AllowPrivateAccess = "true"))
|
||||
TArray<TObjectPtr<UAnimMontage>> CloseRangeAttackMontage;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Montage|Actions", meta = (AllowPrivateAccess = "true"))
|
||||
TArray<TObjectPtr<UAnimMontage>> DodgeMontage;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Montage|Actions", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UAnimMontage> EnterCombat;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Montage|Actions", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UAnimMontage> ExitCombat;
|
||||
|
||||
private:
|
||||
FTimerHandle StaminaTimerHandle;
|
||||
|
||||
private:
|
||||
bool bCanMove = true;
|
||||
bool bHitFront;
|
||||
FHitResult LastHitInfo;
|
||||
};
|
|
@ -6,7 +6,7 @@
|
|||
#include "Components/CombatComponent.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "Kismet/KismetMathLibrary.h"
|
||||
#include "KismetAnimationLibrary.h"
|
||||
|
||||
void UCombatAnimInstance::NativeInitializeAnimation()
|
||||
{
|
||||
|
@ -36,8 +36,8 @@ void UCombatAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
|
|||
|
||||
FVector2D vel = { Velocity.X, Velocity.Y };
|
||||
GroundSpeed = vel.Length();
|
||||
|
||||
Direction = CalculateDirection(CharacterMovementComponent->Velocity, Character->GetActorRotation());
|
||||
|
||||
Direction = UKismetAnimationLibrary::CalculateDirection(CharacterMovementComponent->Velocity, Character->GetActorRotation());
|
||||
|
||||
ShouldMove = (GroundSpeed > 3.f) && (CharacterMovementComponent->GetCurrentAcceleration().Length() != 0);
|
||||
}
|
||||
|
|
|
@ -51,6 +51,8 @@ ACombatCharacter::ACombatCharacter()
|
|||
GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;
|
||||
GetCharacterMovement()->GravityScale = 1.75f;
|
||||
|
||||
GetMesh()->SetRelativeLocationAndRotation(FVector(0.f, 0.f, -100.f), FRotator(0.f, -90.f, 0.f));
|
||||
|
||||
// Create a camera boom (pulls in towards the player if there is a collision)
|
||||
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
|
||||
CameraBoom->SetupAttachment(RootComponent);
|
||||
|
@ -359,18 +361,7 @@ void ACombatCharacter::LightChargeAttack(const FInputActionInstance& Instance)
|
|||
AttackHeldTime = Instance.GetElapsedTime();
|
||||
bAttackCharged = (AttackHeldTime >= ChargeAttackTime);
|
||||
if (bAttackCharged)
|
||||
{
|
||||
if (CanPerformAttack())
|
||||
{
|
||||
PerformAttack(FCombatGameplayTags::Get().Character_Action_Attack_ChargedAttack, CombatComponent->GetAttackCount());
|
||||
ABaseWeapon* pBaseWeapon = CombatComponent->GetMainWeapon();
|
||||
if (IsValid(pBaseWeapon))
|
||||
{
|
||||
StatsComponent->ModifyCurrentStatValue(EStats::Stamina, -1.f * pBaseWeapon->GetStatCostForAction());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
ChargeAttackEvent();
|
||||
}
|
||||
|
||||
void ACombatCharacter::HeavyAttack(const FInputActionValue& Value)
|
||||
|
@ -556,6 +547,31 @@ void ACombatCharacter::AttackEvent()
|
|||
ToggleCombatEvent();
|
||||
}
|
||||
|
||||
void ACombatCharacter::ChargeAttackEvent()
|
||||
{
|
||||
if (CanPerformAttack())
|
||||
{
|
||||
PerformAttack(FCombatGameplayTags::Get().Character_Action_Attack_ChargedAttack, CombatComponent->GetAttackCount());
|
||||
ABaseWeapon* pBaseWeapon = CombatComponent->GetMainWeapon();
|
||||
if (IsValid(pBaseWeapon))
|
||||
{
|
||||
StatsComponent->ModifyCurrentStatValue(EStats::Stamina, -1.f * pBaseWeapon->GetStatCostForAction());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ACombatCharacter::ResetChargeAttack()
|
||||
{
|
||||
AttackHeldTime = 0.f;
|
||||
if (bAttackCharged)
|
||||
{
|
||||
bAttackCharged = false;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void ACombatCharacter::ApplyHitReactionPhysicsVelocity(float InitialSpeed)
|
||||
{
|
||||
if (!GetMesh())
|
||||
|
@ -577,18 +593,6 @@ void ACombatCharacter::EnableRagdoll()
|
|||
GetMesh()->SetAllBodiesBelowPhysicsBlendWeight(PelvisBoneName, 1.f);
|
||||
}
|
||||
|
||||
bool ACombatCharacter::ResetChargeAttack()
|
||||
{
|
||||
AttackHeldTime = 0.f;
|
||||
if (bAttackCharged)
|
||||
{
|
||||
bAttackCharged = false;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void ACombatCharacter::DisableSprint()
|
||||
{
|
||||
UWorld* World = GEngine->GetWorldFromContextObjectChecked(this);
|
||||
|
@ -644,6 +648,9 @@ void ACombatCharacter::ApplyImpactEffect(EDamageType InDamageType)
|
|||
|
||||
void ACombatCharacter::PerformAttack(FGameplayTag attackType, int32 attackIndex)
|
||||
{
|
||||
if(!CombatComponent->GetCombatEnabled())
|
||||
return;
|
||||
|
||||
ABaseWeapon* CurrentWeapon = CombatComponent->GetMainWeapon();
|
||||
if (!CurrentWeapon)
|
||||
return;
|
||||
|
|
|
@ -140,9 +140,10 @@ private://Delegate
|
|||
private:
|
||||
void ToggleCombatEvent();
|
||||
void AttackEvent();
|
||||
void ChargeAttackEvent();
|
||||
bool ResetChargeAttack();
|
||||
void ApplyHitReactionPhysicsVelocity(float InitialSpeed);
|
||||
void EnableRagdoll();
|
||||
bool ResetChargeAttack();
|
||||
void DisableSprint();
|
||||
void SprintStaminaCost();
|
||||
void ApplyHitReaction(EDamageType InDamageType);
|
||||
|
|
|
@ -10,7 +10,7 @@ public class D1 : ModuleRules
|
|||
|
||||
PublicDependencyModuleNames.AddRange(new string[] {
|
||||
"Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay",
|
||||
"EnhancedInput", "GameplayTags", "Niagara"
|
||||
"EnhancedInput", "AnimGraphRuntime", "GameplayTags", "Niagara",
|
||||
});
|
||||
|
||||
PublicIncludePaths.AddRange(new string[] { "D1" });
|
||||
|
|
|
@ -86,4 +86,9 @@ void FCombatGameplayTags::InitializeNativeGameplayTags()
|
|||
FName("Character.Action.Attack.SprintAttack"),
|
||||
FString("Action Attack SprintAttack")
|
||||
);
|
||||
|
||||
GameplayTags.Character_Action_Attack_CloseRange = UGameplayTagsManager::Get().AddNativeGameplayTag(
|
||||
FName("Character.Action.Attack.CloseRange"),
|
||||
FString("Action Attack CloseRange")
|
||||
);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ public:
|
|||
FGameplayTag Character_Action_Attack_HeavyAttack;
|
||||
FGameplayTag Character_Action_Attack_LightAttack;
|
||||
FGameplayTag Character_Action_Attack_SprintAttack;
|
||||
FGameplayTag Character_Action_Attack_CloseRange;
|
||||
|
||||
private:
|
||||
static FCombatGameplayTags GameplayTags;
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "TestDummyCharacter.h"
|
||||
#include "Engine/DamageEvents.h"
|
||||
#include "Actor/BaseEquippable.h"
|
||||
#include "Components/WidgetComponent.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
|
||||
ATestDummyCharacter::ATestDummyCharacter()
|
||||
{
|
||||
//Setting Widget class is Editor to Blueprint
|
||||
LockOnWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("LockOnWidget"));
|
||||
LockOnWidgetComponent->SetWidgetSpace(EWidgetSpace::Screen);
|
||||
LockOnWidgetComponent->SetDrawSize({20.f, 20.f});
|
||||
LockOnWidgetComponent->SetupAttachment(RootComponent);
|
||||
LockOnWidgetComponent->SetRelativeLocation(FVector(0.f, 0.f, 40.f));
|
||||
LockOnWidgetComponent->SetVisibility(false);
|
||||
}
|
||||
|
||||
void ATestDummyCharacter::BeginPlay()
|
||||
{
|
||||
ACharacter::BeginPlay();
|
||||
StatsComponent->InitializeStats();
|
||||
|
||||
for (auto armor : StartingEquipment)
|
||||
{
|
||||
FActorSpawnParameters spawnParam;
|
||||
spawnParam.Owner = this;
|
||||
spawnParam.Instigator = this;
|
||||
ABaseEquippable* SpawnItem = Cast<ABaseEquippable>(GetWorld()->SpawnActor(armor, &GetActorTransform(), spawnParam));
|
||||
if (SpawnItem)
|
||||
SpawnItem->OnEquipped();
|
||||
}
|
||||
}
|
||||
|
||||
bool ATestDummyCharacter::CanBeTargeted()
|
||||
{
|
||||
FGameplayTagContainer inputContainer;
|
||||
inputContainer.AddTag(FCombatGameplayTags::Get().Character_State_Dead);
|
||||
|
||||
return !StateManagerComponent->IsCurrentStateEqualToAny(inputContainer);
|
||||
}
|
||||
|
||||
void ATestDummyCharacter::OnTargeted(bool bIsTargeted)
|
||||
{
|
||||
LockOnWidgetComponent->SetVisibility(bIsTargeted);
|
||||
}
|
||||
|
||||
//float ATestDummyCharacter::TakeDamage(float Damage, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
|
||||
//{
|
||||
// float fDamage = ACharacter::TakeDamage(Damage, DamageEvent, EventInstigator, DamageCauser);
|
||||
//
|
||||
// if (DamageEvent.IsOfType(FPointDamageEvent::ClassID))
|
||||
// {
|
||||
// const FPointDamageEvent* PointDamageEvent = static_cast<const FPointDamageEvent*>(&DamageEvent);
|
||||
//
|
||||
// StatsComponent->TakeDamageOnStat(Damage);
|
||||
//
|
||||
// //Play Sound
|
||||
// UGameplayStatics::PlaySoundAtLocation(this, HitSound, PointDamageEvent->HitInfo.Location);
|
||||
//
|
||||
// //Hit Effect
|
||||
// UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), HitEmitter, PointDamageEvent->HitInfo.Location);
|
||||
//
|
||||
// if (CanReceiveHitReaction())
|
||||
// {
|
||||
// StateManagerComponent->SetCurrentState(ECharacterState::Disable);
|
||||
//
|
||||
// //Play Animation
|
||||
// PlayAnimMontage(HitMontage);
|
||||
// }
|
||||
// }
|
||||
// return fDamage;
|
||||
//}
|
|
@ -1,33 +0,0 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "CombatCharacter.h"
|
||||
#include "Interface/TargetingInterface.h"
|
||||
#include "TestDummyCharacter.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class D1_API ATestDummyCharacter : public ACombatCharacter, public ITargetingInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ATestDummyCharacter();
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
protected:
|
||||
// Inherited via ITargetingInterface
|
||||
virtual bool CanBeTargeted() override;
|
||||
virtual void OnTargeted(bool bIsTargeted) override;
|
||||
public:
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
TArray<TSubclassOf<class ABaseEquippable>> StartingEquipment;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="UI")
|
||||
TObjectPtr<class UWidgetComponent> LockOnWidgetComponent;
|
||||
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "UI/UI_HealthBar.h"
|
||||
|
||||
#include "Components/ProgressBar.h"
|
||||
|
||||
void UUI_HealthBar::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
|
||||
if(bUsePlayerStatsComponent)
|
||||
{
|
||||
if(const APlayerController* playerController = GetOwningPlayer())
|
||||
StatsComponent = playerController->GetComponentByClass<UStatsComponent>();
|
||||
}
|
||||
|
||||
InitializeHealthBar(StatsComponent, StatType);
|
||||
}
|
||||
|
||||
void UUI_HealthBar::InitializeHealthBar(UStatsComponent* InputStatsComponent, EStats InputStatType)
|
||||
{
|
||||
if(!IsValid(InputStatsComponent))
|
||||
return;
|
||||
if(StatType != EStats::None) //Initial은 한번만 탈 수 있도록
|
||||
return;
|
||||
|
||||
StatType = InputStatType;
|
||||
StatsComponent = InputStatsComponent;
|
||||
|
||||
InputStatsComponent->OnCurrentStatValueUpdated.AddUObject(this, &UUI_HealthBar::HealthBarStatValueUpdated);
|
||||
HealthBarStatValueUpdated(StatType, InputStatsComponent->GetCurrentStatValue(StatType)); //최초 1번 실행
|
||||
}
|
||||
|
||||
void UUI_HealthBar::HealthBarStatValueUpdated(const EStats Stat, const float Value) const
|
||||
{
|
||||
if (StatType != Stat)
|
||||
return;
|
||||
|
||||
if (!IsValid(StatsComponent))
|
||||
return;
|
||||
|
||||
const float Percent = Value / StatsComponent->GetMaxStatValue(StatType);
|
||||
HealthBar->SetPercent(Percent);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Components/StatsComponent.h"
|
||||
#include "UI_HealthBar.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class D1_API UUI_HealthBar : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
virtual void NativeConstruct() override;
|
||||
private:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(BindWidget, AllowPrivateAccess="true"))
|
||||
TObjectPtr<class UProgressBar> HealthBar;
|
||||
|
||||
UPROPERTY(visibleAnywhere, BlueprintReadWrite, Category="Components" , meta=(AllowPrivateAccess="true"))
|
||||
TObjectPtr<UStatsComponent> StatsComponent;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Initialization" , meta=(AllowPrivateAccess="true"))
|
||||
EStats StatType;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Initialization" , meta=(AllowPrivateAccess="true"))
|
||||
bool bUsePlayerStatsComponent;
|
||||
|
||||
public:
|
||||
void InitializeHealthBar(UStatsComponent* InputStatsComponent, EStats InputStatType);
|
||||
|
||||
public:
|
||||
FORCEINLINE void SetStatType(EStats InputStatType) { StatType = InputStatType; }
|
||||
FORCEINLINE EStats GetStatType() { return StatType; }
|
||||
|
||||
public: //Delegate
|
||||
void HealthBarStatValueUpdated(const EStats Stat, const float Value) const;
|
||||
};
|
Loading…
Reference in New Issue