Stanford UE4 & UE5 C++ 开发 课程笔记(三) 子弹物理碰撞与弹道校正
物理碰撞
Unreal中两个物体碰撞需要两方预设的碰撞通道中对方对应的类型都设置为Block。
1. 防止碰撞体
在场景中设置一个cube,并将其进行适当拉伸:
选中放置好的cube,在 Collision中将Collision Presets设为Custom,并将每一项置为Block:
注意cube的默认类型是WorldStatic。
2. 自定义碰撞通道
在Project Setting -> Engine -> Collision -> Preset中添加新的通道,并命名为Projectile:
具体设置如下图:
3. 在Projectile蓝图类中设置碰撞
打开Projectile蓝图类,选中SphereComponent,将Collision Preset 设置为上一步自定义的类型:
效果
编译后,查看效果
4. 设置子弹碰撞后爆炸
为了更加符合实际,需要实现让子弹碰到墙壁后爆炸,属于On Component Hit事件:
- 判断发射对象,碰撞的物体不能是发射子弹的人(自己)。
- 获取当前子弹在场景中的具体方位。
- 在对应位置生成一个爆炸效果,具体类型是P_Gideon_Primary_HitWorld。
- 最后,摧毁当前子弹。
效果
弹道校正
1. 原理
可以通过Unreal提供的LineTraceByChannel方法检测被阻挡点的位置。
2. 校正前
3. 校正后
4. 实现
在屏幕中添加准星
新建Widget,命名为Crosshair_Widget:
在HUD中插入一个Image。这里如果没有虚线方框,需要自己在HUD中先添加一个Canvas:
在右侧Image属性栏校准位置:
在PlayerCharacter蓝图类中添加当前UI:
校正弹道
更新在第二篇笔记中编写的 PrimaryAttack_TimeElapsed 函数:
void ALCharacter::PrimaryAttack_TimeElapsed()
{
// 生成角度和坐标
// 获取人物模型中手的坐标,GetSocketLocation可以获取骨骼中的插件
const FVector HandLocation = GetMesh()->GetSocketLocation("Muzzle_01");
// 未校正前的生成方位
// const FTransform SpawnTM = FTransform(GetControlRotation(), HandLocation);
FActorSpawnParameters SpawnParams;
// 无论任何情况都生成(无视重叠,碰撞,覆盖...)
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
// 将角色自身作为触发者传入,以便子弹判断正确的交互对象
SpawnParams.Instigator = this;
// 碰撞参数,忽略自身
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
// 获取Camera组件
APlayerCameraManager* CurrentCamera = GetWorld()->GetFirstPlayerController()->PlayerCameraManager;
// 方向是摄像机视角的正前方(屏幕正中央),这里不要把手部模型的坐标传入,方向会偏
FVector TraceDirection = CurrentCamera->GetActorForwardVector();
// 起始位置是摄像机的位置
FVector TraceStart = CurrentCamera->GetCameraLocation();
// 终点是一段距离,后面的5000不固定
FVector TraceEnd = TraceStart + (TraceDirection * 5000);
FHitResult Hit;
// Line Trace检测与障碍物的撞击点
if (GetWorld()->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECC_GameTraceChannel1, Params))
{
// 将撞击点作为方向向量的终点位置
TraceEnd = Hit.ImpactPoint;
}
// 起始点是我们的子弹生成点(手),终点是目标点,获得Rotation
FRotator ProjRotation = (TraceEnd - HandLocation).Rotation();
// 最终获得校正后的生成方位
FTransform SpawnTM = FTransform(ProjRotation, HandLocation);
// 在世界中生成
GetWorld()->SpawnActor<AActor>(ProjectileClass, SpawnTM, SpawnParams);
}
更新代码后编译,就可以实现正确的弹道。