本帖最后由 bluerose 于 2016-8-20 18:39 编辑
最近看了几天PlayerCameraManager的代码,大致看明白了,本着分享的原则,在此分享一些经验。 PlayerCameraManager,顾名思义就是管理角色摄像仪与视角的,你可以通过继承的方式,编写自己的PlayerCameraManager,之后在你角色的控制类中指定即可。 下面说一下主要的运行过程: 这里可以参考一下ShooterGame(ShooterPlayerCameraManager),从函数UpdateCamera开始看 [mw_shl_code=cpp,true]{
// Make sure view target is valid
if( NewTarget == NULL )
{
NewTarget = PCOwner;
}
// Update current ViewTargets
ViewTarget.CheckViewTarget(PCOwner);
if( PendingViewTarget.Target )
{
PendingViewTarget.CheckViewTarget(PCOwner);
}
// If we're already transitioning to this new target, don't interrupt.
if( PendingViewTarget.Target != NULL && NewTarget == PendingViewTarget.Target )
{
return;
}[/mw_shl_code] 这里并没有视角的相关逻辑,所以进一步看父类的UpdateCamera函数。 [mw_shl_code=cpp,true]void APlayerCameraManager::UpdateCamera(float DeltaTime)
{
if ((PCOwner->layer && PCOwner->IsLocalPlayerController()) || !bUseClientSideCameraUpdates || bDebugClientSideCamera)
{
DoUpdateCamera(DeltaTime);
if (GetNetMode() == NM_Client && bShouldSendClientSideCameraUpdate)
{
// compress the rotation down to 4 bytes
int32 const ShortYaw = FRotator::CompressAxisToShort(CameraCache.POV.Rotation.Yaw);
int32 const ShortPitch = FRotator::CompressAxisToShort(CameraCache.POV.Rotation.Pitch);
int32 const CompressedRotation = (ShortYaw << 16) | ShortPitch;
PCOwner->ServerUpdateCamera(CameraCache.POV.Location, CompressedRotation);
bShouldSendClientSideCameraUpdate = false;
}
}
}[/mw_shl_code] 这里是一些网络的逻辑,继续看DoUpdateCamera函数 [mw_shl_code=cpp,true]这里是一些网络的逻辑,继续看DoUpdateCamera函数[/mw_shl_code] 接下来看UpdateViewTarget函数,计算视角的逻辑都在里面 [mw_shl_code=cpp,true]void APlayerCameraManager::UpdateViewTarget(FTViewTarget& OutVT, float DeltaTime)
{
// Don't update outgoing viewtarget during an interpolation
if ((PendingViewTarget.Target != NULL) && BlendParams.bLockOutgoing && OutVT.Equal(ViewTarget))
{
return;
}
//设置默认属性的摄像机
// store previous POV, in case we need it later
FMinimalViewInfo OrigPOV = OutVT.POV;
//@TODO: CAMERA: Should probably reset the view target POV fully here
OutVT.POV.FOV = DefaultFOV;
OutVT.POV.OrthoWidth = DefaultOrthoWidth;
OutVT.POV.bConstrainAspectRatio = false;
OutVT.POV.bUseFieldOfViewForLOD = true;
OutVT.POV.ProjectionMode = bIsOrthographic ? ECameraProjectionMode::Orthographic : ECameraProjectionMode:erspective;
OutVT.POV.PostProcessSettings.SetBaseValues();
OutVT.POV.PostProcessBlendWeight = 1.0f;
bool bDoNotApplyModifiers = false;
//如果ViewTarget是个摄像机的话,就直接获取摄像机的视角
if (ACameraActor* CamActor = Cast<ACameraActor>(OutVT.Target))
{
// Viewing through a camera actor.
CamActor->GetCameraComponent()->GetCameraView(DeltaTime, OutVT.POV);
}
else
{
//下面都是一些不同模式的摄像机的逻辑,你可以在游戏中按下~,输入Camera XXXX的方式切换这个摄像机
//默认的摄像机是Default
static const FName NAME_Fixed = FName(TEXT("Fixed"));
static const FName NAME_ThirdPerson = FName(TEXT("ThirdPerson"));
static const FName NAME_FreeCam = FName(TEXT("FreeCam"));
static const FName NAME_FreeCam_Default = FName(TEXT("FreeCam_Default"));
static const FName NAME_FirstPerson = FName(TEXT("FirstPerson"));
if (CameraStyle == NAME_Fixed)
{
// do not update, keep previous camera position by restoring
// saved POV, in case CalcCamera changes it but still returns false
OutVT.POV = OrigPOV;
// don't apply modifiers when using this debug camera mode
bDoNotApplyModifiers = true;
}
else if (CameraStyle == NAME_ThirdPerson || CameraStyle == NAME_FreeCam || CameraStyle == NAME_FreeCam_Default)
{
// Simple third person view implementation
FVector Loc = OutVT.Target->GetActorLocation();
FRotator Rotator = OutVT.Target->GetActorRotation();
if (OutVT.Target == PCOwner)
{
Loc = PCOwner->GetFocalLocation();
}
// Take into account Mesh Translation so it takes into account the PostProcessing we do there.
// @fixme, can crash in certain BP cases where default mesh is null
// APawn* TPawn = Cast<APawn>(OutVT.Target);
// if ((TPawn != NULL) && (TPawn->Mesh != NULL))
// {
// Loc += FQuatRotationMatrix(OutVT.Target->GetActorQuat()).TransformVector(TPawn->Mesh->RelativeLocation - GetDefault<APawn>(TPawn->GetClass())->Mesh->RelativeLocation);
// }
//OutVT.Target.GetActorEyesViewPoint(Loc, Rot);
if( CameraStyle == NAME_FreeCam || CameraStyle == NAME_FreeCam_Default )
{
Rotator = PCOwner->GetControlRotation();
}
FVector Pos = Loc + ViewTargetOffset + FRotationMatrix(Rotator).TransformVector(FreeCamOffset) - Rotator.Vector() * FreeCamDistance;
FCollisionQueryParams BoxParams(NAME_FreeCam, false, this);
BoxParams.AddIgnoredActor(OutVT.Target);
FHitResult Result;
GetWorld()->SweepSingleByChannel(Result, Loc, Pos, FQuat::Identity, ECC_Camera, FCollisionShape::MakeBox(FVector(12.f)), BoxParams);
OutVT.POV.Location = !Result.bBlockingHit ? Pos : Result.Location;
OutVT.POV.Rotation = Rotator;
// don't apply modifiers when using this debug camera mode
bDoNotApplyModifiers = true;
}
else if (CameraStyle == NAME_FirstPerson)
{
// Simple first person, view through viewtarget's 'eyes'
OutVT.Target->GetActorEyesViewPoint(OutVT.POV.Location, OutVT.POV.Rotation);
// don't apply modifiers when using this debug camera mode
bDoNotApplyModifiers = true;
}
else
{
//默认摄像机会执行这个函数
UpdateViewTargetInternal(OutVT, DeltaTime);
}
}
//这个应该是执行CameraShakes的逻辑
if (!bDoNotApplyModifiers || bAlwaysApplyModifiers)
{
// Apply camera modifiers at the end (view shakes for example)
ApplyCameraModifiers(DeltaTime, OutVT.POV);
}
//头戴设备的视角逻辑
if (bFollowHmdOrientation)
{
if (GEngine->HMDDevice.IsValid() && GEngine->HMDDevice->IsHeadTrackingAllowed())
{
GEngine->HMDDevice->UpdatePlayerCameraRotation(this, OutVT.POV);
}
}
// Synchronize the actor with the view target results
SetActorLocationAndRotation(OutVT.POV.Location, OutVT.POV.Rotation, false);
UpdateCameraLensEffects(OutVT);
}[/mw_shl_code] 综上所述,你应该把摄像机逻辑写在这,如果想了解得更加清楚,可以继续从UpdateViewTargetInternal函数看下去,一直看到CalcCamera为止。 下面大致说一下编写思路: 在使用编辑器新建完自己的CameraManager后,在控制类中制定,例如: PlayerCameraManagerClass = ADemoCameraManager::StaticClass(); 然后我贴一下自己写的代码,这里我实现了一个固定视角: [mw_shl_code=cpp,true]// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "Camera/PlayerCameraManager.h"
#include "DemoCameraManager.generated.h"
/**
*
*/
UCLASS()
class DEMO_API ADemoCameraManager : public APlayerCameraManager
{
GENERATED_BODY()
protected:
virtual void UpdateViewTarget(FTViewTarget& OutVT, float DeltaTime) override;
//UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = TViewTarget)
//struct FMinimalViewInfo SceneFixedPOV;
};[/mw_shl_code] [mw_shl_code=cpp,true]// Fill out your copyright notice in the Description page of Project Settings.
#include "Demo.h"
#include "DemoCameraManager.h"
#include "DemoCharacter.h"
void ADemoCameraManager::UpdateViewTarget(FTViewTarget& OutVT, float DeltaTime)
{
if (bFollowHmdOrientation)
{
//如果有VR设备就直接运行引擎原始函数
Super::UpdateViewTarget(OutVT, DeltaTime);
return;
}
// Don't update outgoing viewtarget during an interpolation
if ((PendingViewTarget.Target != NULL) && BlendParams.bLockOutgoing && OutVT.Equal(ViewTarget))
{
return;
}
bool bDoNotApplyModifiers = false;
if (ACameraActor* CamActor = Cast<ACameraActor>(OutVT.Target))
{
// Viewing through a camera actor.
CamActor->GetCameraComponent()->GetCameraView(DeltaTime, OutVT.POV);
}
else
{
if (CameraStyle == FName("SceneFixed"))
{
//这里我感觉可能用PendingViewTarget来传递摄像机坐标和旋转比较好,但是还没测试过
//自己定义一个场景固定视角
ADemoCharacter *Character = Cast<ADemoCharacter>(GetOwningPlayerController()->GetPawn());
OutVT.POV.Location = Character->ViewTargetLocation;
OutVT.POV.Rotation = Character->ViewTargetRotator;
//DesiredView.FOV = FieldOfView;
//DesiredView.AspectRatio = AspectRatio;
// don't apply modifiers when using this debug camera mode
bDoNotApplyModifiers = true;
}else if (CameraStyle==FName("Default"))
{
//默认方式是直接取得摄像机的参数来设置FTViewTarget.pov,而摄像机被控制类、SpringArm控制。
UpdateViewTargetInternal(OutVT, DeltaTime);
}
else
{
Super::UpdateViewTarget(OutVT, DeltaTime);
}
}
if (!bDoNotApplyModifiers || bAlwaysApplyModifiers)
{
// Apply camera modifiers at the end (view shakes for example)
ApplyCameraModifiers(DeltaTime, OutVT.POV);
}
// Synchronize the actor with the view target results
SetActorLocationAndRotation(OutVT.POV.Location, OutVT.POV.Rotation, false);
UpdateCameraLensEffects(OutVT);
}[/mw_shl_code] 后面是固定视角VolumeActor的代码: [mw_shl_code=cpp,true]// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameFramework/Actor.h"
#include "Character/DemoCharacter.h"
#include "Character/DemoCameraManager.h"
#include "Character/DemoPlayerController.h"
#include "Runtime/Engine/Classes/Kismet/KismetMathLibrary.h"
#include "BaseSceneFixedViewVolume.generated.h"
UCLASS()
class DEMO_API ABaseSceneFixedViewVolume : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ABaseSceneFixedViewVolume();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
FVector Lcation;
FRotator Rotator;
UPROPERTY(EditDefaultsOnly, Category = "SceneFixedView", meta = (AllowPrivateAccess = "true"))
UBoxComponent* Triggers;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "SceneFixedView", meta = (AllowPrivateAccess = "true"))
UCameraComponent* Camera;
UFUNCTION()
virtual void OnBeginOverlap(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweeoResult);
UFUNCTION()
virtual void OnEndOverlap(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "SceneFixedView")
bool bCalView=false;
UPROPERTY(EditDefaultsOnly,Category="SceneFixedView")
FVector VolumeSize = FVector(500, 500, 200);
ADemoCharacter* Character;
ADemoCameraManager* CameraManage;
FName OriginMode;
};[/mw_shl_code] [mw_shl_code=cpp,true]// Fill out your copyright notice in the Description page of Project Settings.
#include "Demo.h"
#include "BaseSceneFixedViewVolume.h"
// Sets default values
ABaseSceneFixedViewVolume::ABaseSceneFixedViewVolume()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
Triggers = CreateDefaultSubobject<UBoxComponent>(TEXT("Triggers"));
Triggers->SetBoxExtent(VolumeSize);
Triggers->SetRelativeLocation(FVector(0,0,VolumeSize.Z/2));
RootComponent = Triggers;
Camera= CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
Camera->SetRelativeLocation(FVector(VolumeSize.X,VolumeSize.Y,VolumeSize.Z));
Camera->AttachTo(RootComponent);
//FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
//FollowCamera->AttachTo(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
//FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm
Triggers->OnComponentBeginOverlap.AddDynamic(this, &ABaseSceneFixedViewVolume::OnBeginOverlap);
Triggers->OnComponentEndOverlap.AddDynamic(this, &ABaseSceneFixedViewVolume::OnEndOverlap);
}
// Called when the game starts or when spawned
void ABaseSceneFixedViewVolume::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ABaseSceneFixedViewVolume::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
if (bCalView)
{
if (Character)
{
Character->ViewTargetLocation = Camera->K2_GetComponentLocation();
Character->ViewTargetRotator = UKismetMathLibrary::FindLookAtRotation(Camera->K2_GetComponentLocation(), Character->GetActorLocation());
}
}
}
void ABaseSceneFixedViewVolume::OnBeginOverlap(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweeoResult)
{
Character = Cast<ADemoCharacter>(OtherActor);
if (Character)
{
bCalView = true;
ADemoPlayerController* Controller = Cast<ADemoPlayerController>(Character->GetController());
if (Controller)
{
OriginMode=Controller->layerCameraManager->CameraStyle;
Controller->SetCameraMode(FName("SceneFixed"));
//因为我使用控制类来管理视角,为了解决一些视角问题,所以在进入时,必须重新设置视角,屏蔽鼠标修改视角
Controller->SetControlRotation(FRotator(0.0f,90.0f,0.0f));
Controller->SetIgnoreLookInput(true);
}
}
}
void ABaseSceneFixedViewVolume::OnEndOverlap(class AActor* OtherActor ,class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if (OtherActor==Character)
{
bCalView = false;
ADemoPlayerController* Controller = Cast<ADemoPlayerController>(Character->GetController());
if (Controller)
{
if (OriginMode!=FName(""))
{
Controller->SetCameraMode(OriginMode);
}
else
{
Controller->SetCameraMode(FName("Default"));
}
Controller->SetIgnoreLookInput(false);
}
}
Character= nullptr;
}[/mw_shl_code]
|