虚幻引擎-检测射线TraceChannel与形状检测射线

案例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()));
}

这样我们就可以实现对其他物体的完全控制了

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值