UE5相机系统初探(二)

UE5相机系统初探(二)

上一章节我们实现了一个基本的第三人称相机。这一节我们为相机添加一些功能。

Lag

Spring Arm Component有一组关于Lag的参数,它表示相机的跟随延迟,这个是非常非常使用的功能,在Spring Arm Component中我们可以分别控制相机的位移延迟,以及旋转的延迟。位移的延迟可以设置一个最大的延后距离,这样可以保证相机不会离玩家太远导致玩家跑出了屏幕外。

UE5相机系统初探2-1

如上设置后,来看下玩家移动时的延迟效果:

在这里插入图片描述

再把Camera Rotation Lag Speed调到1,观察下旋转的延迟(容易引起晕眩):

在这里插入图片描述

location lag背后的逻辑其实很简单,就是一个线性插值,这个插值有两种方案,一种是直接根据delta time和speed插值,还有一种是使用一个小的步长,在delta time区间内不断更新插值目标,来调整相机的位置,这种表现上更丝滑,当然计算开销也会更高。

if (bUseCameraLagSubstepping && DeltaTime > CameraLagMaxTimeStep && CameraLagSpeed > 0.f)
{
    const FVector ArmMovementStep = (DesiredLoc - PreviousDesiredLoc) * (1.f / DeltaTime);
    FVector LerpTarget = PreviousDesiredLoc;

    float RemainingTime = DeltaTime;
    while (RemainingTime > UE_KINDA_SMALL_NUMBER)
    {
        const float LerpAmount = FMath::Min(CameraLagMaxTimeStep, RemainingTime);
        LerpTarget += ArmMovementStep * LerpAmount;
        RemainingTime -= LerpAmount;

        DesiredLoc = FMath::VInterpTo(PreviousDesiredLoc, LerpTarget, LerpAmount, CameraLagSpeed);
        PreviousDesiredLoc = DesiredLoc;
    }
}
else
{
    DesiredLoc = FMath::VInterpTo(PreviousDesiredLoc, DesiredLoc, DeltaTime, CameraLagSpeed);
}

rotation lag也是同理,只不过换成了球形插值。

Shake

目前UE提供的camera shake有两种蓝图,一是DefaultCameraShakeBase,另一种是LegacyCameraShake,它们都继承自基类UCameraShakeBase

UE5相机系统初探2-4

我们希望在角色跑步时,引入camera shake,来加强跑步的观感。同时,camera shake希望是可配置的,也就是说它可以来自不同的蓝图,但都要继承自UCameraShakeBase。那么需要在类里定义变量:

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera")
TSubclassOf<UCameraShakeBase> SprintShake;

UE5相机系统初探2-5

然后,加入跑步时触发shake和停止shake的逻辑:

void ACF_Character::SprintStart()
{
	GetCharacterMovement()->MaxWalkSpeed = SprintSpeed; 

	if (SprintShake && GetVelocity().Length() != 0)
	{
		CurrentSprintShake = GetWorld()->GetFirstPlayerController()->PlayerCameraManager->StartCameraShake(SprintShake, 1.f);
	}
}

void ACF_Character::SprintEnd()
{
	GetCharacterMovement()->MaxWalkSpeed = DefaultSpeed; 

	if (SprintShake)
	{
		GetWorld()->GetFirstPlayerController()->PlayerCameraManager->StopCameraShake(CurrentSprintShake);
		CurrentSprintShake = nullptr;
	}
}

回到之前创建的camera shake蓝图,我们选择一个shake pattern进行配置:

UE5相机系统初探2-6

来看看效果:

在这里插入图片描述

StartCameraShake背后会先从camera shake的缓存池中找可用的instance,如果找不到再去创建shake class对应的instance,然后播放该shake。

UCameraShakeBase* NewInst = ReclaimShakeFromExpiredPool(ShakeClass);

// No old shakes, create a new one
if (NewInst == nullptr)
{
    NewInst = NewObject<UCameraShakeBase>(this, ShakeClass);
}

if (NewInst)
{
    // Custom initialization if necessary.
    if (bIsCustomInitialized)
    {
        Params.Initializer.Execute(NewInst);
    }

    // Initialize new shake and add it to the list of active shakes
    FCameraShakeBaseStartParams StartParams;
    StartParams.CameraManager = CameraOwner;
    StartParams.Scale = Scale;
    StartParams.PlaySpace = Params.PlaySpace;
    StartParams.UserPlaySpaceRot = Params.UserPlaySpaceRot;
    StartParams.DurationOverride = Params.DurationOverride;
    NewInst->StartShake(StartParams);
}

StopCameraShake则会停止该shake,并将其加入到缓存池中。

for (int32 i = 0; i < ActiveShakes.Num(); ++i)
{
    FActiveCameraShakeInfo& ShakeInfo = ActiveShakes[i];
    if (ShakeInfo.ShakeInstance == ShakeInst)
    {
        ShakeInst->StopShake(bImmediately);
        if (bImmediately)
        {
            ShakeInst->TeardownShake();
            SaveShakeInExpiredPoolIfPossible(ShakeInfo);
            ActiveShakes.RemoveAt(i, 1);
            UE_LOG(LogCameraShake, Verbose, TEXT("UCameraModifier_CameraShake::RemoveCameraShake %s"), *GetNameSafe(ShakeInfo.ShakeInstance));
        }
        break;
    }
}

实际start以及stop的表现逻辑,是交给UCameraShakePattern这个类来实现的,不同的表现效果都继承自这个类,比如DefaultCameraShakeBase使用的是UPerlinNoiseCameraShakePattern

此外,我们还想实现当玩家起跳落地时,相机也会随之震动的效果。但是我们希望,相机离玩家越近时,震动效果越强,离玩家越远,震动效果越弱。这时可借助PlayWorldCameraShake接口实现:

void ACF_Character::Landed(const FHitResult& Hit)
{
	Super::Landed(Hit);
	UGameplayStatics::PlayWorldCameraShake(this, LandedShake, GetActorLocation(), 600.f, 2000.f);
}

代码中PlayWorldCameraShake的后三个参数分别表示播放camera shake的位置,效果开始衰减的起始距离,完全衰减的距离。来看下相机和玩家不同距离下的效果:

在这里插入图片描述

在这里插入图片描述

衰减背后的逻辑也很简单,看代码一目了然,就不赘述了。

float APlayerCameraManager::CalcRadialShakeScale(APlayerCameraManager* Camera, FVector Epicenter, float InnerRadius, float OuterRadius, float Falloff)
{
	// using camera location so stuff like spectator cameras get shakes applied sensibly as well
	// need to ensure server has reasonably accurate camera position
	FVector POVLoc = Camera->GetCameraLocation();

	if (InnerRadius < OuterRadius)
	{
		float DistPct = ((Epicenter - POVLoc).Size() - InnerRadius) / (OuterRadius - InnerRadius);
		DistPct = 1.f - FMath::Clamp(DistPct, 0.f, 1.f);
		return FMath::Pow(DistPct, Falloff);
	}
	else
	{
		// ignore OuterRadius and do a cliff falloff at InnerRadius
		return ((Epicenter - POVLoc).SizeSquared() < FMath::Square(InnerRadius)) ? 1.f : 0.f;
	}
}

camera actor

有时候我们希望使用多个相机,当触发某些条件时,视角可以切换到不同的相机。UE提供了CameraActor来帮助做这件事情。在场景中创建一个CameraActor,可以发现它其实就是一个预设好的绑定了Camera Component的普通Actor而已:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值