目录
前言
原理
代码
测试
结果
前言
通过数学原理判断空间中任意两点的连线是否穿过球体,再通过射线检测检验算法的正确性。
原理
(1)设球体球心的坐标为 ,半径为r;
(2)设线段中A点的坐标为,B点的坐标为
(3)计算、、
(4)计算点到 线段的最短距离
(5) 如果,则线段穿过球体;如果,则线段不穿过球体。
代码
定义一个函数“IsCrossSphere”来判断线段是否穿过球体,函数需要传入点A、B的坐标以及球心坐标和球体半径。
再定义一个结构体作为函数返回值
函数“IsCrossSphere”的实现如下
头文件:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "LineIsCrossSphere.generated.h"
USTRUCT(BlueprintType)
struct FStruct_Result_IsLineCrossSphere
{
GENERATED_BODY();
public:
UPROPERTY(BlueprintReadWrite)
float distanceOfLineAndSphereCenter;
UPROPERTY(BlueprintReadWrite)
bool isCrossSphere;
};
UCLASS()
class STUDY_API ALineIsCrossSphere : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ALineIsCrossSphere();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
UFUNCTION(BlueprintCallable)
FStruct_Result_IsLineCrossSphere IsCrossSphere(FVector pointA, FVector pointB, FVector sphereOrginPoint, float sphereRadius); //计算线段AB到球心的距离并判断AB是否穿过球体
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
源文件:
// Fill out your copyright notice in the Description page of Project Settings.
#include "Test/LineIsCrossSphere.h"
// Sets default values
ALineIsCrossSphere::ALineIsCrossSphere()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void ALineIsCrossSphere::BeginPlay()
{
Super::BeginPlay();
}
FStruct_Result_IsLineCrossSphere ALineIsCrossSphere::IsCrossSphere(FVector pointA, FVector pointB, FVector sphereOrginPoint, float sphereRadius)
{
bool isCrossSphere;
FVector OA = pointA - sphereOrginPoint;
FVector OB = pointB - sphereOrginPoint;
FVector AB = OB - OA;
FVector n = OA.Cross(AB);
float D = n.Size() / AB.Size();
if (D > sphereRadius)
{
isCrossSphere = false;
}
else
{
isCrossSphere = true;
}
FStruct_Result_IsLineCrossSphere result;
result.isCrossSphere = isCrossSphere;
result.distanceOfLineAndSphereCenter = D;
return result;
}
// Called every frame
void ALineIsCrossSphere::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
测试
在Tick中每帧去发射射线检测并调用函数 “IsCrossSphere”,通过观察射线碰撞结果以及函数 “IsCrossSphere”的打印是否相等来判断算法是否有误。(这里设置球体半径固定为50,球体坐标为(0,0,0))
结果
可以看到不论是线段在球体表面,穿过球体,还是在球体外,函数 “IsCrossSphere”与射线检测的结果都是一致的。