案例1:
我们现在有一个需求:我们需要对场景中的某个物体做出交互行为,
因此我们需要先选中这个物体(用检测射线射中这个物体,得到这个物体的指针)
然后操作这个物体(用指针完成所有的行为)
先算出来起点和终点,然后用一个函数发射一个射线,射中物体后返回bool值, 并且得到物体的指针
问题1:由于射线太细,可能导致错过目标物体
解决方案:不再使用线性追踪,而是用一个球体射线(扫掠),更加容易命中物体
问题2:路径上会有别的物体挡路
解决方案:设定自定义的追踪通道,在需要的物体的碰撞预设中添加对我们这个射线的阻碍
这样只有我们想要的物体才会被射线检测到.
第一步: 先新建一条检测通道

第二步: 修改物体的蓝图,使得此物体能够阻挡到探测通道:

第三步:设计函数计算起点和终点
起始点:Camera->GetComponentLocation();
成员变量float MaxInteractionDistance = 300.0f;
结束点:Start + (Camera->GetForwardVector()*MaxInteractionDistance);

写入响应函数:
void ADungeonEscapeCharacter::Interact()
{
FVector Start = FirstPersonCameraComponent->GetComponentLocation();;
FVector End = Start +
(FirstPersonCameraComponent->GetForwardVector() * MaxInteractDistance);
//从起点到终点绘制一条可视化的Red射线用于调试,存在5秒
DrawDebugLine(GetWorld(), Start, End, FColor::Red, false, 5.0f);
}
理想的效果图:我们对着桌子不断点击左键,走到一旁后可以看到,我们的视线检测通道是发散的,如果我们想要轻松的把物体选中,那么就需要,把这个线条的末端设计成这种类似圆形发散的,我们可以为检测通道添加一个球行碰撞体:

第四步:设计球型扫掠检测通道
先设置一个成员变量交互球体半径:InteractionSphereRadius
UPROPERTY(EditAnywhere)
float InteractionSphereRadius = 30.0f;
先创建一个碰撞球
再在起点Start和终点End处创建一个调试球体.
void ADungeonEscapeCharacter::Interact()
{
FVector Start = FirstPersonCameraComponent->GetComponentLocation();;
FVector End = Start +
(FirstPersonCameraComponent->GetForwardVector() * MaxInteractDistance);
//从起点到终点绘制一条可视化的Red射线用于调试,存在5秒
DrawDebugLine(GetWorld(), Start, End, FColor::Red, false, 5.0f);
//创建一个碰撞球体
FCollisionShape InteractionSphere = FCollisionShape::MakeSphere(InteractionSphereRadius);
//创建一个调试球体
DrawDebugSphere(GetWorld(), Start, InteractionSphereRadius, 20, FColor::Green, false, 5.0f);
DrawDebugSphere(GetWorld(), End, InteractionSphereRadius, 20, FColor::Blue, false, 5.0f);
}
效果如图:如果想办法让两个球体之间扫掠,就可以增大检测空间

第五步:使用通道碰撞检测函数来检测物体
SweepSingleByChannel();是虚幻引擎中一个非常重要的碰撞检测函数.
使用此函数可以在游戏世界中执行单次扫描检测
然后把是否命中存入HasHit,把命中的对象存入HitResult
void ADungeonEscapeCharacter::Interact()
{
FVector Start = FirstPersonCameraComponent->GetComponentLocation();;
FVector End = Start +
(FirstPersonCameraComponent->GetForwardVector() * MaxInteractDistance);
//从起点到终点绘制一条可视化的Red射线用于调试,存在5秒
DrawDebugLine(GetWorld(), Start, End, FColor::Red, false, 5.0f);
//创建一个碰撞球体
FCollisionShape InteractionSphere = FCollisionShape::MakeSphere(InteractionSphereRadius);
//创建一个调试球体在起点和终点处
DrawDebugSphere(GetWorld(), Start, InteractionSphereRadius, 20, FColor::Green, false, 5.0f);
DrawDebugSphere(GetWorld(), End, InteractionSphereRadius, 20, FColor::Blue, false, 5.0f);
FHitResult HitResult;
//用一个变量来接收命中信息
bool HasHit = GetWorld()->SweepSingleByChannel(
HitResult,// 检测结果(存放命中的详细信息)
Start,//扫描起始位置
End, //扫描结束位置
FQuat::Identity, //碰撞形状的旋转(identity是无旋转)
ECC_GameTraceChannel2, //碰撞检测通道
InteractionSphere //碰撞形状(球体、胶囊体、盒子等)
);
//检测是否成功检测到任何东西
if (HasHit) {
AActor* HitActor = HitResult.GetActor();
UE_LOG(LogTemp, Display, TEXT("Shape trace hit actor %s"),*(HitActor->GetActorNameOrLabel()));
}
else {
UE_LOG(LogTemp,Display,TEXT("No actor hit!"));
}
}
碰撞检测通道这个参数怎么填?他就是我们原本自定义的通道Interact,但是名称不是Interact,而是需要去项目文件夹的DefaultEngine.ini里面
按ctrl+f搜索Interact
然后就可以找到其通道:GameTraceChannel;



案例进阶:
对于上一个案例,我们再进阶一下,因为有个问题我们大概率会遇见:
目前我们看似已经获取到了被射线探测到的物体的指针---HitActor,但是其类型是AActor* ,无法访问物体类的成员变量,我们需要用Cast<ACollectableItem>();转换一下其类型:
//包含一下你需要转换类型的头文件
#include "CollectableItem.h"
#include "Lock.h"
---------------------------------------------
ALock* LockActor = Cast<ALock>(HitActor);
if (LockActor) {
UE_LOG(LogTemp, Display, TEXT("Shape trace hit \"Lock\" %s"),
*(LockActor->GetActorNameOrLabel()));
}
这样我们就可以实现对其他物体的完全控制了
6161

被折叠的 条评论
为什么被折叠?



