效果:
1.1 创建项目
这里直接使用的第一人称游戏的模板,创建为蓝图项目。
1.2 添加子弹类
子弹类头文件:
其中TargetActor为追踪的目标,BaseVelocity为子弹刚发射时的速度,MaxLength为追踪阶段的最远距离,MinLength为追踪阶段的最近距离(后面会解释)。
class LOCKFPS_API ABulletActor : public AActor
{
GENERATED_BODY()
public:
ABulletActor();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
void SetVelocity();
FVector BaseVelocity;
bool bLock=false;
UPROPERTY(EditDefaultsOnly)
UProjectileMovementComponent*ProjectileMovementComp;
UPROPERTY(BlueprintReadWrite)
AActor*TargetActor;
UPROPERTY(EditAnywhere,BlueprintReadWrite,meta=(DisplayName="追踪阶段最大距离"))
float MaxLength=1000;
UPROPERTY(EditAnywhere,BlueprintReadWrite,meta=(DisplayName="追踪阶段最小距离"))
float MinLength=50;
};
子弹类源文件:
原理:子弹分为追踪阶段和普通运动阶段(速度方向固定),如果未捕获到目标则以普通运动阶段运行。如果捕获到目标,会进入追踪阶段并根据和目标的直线距离实时计算速度方向,当子弹和目标的距离小于追踪阶段的最近距离,停止追踪并进入普通运动阶段。
ABulletActor::ABulletActor()
{
PrimaryActorTick.bCanEverTick = true;
RootComponent=CreateDefaultSubobject<USceneComponent>(TEXT("RootComp"));
ProjectileMovementComp=CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComp"));
ProjectileMovementComp->ProjectileGravityScale=0;
}
void ABulletActor::BeginPlay()
{
Super::BeginPlay();
BaseVelocity=ProjectileMovementComp->Velocity.GetSafeNormal()*ProjectileMovementComp->MaxSpeed;
}
void ABulletActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
SetVelocity();
}
void ABulletActor::SetVelocity()
{
if (TargetActor)
{
float VelocityAlpha;
float VelocityLength;
FVector TargetDir;
//计算当前位置指向目标位置的向量方向和模长
(TargetActor->GetActorLocation()-GetActorLocation()).ToDirectionAndLength(TargetDir,VelocityLength);
//判断当前位置和目标位置的距离是否小于追踪阶段最小距离,如果小于,清除目标并停止追踪。子弹将沿着停止追踪时的速度方向继续运动
if (VelocityLength<=MinLength){TargetActor=nullptr;return;}
if (VelocityLength<MaxLength||bLock)
{
//如果进入追踪状态,将bLock设置为true
bLock=true;
//计算Alpha值,当前位置距离目标位置为MaxLength时,Alpha≈0。当前位置距离目标位置为MinLength时,Alpha=1。
VelocityAlpha=(MaxLength-(VelocityLength-MinLength))/MaxLength;
//使用lerp进行计算,A为子弹刚发射时的速度方向,B为前位置指向目标位置的方向
ProjectileMovementComp->Velocity=FMath::Lerp<FVector,float>
(BaseVelocity.GetSafeNormal(),TargetDir,VelocityAlpha)*ProjectileMovementComp->MaxSpeed;
}
else
{
ProjectileMovementComp->Velocity=ProjectileMovementComp->Velocity.GetSafeNormal()*ProjectileMovementComp->MaxSpeed;
}
}
}