UE4_自定义相机碰撞组件
  • 相比较UE4自带的SpringArm中的碰撞,主要特点在于前后推进速度不一样

  • 在碰撞时 向前推进的速度较快,在离开碰撞时 向后退的速度较慢


SpringArm自带的碰撞检测

SpringArm的碰撞检测,在墙角的位置,由于频繁的碰撞和离开,导致来回的闪动


我的碰撞检测组件

推进,后退 使用不同的差值速度,避免相机频繁推拉,视觉感受上更舒适


C++ 代码部分
h

// TAZORN.COM 2017 ALL RIGHTS RESERVED
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "CameraCollisionComponentBase.generated.h"

/*
相机碰撞组件,可以被Bp继承,继续修改或复写
*/

UCLASS(Blueprintable, BlueprintType, ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class XLABORATORY_API UCameraCollisionComponentBase : public UActorComponent
{
   GENERATED_BODY()public:
   UCameraCollisionComponentBase();
   // SpringArm组件的引用
   USpringArmComponent* SpringComponent;
   float DesiredDistance;
   float CamSpringCurrLength;
   float HitLength;
   float CameraLerpSpeed;
   float Delta;
   virtual void BeginPlay() override;
   virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

   UPROPERTY(BlueprintReadWrite, Category = "CameraCollision")
   bool bEnable;

   UPROPERTY(BlueprintReadWrite, Category = "CameraCollision")
   float CameraTraceRadius;
   // 推进的差值速度
   UPROPERTY(BlueprintReadWrite, Category = "CameraCollision")
   float PushSpeed;
   // 拉远的差值速度
   UPROPERTY(BlueprintReadWrite, Category = "CameraCollision")
   float PullSpeed;

   UFUNCTION(BlueprintCallable, Category = "CameraCollision")
   void Init(USpringArmComponent* InSpringComponent);

   UFUNCTION(BlueprintCallable, Category = "CameraCollision")
   void CalcCameraCollision(USpringArmComponent* SpringArm, float InDesiredDistance, float InPushSpeed, float InPullSpeed);

   UFUNCTION(BlueprintCallable, Category = "CameraCollision")
   void SetDesiredDistance(float InDesiredDistance);

   UFUNCTION(BlueprintCallable, BlueprintPure, Category = "CameraCollision")
   float GetDesiredDistance();
   // 需要在Bp中,指定碰撞的通道
   UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Bitmask, BitmaskEnum = "ECollisionChannel"), Category = "CameraCollision")
       int32 ObjectType;
       
   // 简化的球面检测
   bool SphereTrace(AActor* ActorToIgnore, const FVector& Start, const FVector& End, const float Radius,
       FHitResult& HitOut, FCollisionObjectQueryParams& TraceChannels);
};

cpp

// TAZORN.COM 2017 ALL RIGHTS RESERVED#include "CameraCollisionComponentBase.h"
// 构造函数UCameraCollisionComponentBase::UCameraCollisionComponentBase()
{
   PrimaryComponentTick.bCanEverTick = true;
   // 属性默认值
   DesiredDistance = 400.f;
   CameraTraceRadius = 30.f;
   PushSpeed = 12.f;
   PullSpeed = 0.8f;
   bEnable = true;
}
void UCameraCollisionComponentBase::BeginPlay()
{
   Super::BeginPlay();
}
void UCameraCollisionComponentBase::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
   Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
   Delta = DeltaTime;
   if (SpringComponent && bEnable)
   {
       CalcCameraCollision(SpringComponent, DesiredDistance, PushSpeed, PullSpeed);
   }
}
// 初始化,需要在Bp中指定 SpringArm组件引用
void UCameraCollisionComponentBase::Init(USpringArmComponent* InSpringComponent)
{
   SpringComponent = InSpringComponent;
}
// 计算碰撞void UCameraCollisionComponentBase::CalcCameraCollision
(
   USpringArmComponent* SpringArm, float InDesiredDistance, float InPushSpeed, float InPullSpeed
)
{
  // 开始结束点,获得SprintArm的位置,以及反向*希望的距离作为结束点
   FVector start = SpringArm->GetComponentToWorld().GetLocation();
   FVector end = (SpringArm->GetForwardVector() * -1.f * DesiredDistance) + start;
   // 碰撞通道
   FCollisionObjectQueryParams Channels(ObjectType);
   // 使用简化的球面检测函数
   FHitResult HitOut = FHitResult(ForceInit);
   bool hit = SphereTrace(SpringArm->GetOwner(), start, end, CameraTraceRadius, HitOut, Channels);
   if (hit)
   {
       HitLength = (HitOut.Location - start).Size();
   }
   else
   {
       HitLength = InDesiredDistance;
   }
   // 差值
   CamSpringCurrLength = FMath::FInterpTo(CamSpringCurrLength, HitLength, Delta, CameraLerpSpeed);
   CameraLerpSpeed = (CamSpringCurrLength - HitLength > 0.f) ? PushSpeed : PullSpeed;
   // 设置到SprintArm
   SpringArm->TargetArmLength = CamSpringCurrLength;

}

// 设置DesiredDistancevoid UCameraCollisionComponentBase::SetDesiredDistance(float InDesiredDistance)
{
   DesiredDistance = InDesiredDistance;
}

float UCameraCollisionComponentBase::GetDesiredDistance()
{
   return DesiredDistance;
}

// 简化的球面检测函数bool UCameraCollisionComponentBase::SphereTrace
(
   AActor* ActorToIgnore,
   const FVector& Start,
   const FVector& End,
   const float Radius,
   FHitResult& HitOut,
   FCollisionObjectQueryParams& TraceChannels
)

{
   FCollisionQueryParams TraceParams(true);
   //TraceParams.bTraceComplex = true;
   //TraceParams.bTraceAsyncScene = true;
   TraceParams.bReturnPhysicalMaterial = false;
   //Ignore Actors
   TraceParams.AddIgnoredActor(ActorToIgnore);
   //Re-initialize hit info
   HitOut = FHitResult(ForceInit);
   return GetWorld()->SweepSingleByObjectType
   (
       HitOut,
       Start,
       End,
       FQuat(),
       TraceChannels,
       FCollisionShape::MakeSphere(Radius),
       TraceParams
   );
}

在Bp中使用

编译后在Editor中创建蓝图,继承CameraCollisionComponentBase
我这里给蓝图的命名为 BPC_CameraCollision


在一个设置有SpringArm以及相机的蓝图中使用 BPC_CameraCollision
关闭原有SpringArm的相机碰撞

添加该组件,在BeginPlay调用Init,把 SpringArm引用设置进去

在BPC_CameraComponent属性栏 CameraCollision 中设置 碰撞通道

通过设置DesiredDistance 来进行设置 默认的相机距离



作者:TAZORN

著作权归作者所有






【相关阅读】:


【上传发布插件】: