UE中镜头过渡的实现

正在做一个展示3d模型的Demo,其中需要去处理在场景中切换镜头。

镜头的处理

  1. 自行实现镜头的插值移动
  2. 使用其封装好的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);
}

在我的底下的代码水平下,我只能提出一个解决方案(关于内部组件之间镜头的切换)

  1. 创建一个摄像机actor,依附于当前actor,拷贝需要切换镜头的所有数据
  2. 使用SetViewTargetWithBlend进行切换
  3. 完成后,将目标摄像机组件激活,并销毁临时摄像机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);
}

image-20220405233406756

image-20220405233433299

posted @ 2022-04-06 21:57  LDnanchao  阅读(1233)  评论(0编辑  收藏  举报