旋转炮塔
在这篇文章中,我们实现炮塔的旋转。
因为我们自身坦克和敌方坦克的炮塔都需要旋转,所以如代码清单 1 所示,我们把旋转功能放在它们的父类 ABasePawn 里。
- UCLASS()
- class TOONTANKS_API ABasePawn : public APawn
- {
- GENERATED_BODY()
- protected:
- void RotateTurret(FVector LookAtTarget);
- };
代码清单 2 是炮塔旋转的实现。我们首先根据朝向的点获取朝向向量。FVector::Rotation() 函数可以根据一个向量,返回对应的旋转量。
因为炮塔只可以在水平方向上旋转,所以我们只保留 Yaw 分量。
接着我们使用 SetWorldRotation() 函数设置炮塔的旋转。
SetComponentRotation() 是自身局部空间;SetWorldRotation() 是世界空间。
FVector::Rotation() 返回的是基于世界的旋转量。
如果我们直接使用 LookAtRotation 设置旋转量,运行时会出现跳变的情况。为此,我们使用 FMath::RInterpTo() 函数,对旋转量进行插值。
- void ABasePawn::RotateTurret(FVector LookAtTarget)
- {
- FVector ToTarget = LookAtTarget - TurretMesh->GetComponentLocation();
- FRotator LookAtRotation = FRotator(0.0f, ToTarget.Rotation().Yaw, 0.0f);
- TurretMesh->SetWorldRotation(
- FMath::RInterpTo(
- TurretMesh->GetComponentRotation(),
- LookAtRotation,
- UGameplayStatics::GetWorldDeltaSeconds(this),
- 15.0f));
- }
接着我们需要调用旋转函数。如代码清单 3 所示,调用点在上一节调试鼠标射线扫描的地方。我们将发生碰撞的点作为参数传入。
- // Called every frame
- void ATank::Tick(float DeltaTime)
- {
- Super::Tick(DeltaTime);
- if (PlayerControllerRef)
- {
- FHitResult HitResult;
- PlayerControllerRef->GetHitResultUnderCursor(
- ECollisionChannel::ECC_Visibility,
- false, HitResult);
- //DrawDebugSphere(GetWorld(),
- // HitResult.ImpactPoint,
- // 25.0f, 12, FColor::Red);
- RotateTurret(HitResult.ImpactPoint);
- }
- }
我们尝试运行程序,会发现在边界位置,炮塔会受不到控制。我们可以把代码清单 3 中的 DrawDebugSphere() 函数注释回来。如图 1 所示,当鼠标位置超过边界的时候,会获取不到碰撞点,导致旋转量计算错误。
为了修复这个问题,如图 2 所示,我们可以在场景周围添加 阻挡体积。阻挡体积默认不阻挡 Visibility。我们需要把 碰撞预设 设置为 Custom,然后将 Visibility 设置为阻挡。
设置好后,如图 3 所示,我们鼠标的射线扫描就可以指向场景外了。
最终运行的效果,如以下视频所示。