正在做一個展示3d模型的Demo,其中需要去處理在場景中切換鏡頭。
鏡頭的處理
- 自行實現鏡頭的插值移動
- 使用其封裝好的SetViewTargetWithBlend函數
先進行SetViewTargetWithBlend的使用
處理同個actor中不同鏡頭的切換,以及不同actor的切換。
https://docs.unrealengine.com/4.27/zh-CN/InteractiveExperiences/Framework/Camera/
根據文檔說明
ViewTarget 結構在 PlayerCameraManager 中定義,負責向 PlayerCameraManager 提供理想的視角(POV)。ViewTarget 包含有關 Actor 目標、Actor 目標的控制器 (用於非本地控制的 Pawn),以及 PlayerState 的信息(用於在觀看的同時跟隨同一個玩家完成 Pawn 過渡和其它變更)。攝像機信息 被通過視角屬性以"FMinimalViewInfo"結構的形式傳給 PlayerCameraManager。該結構包含來自 CameraComponent 的基本攝像機信息,包括位置、轉動、 放映模式(透視或正交)、視場、投影寬度、寬高比和后期處理效果。讓 PlayerCameraManager 能訪問這些值使 PlayerCameraManager 能在 攝像機管理期間混合兩種攝像機模式。
view包含了actor以及controller等信息,其使用在於camera actor和具備camera component的actor之間進行鏡頭切換。
以下為ue4中鏡頭轉換實現代碼
void APlayerCameraManager::SetViewTarget(class AActor* NewTarget, struct FViewTargetTransitionParams TransitionParams)
{
// 確定view target是否為空
if (NewTarget == NULL)
{
NewTarget = PCOwner;
}
// 更新當前ViewTargets
ViewTarget.CheckViewTarget(PCOwner);
if (PendingViewTarget.Target)
{
PendingViewTarget.CheckViewTarget(PCOwner);
}
// 如果我們已經過渡到這個新目標,打斷。
if (PendingViewTarget.Target != NULL && NewTarget == PendingViewTarget.Target)
{
return;
}
if (UWorld* World = GetWorld())
{
World->GetTimerManager().ClearTimer(SwapPendingViewTargetWhenUsingClientSideCameraUpdatesTimerHandle);
}
//如果viewtarget 不同於新的或者我們已經在從同一個目標過渡中且已經鎖定,分配他
if ((NewTarget != ViewTarget.Target) || (PendingViewTarget.Target && BlendParams.bLockOutgoing))
{
//如果過渡時間被指定了,則設置相應的過渡view target
if (TransitionParams.BlendTime > 0)
{
// 在這種情況下,EndViewTarget()被正確調用
if (PendingViewTarget.Target == NULL)
{
PendingViewTarget.Target = ViewTarget.Target;
}
// 使用最后一幀視角
ViewTarget.POV = GetLastFrameCameraCachePOV();
BlendParams = TransitionParams;
BlendTimeToGo = TransitionParams.BlendTime;
AssignViewTarget(NewTarget, PendingViewTarget, TransitionParams);
PendingViewTarget.CheckViewTarget(PCOwner);
if (bUseClientSideCameraUpdates && GetNetMode() != NM_Client)
{
if (UWorld* World = GetWorld())
{
World->GetTimerManager().SetTimer(SwapPendingViewTargetWhenUsingClientSideCameraUpdatesTimerHandle, this, &ThisClass::SwapPendingViewTargetWhenUsingClientSideCameraUpdates, TransitionParams.BlendTime, false);
}
}
}
else
{
// 否則,立即注冊新的view target
AssignViewTarget(NewTarget, ViewTarget);
ViewTarget.CheckViewTarget(PCOwner);
//移除舊的過渡view tager,這樣我們就不會始終選擇他
PendingViewTarget.Target = NULL;
}
}
else
{
//我們已經設置了我們要轉換的viewtarget
//停止轉換
//如果我們想遍歷上面的代碼,如AssignViewTarget等
//執行
if (PendingViewTarget.Target != NULL)
{
if (!PCOwner->IsPendingKillPending() && !PCOwner->IsLocalPlayerController() && GetNetMode() != NM_Client)
{
PCOwner->ClientSetViewTarget(NewTarget, TransitionParams);
}
}
PendingViewTarget.Target = NULL;
}
}
void APlayerCameraManager::DoUpdateCamera(float DeltaTime)
{
FMinimalViewInfo NewPOV = ViewTarget.POV;
// update color scale interpolation
if (bEnableColorScaleInterp)
{
float BlendPct = FMath::Clamp((GetWorld()->TimeSeconds - ColorScaleInterpStartTime) / ColorScaleInterpDuration, 0.f, 1.0f);
ColorScale = FMath::Lerp(OriginalColorScale, DesiredColorScale, BlendPct);
// if we've maxed
if (BlendPct == 1.0f)
{
// disable further interpolation
bEnableColorScaleInterp = false;
}
}
// Don't update outgoing viewtarget during an interpolation when bLockOutgoing is set.
if ((PendingViewTarget.Target == NULL) || !BlendParams.bLockOutgoing)
{
// Update current view target
ViewTarget.CheckViewTarget(PCOwner);
UpdateViewTarget(ViewTarget, DeltaTime);
}
// our camera is now viewing there
NewPOV = ViewTarget.POV;
// if we have a pending view target, perform transition from one to another.
if (PendingViewTarget.Target != NULL)
{
BlendTimeToGo -= DeltaTime;
// Update pending view target
PendingViewTarget.CheckViewTarget(PCOwner);
UpdateViewTarget(PendingViewTarget, DeltaTime);
// blend....
if (BlendTimeToGo > 0)
{
float DurationPct = (BlendParams.BlendTime - BlendTimeToGo) / BlendParams.BlendTime;
float BlendPct = 0.f;
switch (BlendParams.BlendFunction)
{
case VTBlend_Linear:
BlendPct = FMath::Lerp(0.f, 1.f, DurationPct);
break;
case VTBlend_Cubic:
BlendPct = FMath::CubicInterp(0.f, 0.f, 1.f, 0.f, DurationPct);
break;
case VTBlend_EaseIn:
BlendPct = FMath::Lerp(0.f, 1.f, FMath::Pow(DurationPct, BlendParams.BlendExp));
break;
case VTBlend_EaseOut:
BlendPct = FMath::Lerp(0.f, 1.f, FMath::Pow(DurationPct, 1.f / BlendParams.BlendExp));
break;
case VTBlend_EaseInOut:
BlendPct = FMath::InterpEaseInOut(0.f, 1.f, DurationPct, BlendParams.BlendExp);
break;
case VTBlend_PreBlended:
BlendPct = 1.0f;
break;
default:
break;
}
// Update pending view target blend
NewPOV = ViewTarget.POV;
NewPOV.BlendViewInfo(PendingViewTarget.POV, BlendPct);//@TODO: CAMERA: Make sure the sense is correct! BlendViewTargets(ViewTarget, PendingViewTarget, BlendPct);
}
else
{
// we're done blending, set new view target
ViewTarget = PendingViewTarget;
// clear pending view target
PendingViewTarget.Target = NULL;
BlendTimeToGo = 0;
// our camera is now viewing there
NewPOV = PendingViewTarget.POV;
OnBlendComplete().Broadcast();
}
}
if (bEnableFading)
{
if (bAutoAnimateFade)
{
FadeTimeRemaining = FMath::Max(FadeTimeRemaining - DeltaTime, 0.0f);
if (FadeTime > 0.0f)
{
FadeAmount = FadeAlpha.X + ((1.f - FadeTimeRemaining / FadeTime) * (FadeAlpha.Y - FadeAlpha.X));
}
if ((bHoldFadeWhenFinished == false) && (FadeTimeRemaining <= 0.f))
{
// done
StopCameraFade();
}
}
if (bFadeAudio)
{
ApplyAudioFade();
}
}
if (AllowPhotographyMode())
{
const bool bPhotographyCausedCameraCut = UpdatePhotographyCamera(NewPOV);
bGameCameraCutThisFrame = bGameCameraCutThisFrame || bPhotographyCausedCameraCut;
}
// Cache results
FillCameraCache(NewPOV);
}
在我的底下的代碼水平下,我只能提出一個解決方案(關於內部組件之間鏡頭的切換)
- 創建一個攝像機actor,依附於當前actor,拷貝需要切換鏡頭的所有數據
- 使用
SetViewTargetWithBlend
進行切換 - 完成后,將目標攝像機組件激活,並銷毀臨時攝像機actor
以此完成組件的切換
當然最好的辦法就是建立一個公用的臨時攝像機actor,由GameMode提供函數進行切換
void AShowPawn_Base::TurnCamera()
{
/*AActor* Actor = (AActor*)CameraActor;
if(!Actor)
{
return;
}
APlayerController* PlayerController = UGameplayStatics::GetPlayerController(this, 0);
PlayerController->SetViewTargetWithBlend(Actor, 3.f);*/
//OtherCamera->SetActive(true);
//Camera->SetActive(false);
APlayerController* PlayerController = UGameplayStatics::GetPlayerController(this, 0);
PlayerController->SetViewTargetWithBlend(this, 3);
ACameraActor* CameraActorz = GetWorld()->SpawnActor<ACameraActor>(ACameraActor::StaticClass()) ;
CameraActorz->AttachToActor(this, FAttachmentTransformRules::KeepWorldTransform);
CameraActorz->SetActorRelativeTransform(OtherCamera->GetRelativeTransform());
PlayerController->SetViewTargetWithBlend(CameraActorz, 3.f);
}