P1.ViewLock
大约 3 分钟
思考
方案1
发射简单射线
方案1|问题
- 单纯的射线并不适合用来检测物体。
- 需要一个极限检测距离。
- 需要一个极限脱离距离。
方案2
球状射线检测
方案2|问题
方案3
球状射线检测+视口中心夹角计算
原理说明
- 发射球形射线:确定所有与球体相交的物体。
- 计算每个物体的夹角:评估物体相对于视口方向的角度。
- 选择最小夹角物体:找到夹角最小的物体作为最终结果。
核心函数
AActor* UViewLockComponent::GetNearestFocusActor(ETraceType TraceType)
{
if (!ViewController) return nullptr;
// 获取控制器视口位置和方向
FVector ViewportLocation;
FRotator ViewportRotation;
ViewController->GetPlayerViewPoint(ViewportLocation, ViewportRotation);
// 以用户组件位置为起点
const FVector StaLocation = GetOwner()->GetActorLocation();
// 视口朝向*距离为终点
const FVector EndLocation = StaLocation + ViewportRotation.Vector() * LockViewAttribute.MaxFocusDist;
// 根据枚举值选择执行的追踪方法
switch (TraceType)
{
case ETraceType::Single:
return PerformSingleTrace(StaLocation, EndLocation);
case ETraceType::Multi:
return PerformMultiTrace(ViewportLocation, ViewportRotation, StaLocation, EndLocation);
default:
return nullptr;
}
}
//方案2
AActor* UViewLockComponent::PerformSingleTrace(const FVector& StaLocation, const FVector& EndLocation)
{
FHitResult OutHit;
UKismetSystemLibrary::SphereTraceSingleForObjects(
this,
StaLocation,
EndLocation,
LockViewAttribute.ViewHalfSize,
LockViewAttribute.ObjectTypes,
false,
{},
LockViewAttribute.DebugType,
OutHit,
true);
return OutHit.GetActor();
}
//方案3
AActor* UViewLockComponent::PerformMultiTrace(const FVector& ViewportLocation, const FRotator& ViewportRotation, const FVector& StaLocation, const FVector& EndLocation)
{
TArray<FHitResult> OutHits;
UKismetSystemLibrary::SphereTraceMultiForObjects(
this,
StaLocation,
EndLocation,
LockViewAttribute.ViewHalfSize,
LockViewAttribute.ObjectTypes,
false,
{},
LockViewAttribute.DebugType,
OutHits,
true);
if (OutHits.Num() == 0) return nullptr;
float MinAngle = MAX_FLT;
AActor* NearestActor = nullptr;
for (const FHitResult& Hit : OutHits)
{
AActor* HitActor = Hit.GetActor();
if (HitActor)
{
// 获取物体位置
FVector ActorLocation = HitActor->GetActorLocation();
// 计算物体与玩家视口方向的向量
FVector ToActor = ActorLocation - ViewportLocation;
ToActor.Normalize();
// 计算玩家视口方向的向量
FVector ViewDirection = ViewportRotation.Vector();
// 计算物体与玩家视口方向的夹角
float Angle = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(ToActor, ViewDirection)));
// 更新最小夹角和对应的物体
if (Angle < MinAngle)
{
MinAngle = Angle;
NearestActor = HitActor;
}
}
}
return NearestActor;
}