敌人巡逻的逻辑如下:
敌人在游戏一开始的时候就通过moveto函数先前往首先设定的patroltarget目标,在距离patroltarget距离为patroradius(200unit)之内时就可以通过checkpatroltarget函数更新新的patroltarget目标,随后前往新的patroltarget
---------------------伪代码-----------------
BeginPlay():
MoveToTarget(PatrolTarget);
MoveToTarget(PatrolTarget):
FAIMoveRequest MoveRequest;
MoveRequest.SetGoalActor(Target);
MoveRequest.SetAcceptanceRadius(AcceptanceRadius);
EnemyController->MoveTo(MoveRequest);Tick(float DeltaTime):
//每一帧检查是否可以更新patroltarget,在与原本的patroltarget距离在patrolradius之内时就可以更新patroltarget
CheckPatrolTarget();
CheckPatrolTarget:if (InTargetRange(PatrolTarget, PatrolRadius))
//更新新的patroltarget
PatrolTarget = ChoosePatrolTarget();GetWorldTimerManager().SetTimer(PatrolTimer, this, &AEnemy::PatrolTimerFinished, WaitTime);
InTargetRange(AActor* Target, double Radius):
const double DistanceToTarget = (Target->GetActorLocation() - GetActorLocation()).Size();
return DistanceToTarget <= Radius;AEnemy::PatrolTimerFinished():
//PatrolTimer倒数完就可以前往新的patroltarget
MoveToTarget(PatrolTarget);
可是在实现过程中,发现敌人没法更新patroltarget。
随后在InTargetRange()中加上日志
bool AEnemy::InTargetRange(AActor* Target, double Radius)
{if (Target == nullptr) return false;const double DistanceToTarget = (Target->GetActorLocation() - GetActorLocation()).Size();FVector EnemyLocation = GetActorLocation();FVector TargetLocation = Target->GetActorLocation();UE_LOG(LogTemp, Warning, TEXT("Enemy Location: %s"), *EnemyLocation.ToString());UE_LOG(LogTemp, Warning, TEXT("Target Location: %s"), *TargetLocation.ToString());float Distance3D = FVector::Dist(EnemyLocation, TargetLocation);float Distance2D = FVector::Dist2D(EnemyLocation, TargetLocation); // 忽略Z轴UE_LOG(LogTemp, Warning, TEXT("3D Distance: %f"), Distance3D);UE_LOG(LogTemp, Warning, TEXT("2D (Horizontal) Distance: %f"), Distance2D);return DistanceToTarget <= Radius;
}
发现敌人在距离目标点2d平面距离为155unit,3d矢量距离为205unit的时候就停下了,205> patrolradius(200)就无法更新patroltarget。这不对劲啊,我前面通过设置MoveRequest.SetAcceptanceRadius(AcceptanceRadius); AcceptanceRadius=50.f代码,就预期敌人会在距离patroltarget 50unit的时候停下,而不是在155unit的时候停下。
于是问了chatgpt。
结果它回答我
✅【AIController::MoveTo 的到达逻辑:】
AI 到达目标的判断规则 roughly 是这样的(简化版):
ActualDistance <= (AcceptanceRadius + TargetCapsuleRadius + SelfCapsuleRadius)
也就是说,如果目标和自身的胶囊体比较大,即使你设置的 AcceptanceRadius 很小,也可能提前“到达”了目标。
并且给了我验证的日志代码
float SelfRadius = GetCapsuleComponent()->GetScaledCapsuleRadius();
float TargetRadius = 0.f;if (Target->IsA<ACharacter>())
{ACharacter* TargetChar = Cast<ACharacter>(Target);TargetRadius = TargetChar->GetCapsuleComponent()->GetScaledCapsuleRadius();
}UE_LOG(LogTemp, Warning, TEXT("SelfRadius: %f, TargetRadius: %f, AcceptanceRadius: %f, TotalEffectiveRadius: %f"),SelfRadius, TargetRadius, AcceptanceRadius,SelfRadius + TargetRadius + AcceptanceRadius);
于是我在movetotarget中加上代码
void AEnemy::MoveToTarget(AActor* Target)
{if (EnemyController == nullptr || Target == nullptr) return;float SelfRadius = GetCapsuleComponent()->GetScaledCapsuleRadius();float TargetRadius = 0.f;if (Target->IsA<ACharacter>()){ACharacter* TargetChar = Cast<ACharacter>(Target);TargetRadius = TargetChar->GetCapsuleComponent()->GetScaledCapsuleRadius();}UE_LOG(LogTemp, Warning, TEXT("SelfRadius: %f, TargetRadius: %f, AcceptanceRadius: %f, TotalEffectiveRadius: %f"),SelfRadius, TargetRadius, AcceptanceRadius,SelfRadius + TargetRadius + AcceptanceRadius);FAIMoveRequest MoveRequest;MoveRequest.SetGoalActor(Target);MoveRequest.SetAcceptanceRadius(AcceptanceRadius);EnemyController->MoveTo(MoveRequest);}
日志显示:
我的敌人在2d距离为155的时候停下了, AcceptanceRadius + TargetCapsuleRadius + SelfCapsuleRadius=149,和155也很接近,看来要使3d距离小于200,则就应该缩减AcceptanceRadius或者增大patrolradius,所以将AcceptanceRadius缩减至30之后,敌人又能正常更新patroltarget了。
这个说明了,不同体型的敌人, 其attackradius, combatradius, acceptanceradius,patrolradius都应该根据体型变化。
还有就是思考chatgpt的回答我是不是不应该写博客上,这样可能会导致数据污染,chatgpt之后爬网搜集数据搜集到的又是它自己生成的,自产自销了