// Fill out your copyright notice in the Description page of Project Settings.
#include "Enemy/NormalEnemyController.h"
#include "Enemy/NormalEnemy.h"
#include "NavigationSystem.h"
#include "TimerManager.h"
#include <GameFramework/CharacterMovementComponent.h>
#include "Kismet/KismetMathLibrary.h"
#include "Components/SkeletalMeshComponent.h"
#include "Animation/AnimInstance.h"
#include "Animation/AnimMontage.h"
ANormalEnemyController::ANormalEnemyController()
{
//实例化导航系统组件
NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(this);////此处的this报空指针
}
void ANormalEnemyController::OnPossess(APawn* InPawn)
{
Super::OnPossess(InPawn);
//类型转换
EnemyPawn = Cast<ANormalEnemy>(InPawn);
//获得动画实例
AnimInstance = EnemyPawn->GetMesh()->GetAnimInstance();
//调整巡逻速度
EnemyPawn->GetCharacterMovement()->MaxWalkSpeed = PatrolWalkSpeed;
}
int ANormalEnemyController::GetNextAnimationIndex()
{
//随机动画索引
CurrentAttackIndex= FMath::RandRange(0, 2);
return CurrentAttackIndex;
}
bool ANormalEnemyController::bInAttackRange()
{
//判断TargetActor有效性
if (TargetActor)
{
//判断距离有效性
return(EnemyPawn->GetDistanceTo(TargetActor) <= AttackRange);
}
else
{
return false;
}
}
void ANormalEnemyController::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result)
{
Super::OnMoveCompleted(RequestID, Result);
//判断返回状态
if (bIsRuningBack)
{
//调整返回状态
bIsRuningBack = false;
//重置巡逻状态
bIsPatrolling = true;
//调整巡逻速度
EnemyPawn->GetCharacterMovement()->MaxWalkSpeed = PatrolWalkSpeed;
}
//判断是否在攻击范围
if (bInAttackRange())
{
//调用攻击函数
PerformAttack();
return;
}
//判断巡逻状态
if (bIsPatrolling)
{
//设置Idle时间
float IdleTime = FMath::RandRange(1, 10);
//设置定时器
GetWorldTimerManager().SetTimer(TimerHandle_Patrol, this, &ANormalEnemyController::DetectedPatrol, IdleTime, false);
}
}
void ANormalEnemyController::DetectedPatrol()
{
//判断巡逻状态
if (bIsPatrolling)
{
Patrol();
}
}
void ANormalEnemyController::PerformAttack()
{
//判断敌人状态
if (!EnemyPawn->GetbDead() && !bIsRuningBack && bWasAggroed)
{
//停止巡逻
bIsPatrolling = false;
//停止移动
EnemyPawn->GetCharacterMovement()->StopMovementImmediately();
//判断目标有效性
if (TargetActor)
{
//获取朝向目标的角度(1、敌人位置,2、目标位置)
FRotator Rotation= UKismetMathLibrary::FindLookAtRotation(EnemyPawn->GetActorLocation(), TargetActor->GetActorLocation());
//设置敌人角度
EnemyPawn->SetActorRotation(Rotation);
}
//设置当前动画
CurrentAttackMontage = EnemyPawn->AttackAnimations[CurrentAttackIndex];
//播放攻击动画
AnimInstance->Montage_Play(CurrentAttackMontage);
//调用定时器
GetWorldTimerManager().SetTimer(TimerHandle_AnimPlayOver,this,&ANormalEnemyController::OnAnimPlayOver,CurrentAttackMontage->SequenceLength,false);
}
}
void ANormalEnemyController::OnAnimPlayOver()
{
//停止之前播放动画
CurrentAttackMontage = nullptr;
//放下一次需要播放的动画
CurrentAttackIndex = GetNextAnimationIndex();
//判断目标是否在攻击范围内
if (bInAttackRange())
{
//继续攻击
PerformAttack();
}
else
{
//移向目标
MoveToActor(TargetActor);
}
}
void ANormalEnemyController::OnAggoredPulled(AActor* Target)
{
//判断狂暴状态
if (!bWasAggroed&&bIsRuningBack)
{
//调整狂暴状态
bWasAggroed = true;
//设置当前目标
TargetActor = Target;
//调整巡逻状态
bIsPatrolling = false;
//设置追逐速度
EnemyPawn->GetCharacterMovement()->MaxWalkSpeed = AggroedWalkSpeed;
//利用定时器持续检测敌人与目标距离
GetWorldTimerManager().SetTimer(TimerHandle_CalDis, this, &ANormalEnemyController::CacualteTargetDistance, 1.0f, true);
//判断追逐时的攻击范围
if (bInAttackRange())
{
//调用攻击函数
PerformAttack();
}
else
{
//移向目标
MoveToActor(TargetActor);
}
}
}
void ANormalEnemyController::CacualteTargetDistance()
{
//判断数据有效性
if (FVector::Dist(EnemyPawn->GetActorLocation(),TargetActor->GetActorLocation())>MaxDistanceToFollowTarget)
{
//重置敌人状态
OnReset();
}
}
void ANormalEnemyController::OnReset()
{
//停止播放攻击动画
AnimInstance->Montage_Stop(0.0f);
//调整返回状态
bIsRuningBack = true;
//清除定时器
GetWorldTimerManager().ClearTimer(TimerHandle_Patrol);
GetWorldTimerManager().ClearTimer(TimerHandle_AnimPlayOver);
GetWorldTimerManager().ClearTimer(TimerHandle_CalDis);
//目标置为空
TargetActor = nullptr;
//调整狂暴状态
bWasAggroed = false;
//回到初始位置
MoveToLocation(EnemyPawn->StartLocation);
}
void ANormalEnemyController::Patrol()
{
//定义巡逻半径
const float SearchRadius = 1000.0f;
//判断NavSys有效性
if (NavSys)
{
//定义随机巡逻点
FNavLocation RandomPt;
//调用获得半径内随机可达点函数
bool bFound = NavSys->GetRandomReachablePointInRadius(EnemyPawn->GetActorLocation(), SearchRadius, RandomPt);
//判断能否找到
if (bFound)
{
//移向找到的随机位置
MoveToLocation(RandomPt.Location);
}
}
}
在构造函数中,传入的this报空指针