Unreal Engine 5.6 官方相机系统插件深度解析
文档版本: 1.0 | 更新日期: 2026-02-16
目录
1. 概述与架构设计
1.1 插件概述
GameplayCameras 是 Unreal Engine 5.6 引入的官方相机系统插件,旨在提供一套数据驱动、高度模块化的相机解决方案。该插件取代了传统的 APlayerCameraManager 模式,采用现代化的节点图架构设计。
1.1.1 设计目标
| 目标 | 说明 |
|---|---|
| 数据驱动 | 相机行为通过资产(Asset)配置,而非硬编码 |
| 可视化编辑 | 支持节点图编辑器,设计师可直观调整相机逻辑 |
| 高性能 | 采用评估器缓存、内存池等优化技术 |
| 可扩展 | 支持自定义节点、服务、导演等扩展点 |
| 平滑过渡 | 内置强大的混合栈系统,支持复杂的相机切换 |
1.1.2 核心概念对照表
┌─────────────────────┬──────────────────────────────────────────────┐
│ 概念 │ 说明 │
├─────────────────────┼──────────────────────────────────────────────┤
│ UCameraAsset │ 相机资产,包含导演、过渡、参数定义 │
│ UCameraRigAsset │ 相机装备,定义一组节点树实现特定相机行为 │
│ UCameraNode │ 相机节点,可组合的最小功能单元 │
│ FCameraNodeEvaluator│ 运行时评估器,执行节点的实际逻辑 │
│ FCameraSystemEvaluator│ 相机系统评估器,管理整个评估流程 │
│ FCameraEvaluationContext│ 评估上下文,提供相机运行的环境信息 │
└─────────────────────┴──────────────────────────────────────────────┘
1.1.3 与传统相机系统的对比
// 传统方式 (APlayerCameraManager)
// - 每帧通过 PlayerCameraManager->UpdateViewTarget() 更新
// - 相机逻辑分散在各个 Actor 中
// - 难以实现复杂的混合过渡
// GameplayCameras 方式
// - 通过 UGameplayCameraComponent 运行 UCameraAsset
// - 相机逻辑集中在可复用的资产中
// - 混合栈自动处理相机切换和过渡
1.2 核心架构模式
GameplayCameras 采用 节点-评估器分离 的设计模式,这是一种类似于虚幻引擎蓝图的数据-逻辑分离架构。
1.2.1 节点与评估器分离
┌────────────────────────────────────────────────────────────────────┐
│ 架构层次图 │
├────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ UCameraAsset │ │ UCameraDirector │ │
│ │ (相机资产) │────────▶│ (导演) │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ UCameraRigAsset │ │FCameraDirectorEval │ │
│ │ (相机装备) │────────▶│ (导演评估器) │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ UCameraNode │ Build │FCameraNodeEvaluator │ │
│ │ (相机节点) │────────▶│ (节点评估器) │ │
│ │ 【数据层】 │ │ 【逻辑层】 │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────┘
源码示例 - 节点定义 (Public/Core/CameraNode.h:38):
/**
* The base class for a camera node.
*/
UCLASS(MinimalAPI, Abstract, DefaultToInstanced, EditInlineNew,
meta=(CameraNodeCategories="Miscellaneous"))
class UCameraNode
: public UObject
, public IObjectTreeGraphObject
{
GENERATED_BODY()
public:
// 获取子节点列表
UE_API FCameraNodeChildrenView GetChildren();
// 构建评估器 - 关键方法!
UE_API FCameraNodeEvaluatorPtr BuildEvaluator(FCameraNodeEvaluatorBuilder& Builder) const;
protected:
// 子类重写此方法创建对应的评估器
virtual FCameraNodeEvaluatorPtr OnBuildEvaluator(FCameraNodeEvaluatorBuilder& Builder) const
{
return nullptr;
}
public:
// 节点是否启用
UPROPERTY(EditAnywhere, Category=Common)
bool bIsEnabled = true;
};
源码示例 - 评估器基类 (Public/Core/CameraNodeEvaluator.h:268):
/**
* Base class for objects responsible for running a camera node.
*/
class FCameraNodeEvaluator
{
UE_GAMEPLAY_CAMERAS_DECLARE_RTTI_BASE(GAMEPLAYCAMERAS_API, FCameraNodeEvaluator)
public:
// 构建子评估器
void Build(const FCameraNodeEvaluatorBuildParams& Params);
// 初始化评估器
void Initialize(const FCameraNodeEvaluatorInitializeParams& Params,
FCameraNodeEvaluationResult& OutResult);
// 运行评估 - 每帧调用的核心方法!
GAMEPLAYCAMERAS_API void Run(const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult);
// 获取关联的节点数据
const UCameraNode* GetCameraNode() const { return PrivateCameraNode; }
protected:
// 子类实现具体的评估逻辑
GAMEPLAYCAMERAS_API virtual void OnRun(const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult) {}
private:
// 关联的节点数据(只读)
TObjectPtr<const UCameraNode> PrivateCameraNode;
};
1.2.2 设计优势
| 优势 | 说明 |
|---|---|
| 序列化友好 | 节点数据可保存为资产,评估器无需序列化 |
| 内存高效 | 评估器可复用,避免每帧创建临时对象 |
| 热重载支持 | 节点资产修改后可热重载,评估器自动重建 |
| 调试分离 | 调试信息可在评估器中收集,不影响数据层 |
1.2.3 实际案例:BoomArm 节点
节点定义 (Public/Nodes/Common/BoomArmCameraNode.h:17):
UCLASS(MinimalAPI, meta=(CameraNodeCategories="Common,Transform"))
class UBoomArmCameraNode : public UCameraNode
{
GENERATED_BODY()
public:
UBoomArmCameraNode(const FObjectInitializer& ObjInit);
protected:
// 创建对应的评估器
virtual FCameraNodeEvaluatorPtr OnBuildEvaluator(FCameraNodeEvaluatorBuilder& Builder) const override;
public:
// 吊臂偏移量 - 设计师可在编辑器中调整
UPROPERTY(EditAnywhere, Category=Common)
FVector3dCameraParameter BoomOffset;
// 吊臂长度插值器
UPROPERTY(EditAnywhere, Category=Common)
TObjectPtr<UCameraValueInterpolator> BoomLengthInterpolator;
// 输入槽 - 接收玩家输入
UPROPERTY(meta=(ObjectTreeGraphPinDirection=Input))
TObjectPtr<UInput2DCameraNode> InputSlot;
};
1.3 模块依赖关系图
1.3.1 整体架构图
1.3.2 评估流程图
1.3.3 层系统架构
1.4 目录结构说明
1.4.1 源码目录组织
GameplayCameras/Source/GameplayCameras/
│
├── Public/ # 公共头文件
│ │
│ ├── Core/ # 核心系统 (72个文件)
│ │ ├── CameraSystemEvaluator.h # 相机系统评估器
│ │ ├── CameraNodeEvaluator.h # 节点评估器基类
│ │ ├── CameraNode.h # 节点基类
│ │ ├── CameraPose.h # 相机姿态
│ │ ├── CameraVariableTable.h # 变量表
│ │ ├── CameraRigAsset.h # 相机装备资产
│ │ ├── CameraAsset.h # 相机资产
│ │ ├── BlendCameraNode.h # 混合节点基类
│ │ ├── BlendStackCameraNode.h # 混合栈节点
│ │ ├── RootCameraNode.h # 根节点
│ │ └── DefaultRootCameraNode.h # 默认根节点实现
│ │
│ ├── Nodes/ # 节点实现
│ │ ├── Common/ # 通用节点 (21个)
│ │ │ ├── BoomArmCameraNode.h
│ │ │ ├── ArrayCameraNode.h
│ │ │ └── ...
│ │ ├── Blends/ # 混合节点 (8个)
│ │ ├── Attach/ # 附加节点 (4个)
│ │ ├── Framing/ # 构图节点 (5个)
│ │ ├── Input/ # 输入节点 (6个)
│ │ ├── Shakes/ # 震动节点 (6个)
│ │ └── Collision/ # 碰撞节点 (2个)
│ │
│ ├── Services/ # 评估服务 (4个)
│ │ ├── CameraModifierService.h
│ │ ├── CameraShakeService.h
│ │ └── ...
│ │
│ ├── Directors/ # 导演系统 (6个)
│ │ ├── BlueprintCameraDirector.h
│ │ ├── SingleCameraDirector.h
│ │ └── ...
│ │
│ ├── GameFramework/ # 游戏框架集成 (19个)
│ │ ├── GameplayCameraComponent.h
│ │ ├── GameplayCameraActor.h
│ │ └── ...
│ │
│ ├── Debug/ # 调试系统 (24个)
│ │ └── ...
│ │
│ └── Build/ # 构建系统
│ └── ...
│
└── Private/ # 私有实现文件
└── [与 Public 对应的目录结构]
1.4.2 关键目录说明
| 目录 | 职责 | 关键文件 |
|---|---|---|
Core/ |
核心类型定义和基类 | CameraSystemEvaluator.h, CameraNodeEvaluator.h |
Nodes/ |
可组合的相机功能节点 | BoomArmCameraNode.h, SmoothBlendCameraNode.h |
Services/ |
横切关注点的服务实现 | CameraShakeService.h, CameraModifierService.h |
Directors/ |
控制相机装备激活的导演 | SingleCameraDirector.h, BlueprintCameraDirector.h |
GameFramework/ |
与 UE 游戏框架的集成 | GameplayCameraComponent.h |
Debug/ |
调试可视化和诊断工具 | CameraDebugBlock.h |
1.4.3 命名约定
// 节点类命名:U + 功能描述 + CameraNode
UCLASS()
class UBoomArmCameraNode : public UCameraNode { ... };
// 评估器类命名:F + 功能描述 + CameraNodeEvaluator
class FBoomArmCameraNodeEvaluator : public FCameraNodeEvaluator { ... };
// 资产类命名:U + 功能描述 + Asset
UCLASS()
class UCameraRigAsset : public UBaseCameraObject { ... };
// 服务类命名:F + 功能描述 + Service
class FCameraShakeService : public FCameraEvaluationService { ... };
2. 核心类与继承体系
2.1 评估器体系 (FCameraNodeEvaluator 家族)
评估器是 GameplayCameras 的运行时逻辑层,负责执行实际的相机计算。所有评估器都继承自 FCameraNodeEvaluator 基类。
2.1.1 自定义 RTTI 系统
评估器系统使用自定义的运行时类型识别(RTTI)机制,而非虚幻引擎原生的 UObject 反射系统。这样做的原因是评估器不是 UObject,需要轻量级的类型识别。
源码位置: Public/Core/CameraObjectRtti.h
// 类型ID结构
struct FCameraObjectTypeID
{
uint32 ID;
bool IsValid() const { return ID != MAX_uint32; }
static FCameraObjectTypeID Invalid() { return FCameraObjectTypeID(MAX_uint32); }
};
// 类型信息结构
struct FCameraObjectTypeInfo
{
FName TypeName;
uint32 Sizeof = 0;
uint32 Alignof = 0;
using FConstructor = void(*)(void*);
FConstructor Constructor;
using FDestructor = void(*)(void*);
FDestructor Destructor;
};
RTTI 宏系统:
// 在基类中声明 RTTI(根类)
#define UE_GAMEPLAY_CAMERAS_DECLARE_RTTI_BASE(ApiDeclSpec, ClassName)\
public:\
static const TCameraObjectTypeID<ClassName>& StaticTypeID();\
virtual const FCameraObjectTypeID& GetTypeID() const;\
virtual bool IsKindOf(const FCameraObjectTypeID& InTypeID) const;\
template<typename Type> Type* CastThis();\
template<typename Type> Type* CastThisChecked();
// 在派生类中声明 RTTI
#define UE_GAMEPLAY_CAMERAS_DECLARE_RTTI(ApiDeclSpec, ClassName, BaseClassName)\
public:\
using Super = BaseClassName;\
static const TCameraObjectTypeID<ClassName>& StaticTypeID();\
virtual const FCameraObjectTypeID& GetTypeID() const override;\
virtual bool IsKindOf(const FCameraObjectTypeID& InTypeID) const override;
// 在 cpp 文件中定义 RTTI
#define UE_GAMEPLAY_CAMERAS_DEFINE_RTTI(ClassName)\
const TCameraObjectTypeID<ClassName> ClassName::PrivateTypeID = \
TCameraObjectTypeID<ClassName>::RegisterType(#ClassName);
实际使用示例:
// 声明评估器
class FBoomArmCameraNodeEvaluator : public FCameraNodeEvaluator
{
UE_DECLARE_CAMERA_NODE_EVALUATOR(GAMEPLAYCAMERAS_API, FBoomArmCameraNodeEvaluator)
// ...
};
// 在 cpp 中定义
UE_DEFINE_CAMERA_NODE_EVALUATOR(FBoomArmCameraNodeEvaluator)
// 类型检查和转换
if (FBoomArmCameraNodeEvaluator* BoomEval = Evaluator->CastThis<FBoomArmCameraNodeEvaluator>())
{
// 安全地访问特定类型的方法
}
2.1.2 评估器继承层次
2.1.3 评估器核心方法生命周期
// 评估器的生命周期
class FCameraNodeEvaluator
{
public:
// 1. 构建阶段 - 创建子评估器树
void Build(const FCameraNodeEvaluatorBuildParams& Params);
// 2. 初始化阶段 - 设置初始状态
void Initialize(const FCameraNodeEvaluatorInitializeParams& Params,
FCameraNodeEvaluationResult& OutResult);
// 3. 每帧评估 - 核心逻辑
void Run(const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult);
// 4. 序列化 - 保存/恢复状态
void Serialize(const FCameraNodeEvaluatorSerializeParams& Params, FArchive& Ar);
};
2.1.4 评估器标志
// 源码位置: Public/Core/CameraNodeEvaluator.h:37
enum class ECameraNodeEvaluatorFlags
{
None = 0,
// 需要参数更新回调
NeedsParameterUpdate = 1 << 0,
// 需要序列化
NeedsSerialize = 1 << 1,
// 支持操作(如 IK)
SupportsOperations = 1 << 2,
// 默认标志
Default = NeedsParameterUpdate | NeedsSerialize | SupportsOperations
};
2.2 节点体系 (UCameraNode 家族)
节点是 GameplayCameras 的数据层,定义相机行为的数据结构和编辑器可见属性。
2.2.1 节点基类
源码位置: Public/Core/CameraNode.h:38
UCLASS(MinimalAPI, Abstract, DefaultToInstanced, EditInlineNew,
meta=(CameraNodeCategories="Miscellaneous"))
class UCameraNode : public UObject, public IObjectTreeGraphObject
{
GENERATED_BODY()
public:
using FCameraBuildLog = UE::Cameras::FCameraBuildLog;
using FCameraNodeEvaluatorBuilder = UE::Cameras::FCameraNodeEvaluatorBuilder;
// 获取子节点视图
UE_API FCameraNodeChildrenView GetChildren();
// 构建前预处理
UE_API void PreBuild(FCameraBuildLog& BuildLog);
// 构建分配信息
UE_API void Build(FCameraObjectBuildContext& BuildContext);
// 构建评估器 - 关键方法!
UE_API FCameraNodeEvaluatorPtr BuildEvaluator(FCameraNodeEvaluatorBuilder& Builder) const;
protected:
// 子类重写:返回子节点
virtual FCameraNodeChildrenView OnGetChildren() { return FCameraNodeChildrenView(); }
// 子类重写:预处理逻辑
virtual void OnPreBuild(FCameraBuildLog& BuildLog) {}
// 子类重写:构建逻辑
virtual void OnBuild(FCameraObjectBuildContext& BuildContext) {}
// 子类重写:创建对应的评估器
virtual FCameraNodeEvaluatorPtr OnBuildEvaluator(FCameraNodeEvaluatorBuilder& Builder) const
{
return nullptr;
}
public:
// 节点启用状态
UPROPERTY(EditAnywhere, Category=Common)
bool bIsEnabled = true;
#if WITH_EDITORONLY_DATA
// 编辑器中的节点位置
UPROPERTY()
FIntVector2 GraphNodePos = FIntVector2::ZeroValue;
// 节点注释
UPROPERTY()
FString GraphNodeComment;
#endif
};
2.2.2 节点继承层次
2.2.3 节点分类(CameraNodeCategories)
节点通过 meta=(CameraNodeCategories="...") 指定在编辑器中显示的分类:
| 分类 | 说明 | 示例节点 |
|---|---|---|
Common |
通用节点 | UArrayCameraNode, UBoomArmCameraNode |
Common,Transform |
变换相关 | UOffsetCameraNode, USetLocationCameraNode |
Blends |
混合节点 | USmoothBlendCameraNode, ULinearBlendCameraNode |
Input |
输入处理 | UInput2DCameraNode, UInput1DCameraNode |
Attach |
附加功能 | UAttachToActorCameraNode, UAttachToPlayerPawnCameraNode |
Framing |
构图功能 | UDollyFramingCameraNode, UPanningFramingCameraNode |
Shakes |
震动效果 | UEnvelopeShakeCameraNode, UPerlinNoiseRotationShakeCameraNode |
Collision |
碰撞处理 | UCollisionPushCameraNode |
2.2.4 实际案例:Sequence 节点
UArrayCameraNode(编辑器显示名为 "Sequence")是一个组合节点,按顺序执行多个子节点:
// 源码位置: Public/Nodes/Common/ArrayCameraNode.h:14
UCLASS(MinimalAPI, meta=(DisplayName="Sequence", CameraNodeCategories="Common,Utility"))
class UArrayCameraNode : public UCameraNode
{
GENERATED_BODY()
public:
UArrayCameraNode(const FObjectInitializer& ObjInit);
protected:
// 返回子节点列表
virtual FCameraNodeChildrenView OnGetChildren() override;
// 创建评估器
virtual FCameraNodeEvaluatorPtr OnBuildEvaluator(FCameraNodeEvaluatorBuilder& Builder) const override;
public:
// 子节点数组 - 设计师在编辑器中配置
UPROPERTY()
TArray<TObjectPtr<UCameraNode>> Children;
};
使用场景示例:
Sequence 节点结构:
┌─────────────────────────────────────┐
│ Sequence (ArrayNode) │
├─────────────────────────────────────┤
│ [0] AttachToPlayerPawn │ ← 附加到玩家
│ [1] BoomArm │ ← 吊臂控制
│ [2] CollisionPush │ ← 碰撞推挤
│ [3] DampenRotation │ ← 旋转阻尼
└─────────────────────────────────────┘
2.3 资产体系
资产是 GameplayCameras 的可复用配置单元,存储在内容浏览器中。
2.3.1 资产继承关系
2.3.2 UCameraAsset 相机资产
源码位置: Public/Core/CameraAsset.h:69
UCameraAsset 是顶级相机配置容器,包含:
UCLASS(MinimalAPI)
class UCameraAsset : public UObject
, public IHasCameraBuildStatus
, public IObjectTreeGraphObject
, public IObjectTreeGraphRootObject
{
GENERATED_BODY()
public:
// 获取/设置导演
UCameraDirector* GetCameraDirector() const { return CameraDirector; }
void SetCameraDirector(UCameraDirector* InCameraDirector);
// 进入/退出过渡
TArrayView<const TObjectPtr<UCameraRigTransition>> GetEnterTransitions() const;
void AddEnterTransition(UCameraRigTransition* InTransition);
TArrayView<const TObjectPtr<UCameraRigTransition>> GetExitTransitions() const;
void AddExitTransition(UCameraRigTransition* InTransition);
// 默认参数
const FInstancedPropertyBag& GetDefaultParameters() const { return DefaultParameters; }
FInstancedPropertyBag& GetDefaultParameters() { return DefaultParameters; }
// 参数定义
TConstArrayView<FCameraObjectInterfaceParameterDefinition> GetParameterDefinitions() const;
// 构建相机
void BuildCamera();
void BuildCamera(UE::Cameras::FCameraBuildLog& InBuildLog);
private:
// 导演实例
UPROPERTY(Instanced)
TObjectPtr<UCameraDirector> CameraDirector;
// 进入过渡列表
UPROPERTY(Instanced)
TArray<TObjectPtr<UCameraRigTransition>> EnterTransitions;
// 退出过渡列表
UPROPERTY(Instanced)
TArray<TObjectPtr<UCameraRigTransition>> ExitTransitions;
// 构建状态
UPROPERTY(Transient)
ECameraBuildStatus BuildStatus = ECameraBuildStatus::Dirty;
// 默认参数值
UPROPERTY()
FInstancedPropertyBag DefaultParameters;
// 参数定义列表
UPROPERTY()
TArray<FCameraObjectInterfaceParameterDefinition> ParameterDefinitions;
};
2.3.3 UCameraRigAsset 相机装备资产
源码位置: Public/Core/CameraRigAsset.h:57
UCameraRigAsset 定义一组可复用的相机行为:
UCLASS(MinimalAPI)
class UCameraRigAsset : public UBaseCameraObject
, public IGameplayTagAssetInterface
, public IHasCameraBuildStatus
, public IObjectTreeGraphObject
, public IObjectTreeGraphRootObject
{
GENERATED_BODY()
public:
// 根节点 - 装备的入口点
UPROPERTY(Instanced)
TObjectPtr<UCameraNode> RootNode;
// Gameplay 标签
UPROPERTY(EditAnywhere, Category=GameplayTags)
FGameplayTagContainer GameplayTags;
// 进入过渡
UPROPERTY(Instanced)
TArray<TObjectPtr<UCameraRigTransition>> EnterTransitions;
// 退出过渡
UPROPERTY(Instanced)
TArray<TObjectPtr<UCameraRigTransition>> ExitTransitions;
// 初始朝向模式
UPROPERTY(EditAnywhere, Category="Transition")
ECameraRigInitialOrientation InitialOrientation = ECameraRigInitialOrientation::None;
// 唯一标识符
const FGuid& GetGuid() const { return Guid; }
// 构建装备
void BuildCameraRig();
void BuildCameraRig(UE::Cameras::FCameraBuildLog& InBuildLog);
private:
UPROPERTY()
FGuid Guid;
};
2.3.4 资产与运行时的关系
┌─────────────────────────────────────────────────────────────────────┐
│ 资产层次 (编辑时) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ UCameraAsset │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ UCameraDirector (SingleCameraDirector) │ │
│ │ └── FCameraRigProxy → UCameraRigAsset │ │
│ │ └── UCameraNode (RootNode) │ │
│ │ └── UArrayCameraNode │ │
│ │ ├── UAttachToPlayerPawnCameraNode │ │
│ │ ├── UBoomArmCameraNode │ │
│ │ └── UCollisionPushCameraNode │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
│
│ Build
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 评估器层次 (运行时) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ FCameraSystemEvaluator │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ FCameraDirectorEvaluator │ │
│ │ └── FRootCameraNodeEvaluator (DefaultRoot) │ │
│ │ ├── FPersistentBlendStackEvaluator (BaseLayer) │ │
│ │ ├── FTransientBlendStackEvaluator (MainLayer) │ │
│ │ │ └── FCameraRigEntry │ │
│ │ │ └── FArrayCameraNodeEvaluator │ │
│ │ │ ├── FAttachToPlayerPawnEvaluator │ │
│ │ │ ├── FBoomArmEvaluator │ │
│ │ │ └── FCollisionPushEvaluator │ │
│ │ ├── FPersistentBlendStackEvaluator (GlobalLayer) │ │
│ │ └── FPersistentBlendStackEvaluator (VisualLayer) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.4 类图与关系说明
2.4.1 完整的核心类关系图
2.4.2 核心类职责表
| 类名 | 类型 | 职责 | 生命周期 |
|---|---|---|---|
UCameraAsset |
UObject | 顶层相机配置容器 | 持久化资产 |
UCameraRigAsset |
UObject | 可复用的相机行为定义 | 持久化资产 |
UCameraNode |
UObject | 节点数据定义 | 持久化资产 |
FCameraSystemEvaluator |
C++ | 管理整个评估流程 | 游戏运行时 |
FCameraEvaluationContext |
C++ | 提供评估的环境和上下文 | 游戏运行时 |
FCameraNodeEvaluator |
C++ | 执行节点评估逻辑 | 游戏运行时 |
FCameraPose |
C++ | 相机姿态数据 | 每帧更新 |
FCameraVariableTable |
C++ | 参数变量存储 | 每帧更新 |
FCameraNodeEvaluationResult |
C++ | 评估结果容器 | 每帧更新 |
3. 求值流程详解
3.1 入口点与调用链
3.1.1 从游戏框架到相机系统
GameplayCameras 通过 UGameplayCameraComponent 与游戏框架集成。整个调用链如下:
APlayerController::GetPlayerViewPoint()
└── APlayerCameraManager::CalcCameraView()
└── UGameplayCameraComponentBase::GetCameraView()
└── FCameraSystemEvaluator::Update()
└── FRootCameraNodeEvaluator::Run()
└── [四层评估]
源码位置: Private/Core/CameraSystemEvaluator.cpp:225
void FCameraSystemEvaluator::Update(const FCameraSystemEvaluationParams& Params)
{
// 委托给内部实现,使用标准评估类型
UpdateImpl(Params.DeltaTime, ECameraNodeEvaluationType::Standard);
}
3.1.2 核心更新实现
源码位置: Private/Core/CameraSystemEvaluator.cpp:230
void FCameraSystemEvaluator::UpdateImpl(float DeltaTime, ECameraNodeEvaluationType EvaluationType)
{
SCOPE_CYCLE_COUNTER(CameraSystemEval_Total); // 性能统计
// Step 1: 重置结果标志
RootNodeResult.ResetFrameFlags();
// Step 2: 重置变量和上下文数据
RootNodeResult.VariableTable.AutoResetValues();
RootNodeResult.ContextDataTable.AutoResetValues();
// Step 3: 预更新所有服务
PreUpdateServices(DeltaTime, ECameraEvaluationServiceFlags::None);
// Step 4: 获取活动评估上下文
TSharedPtr<FCameraEvaluationContext> ActiveContext = ContextStack.GetActiveContext();
if (UNLIKELY(!ActiveContext.IsValid()))
{
Result.bIsValid = false;
PreVisualResult.bIsValid = false;
return;
}
// Step 5: 运行相机导演
FCameraDirectorEvaluator* ActiveDirectorEvaluator = ActiveContext->GetDirectorEvaluator();
if (ActiveDirectorEvaluator)
{
UpdateCameraDirector(DeltaTime, ActiveDirectorEvaluator);
}
// Step 6: 运行节点树评估
{
FCameraNodeEvaluationParams NodeParams;
NodeParams.Evaluator = this;
NodeParams.DeltaTime = DeltaTime;
NodeParams.EvaluationType = EvaluationType;
RootNodeResult.Reset();
RootEvaluator->Run(NodeParams, RootNodeResult);
RootNodeResult.bIsValid = true;
}
// Step 7: 后更新所有服务
PostUpdateServices(DeltaTime, ECameraEvaluationServiceFlags::None);
// Step 8: 收集最终结果
PreVisualResult.Reset(RootEvaluator->GetPreVisualLayerResult());
Result.Reset(RootNodeResult);
// Step 9: 生成调试信息(如果启用)
#if UE_GAMEPLAY_CAMERAS_DEBUG
BuildDebugBlocksIfNeeded();
#endif
// Step 10: 结束更新
ContextStack.OnEndCameraSystemUpdate();
}
3.2 每帧更新流程
3.2.1 更新流程图
3.2.2 评估类型
源码位置: Public/Core/CameraNodeEvaluator.h:132
enum class ECameraNodeEvaluationType
{
/** 正常评估 */
Standard,
/** 视角旋转预览评估 */
ViewRotationPreview,
/** IK 目标评估 */
IK,
#if WITH_EDITOR
/** 编辑器预览评估 */
EditorPreview
#endif
};
3.2.3 服务更新周期
源码位置: Private/Core/CameraSystemEvaluator.cpp:415
void FCameraSystemEvaluator::PreUpdateServices(float DeltaTime, ECameraEvaluationServiceFlags ExtraFlags)
{
FCameraEvaluationServiceUpdateParams ServiceUpdateParams;
ServiceUpdateParams.Evaluator = this;
ServiceUpdateParams.DeltaTime = DeltaTime;
FCameraEvaluationServiceUpdateResult ServiceUpdateResult(RootNodeResult);
for (TSharedPtr<FCameraEvaluationService> EvaluationService : EvaluationServices)
{
if (EvaluationService->HasAllEvaluationServiceFlags(
ECameraEvaluationServiceFlags::NeedsPreUpdate | ExtraFlags))
{
EvaluationService->PreUpdate(ServiceUpdateParams, ServiceUpdateResult);
}
}
}
3.3 混合栈求值过程
3.3.1 四层评估流程
源码位置: Private/Core/DefaultRootCameraNode.cpp:77
void FDefaultRootCameraNodeEvaluator::OnRun(
const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
// 1. Base Layer - 基础层(持久化、叠加)
BaseLayer->Run(Params, OutResult);
// 2. Main Layer - 主层(瞬态、混合)
MainLayer->Run(Params, OutResult);
// 3. Global Layer - 全局层(持久化、叠加)
GlobalLayer->Run(Params, OutResult);
// 保存 Pre-Visual 结果(用于某些特殊用途)
SetPreVisualLayerResult(OutResult);
// 4. Visual Layer - 视觉层(后处理效果)
// 注意:IK 和视角预览评估不运行视觉层
if (Params.EvaluationType != ECameraNodeEvaluationType::IK &&
Params.EvaluationType != ECameraNodeEvaluationType::ViewRotationPreview)
{
VisualLayer->Run(Params, OutResult);
}
}
3.3.2 瞬态混合栈求值 (Main Layer)
源码位置: Private/Core/TransientBlendStackCameraNode.cpp:209
void FTransientBlendStackCameraNodeEvaluator::OnRun(
const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
ensure(Entries.Num() == EntryExtraInfos.Num());
// Step 1: 验证条目并解析评估上下文
TArray<FResolvedEntry> ResolvedEntries;
ResolveEntries(ResolvedEntries);
// Step 2: 预混合准备 - 收集参数
InternalPreBlendPrepare(ResolvedEntries, Params, OutResult);
// Step 3: 预混合执行 - 混合输入变量
InternalPreBlendExecute(ResolvedEntries, Params, OutResult);
// Step 4: 更新 - 运行每个条目的根节点
InternalUpdate(ResolvedEntries, Params, OutResult);
// Step 5: 后混合执行 - 混合结果并弹出已完成条目
InternalPostBlendExecute(ResolvedEntries, Params, OutResult);
// Step 6: 清理
OnRunFinished(OutResult);
InternalRunFinished(OutResult);
}
3.3.3 混合栈求值详解
Step 1: 预混合准备 (InternalPreBlendPrepare)
void FTransientBlendStackCameraNodeEvaluator::InternalPreBlendPrepare(
TArrayView<FResolvedEntry> ResolvedEntries,
const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
for (FResolvedEntry& ResolvedEntry : ResolvedEntries)
{
FCameraRigEntry& Entry = ResolvedEntry.Entry;
if (UNLIKELY(Entry.Flags.bIsFrozen))
continue; // 冻结条目跳过
// 复制输入变量到条目结果
CurResult.VariableTable.OverrideAll(OutResult.VariableTable);
// 应用上下文覆盖
CurResult.VariableTable.OverrideAll(ContextResult.VariableTable, true);
// 应用参数设置器服务
if (ParameterSetterService)
{
ParameterSetterService->ApplyCameraVariableSetters(CurResult.VariableTable);
}
// 收集需要参数更新的节点
if (!EntryExtraInfo.bInputRunThisFrame)
{
FCameraBlendedParameterUpdateParams InputParams(CurParams, CurResult.CameraPose);
FCameraBlendedParameterUpdateResult InputResult(CurResult.VariableTable);
Entry.EvaluatorHierarchy.ForEachEvaluator(
ECameraNodeEvaluatorFlags::NeedsParameterUpdate,
[&InputParams, &InputResult](FCameraNodeEvaluator* ParameterEvaluator)
{
ParameterEvaluator->UpdateParameters(InputParams, InputResult);
});
}
// 运行混合节点的 Run
FBlendCameraNodeEvaluator* EntryBlendEvaluator = Entry.RootEvaluator->GetBlendEvaluator();
if (EntryBlendEvaluator)
{
EntryBlendEvaluator->Run(CurParams, CurResult);
}
}
}
Step 2: 预混合执行 (InternalPreBlendExecute)
void FTransientBlendStackCameraNodeEvaluator::InternalPreBlendExecute(
TArrayView<FResolvedEntry> ResolvedEntries,
const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
PreBlendVariableTable.ClearAllWrittenThisFrameFlags();
// 混合所有条目的输入变量
constexpr ECameraVariableTableFilter VariableTableFilter = ECameraVariableTableFilter::InputOnly;
for (FResolvedEntry& ResolvedEntry : ResolvedEntries)
{
FCameraRigEntry& Entry = ResolvedEntry.Entry;
if (!Entry.Flags.bIsFrozen)
{
// 调用混合节点的 BlendParameters
FBlendCameraNodeEvaluator* EntryBlendEvaluator = Entry.RootEvaluator->GetBlendEvaluator();
if (EntryBlendEvaluator)
{
FCameraNodePreBlendParams PreBlendParams(CurParams, CurResult.CameraPose, CurResult.VariableTable);
FCameraNodePreBlendResult PreBlendResult(PreBlendVariableTable);
EntryBlendEvaluator->BlendParameters(PreBlendParams, PreBlendResult);
}
}
else
{
// 冻结条目使用最后评估的值
PreBlendVariableTable.Override(CurResult.VariableTable, VariableTableFilter);
}
}
// 将预混合值写回每个条目
for (FResolvedEntry& ResolvedEntry : ResolvedEntries)
{
FCameraRigEntry& Entry = ResolvedEntry.Entry;
if (!Entry.Flags.bIsFrozen)
{
CurResult.VariableTable.Override(PreBlendVariableTable, ECameraVariableTableFilter::KnownOnly);
}
}
}
Step 3: 更新 (InternalUpdate)
void FTransientBlendStackCameraNodeEvaluator::InternalUpdate(
TArrayView<FResolvedEntry> ResolvedEntries,
const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
for (FResolvedEntry& ResolvedEntry : ResolvedEntries)
{
FCameraRigEntry& Entry = ResolvedEntry.Entry;
if (UNLIKELY(Entry.Flags.bIsFrozen))
continue; // 冻结条目跳过
FCameraNodeEvaluationResult& CurResult = Entry.Result;
// 重置结果并设置初始状态
CurResult.Reset();
CurResult.CameraPose = OutResult.CameraPose;
CurResult.bIsValid = true;
// 运行相机装备的根节点
FCameraNodeEvaluator* RootEvaluator = Entry.RootEvaluator->GetRootEvaluator();
if (RootEvaluator)
{
RootEvaluator->Run(CurParams, CurResult);
}
}
}
Step 4: 后混合执行 (InternalPostBlendExecute)
void FTransientBlendStackCameraNodeEvaluator::InternalPostBlendExecute(
TArrayView<FResolvedEntry> ResolvedEntries,
const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
int32 PopEntriesBelow = INDEX_NONE;
for (FResolvedEntry& ResolvedEntry : ResolvedEntries)
{
FCameraRigEntry& Entry = ResolvedEntry.Entry;
FCameraNodeEvaluationResult& CurResult = Entry.Result;
if (!Entry.Flags.bIsFrozen)
{
// 调用混合节点的 BlendResults
FBlendCameraNodeEvaluator* EntryBlendEvaluator = Entry.RootEvaluator->GetBlendEvaluator();
if (EntryBlendEvaluator)
{
FCameraNodeBlendParams BlendParams(CurParams, CurResult);
FCameraNodeBlendResult BlendResult(OutResult);
EntryBlendEvaluator->BlendResults(BlendParams, BlendResult);
// 如果混合完成且达到100%,标记弹出下方条目
if (BlendResult.bIsBlendFull && BlendResult.bIsBlendFinished)
{
PopEntriesBelow = ResolvedEntry.EntryIndex;
}
}
else
{
OutResult.OverrideAll(CurResult);
PopEntriesBelow = ResolvedEntry.EntryIndex;
}
}
else
{
// 冻结条目直接覆盖结果
OutResult.OverrideAll(CurResult);
PopEntriesBelow = ResolvedEntry.EntryIndex;
}
}
// 弹出已完全混合的条目(仅限 IsolatedTransient 模式)
if (!Params.IsStatelessEvaluation())
{
const UBlendStackCameraNode* BlendStackNode = GetCameraNodeAs<UBlendStackCameraNode>();
if (BlendStackNode->BlendStackType == ECameraBlendStackType::IsolatedTransient &&
PopEntriesBelow != INDEX_NONE)
{
PopEntries(PopEntriesBelow);
EntryExtraInfos.RemoveAt(0, PopEntriesBelow);
}
}
}
3.4 参数传递与结果收集
3.4.1 评估参数结构
// 源码位置: Public/Core/CameraNodeEvaluator.h:150
struct FCameraNodeEvaluationParams
{
// 运行此评估的评估器
FCameraSystemEvaluator* Evaluator = nullptr;
// 负责此评估分支的评估上下文
TSharedPtr<const FCameraEvaluationContext> EvaluationContext;
// 评估的时间间隔
float DeltaTime = 0.f;
// 评估类型
ECameraNodeEvaluationType EvaluationType = ECameraNodeEvaluationType::Standard;
// 是否是此节点层次结构的第一帧评估
bool bIsFirstFrame = false;
// 是否是无状态评估(IK或视角预览)
bool IsStatelessEvaluation() const
{
return EvaluationType == ECameraNodeEvaluationType::IK ||
EvaluationType == ECameraNodeEvaluationType::ViewRotationPreview;
}
};
3.4.2 评估结果结构
// 源码位置: Public/Core/CameraNodeEvaluator.h:173
struct FCameraNodeEvaluationResult
{
// 相机姿态
FCameraPose CameraPose;
// 变量表
FCameraVariableTable VariableTable;
// 上下文数据表
FCameraContextDataTable ContextDataTable;
// 相机装备关节列表
FCameraRigJoints CameraRigJoints;
// 后处理设置
FPostProcessSettingsCollection PostProcessSettings;
// 当前帧是否是相机切镜
bool bIsCameraCut = false;
// 结果是否有效
bool bIsValid = false;
public:
// 重置为默认(无效)状态
void Reset();
// 重置本帧写入标志
void ResetFrameFlags();
// 用另一个结果覆盖
void OverrideAll(const FCameraNodeEvaluationResult& OtherResult, bool bIncludePrivateValues = false);
// 向另一个结果插值
void LerpAll(const FCameraNodeEvaluationResult& ToResult, float BlendFactor, bool bIncludePrivateValues = false);
// 序列化
void Serialize(FArchive& Ar);
};
3.4.3 数据流向图
3.4.4 结果混合方式
// 完全覆盖
void FCameraNodeEvaluationResult::OverrideAll(const FCameraNodeEvaluationResult& OtherResult, bool bIncludePrivateValues)
{
CameraPose.OverrideAll(OtherResult.CameraPose);
VariableTable.OverrideAll(OtherResult.VariableTable, bIncludePrivateValues);
ContextDataTable.OverrideAll(OtherResult.ContextDataTable);
PostProcessSettings.OverrideAll(OtherResult.PostProcessSettings);
bIsCameraCut = OtherResult.bIsCameraCut;
}
// 插值混合
void FCameraNodeEvaluationResult::LerpAll(const FCameraNodeEvaluationResult& ToResult, float BlendFactor, bool bIncludePrivateValues)
{
CameraPose.LerpAll(ToResult.CameraPose, BlendFactor);
VariableTable.LerpAll(ToResult.VariableTable, BlendFactor, bIncludePrivateValues);
// ContextDataTable 不插值
PostProcessSettings.LerpAll(ToResult.PostProcessSettings, BlendFactor);
bIsCameraCut = ToResult.bIsCameraCut;
}
3.5 流程图与时序图
3.5.1 完整评估时序图
3.5.2 混合栈详细流程
3.5.3 关键帧时序
帧 N:
┌─────────────────────────────────────────────────────────────────────┐
│ PreUpdate Services │
├─────────────────────────────────────────────────────────────────────┤
│ Director: 激活 CameraRig_B │
├─────────────────────────────────────────────────────────────────────┤
│ Main BlendStack: │
│ [CameraRig_A] ← 正在混合出去 (blend=0.3) │
│ [CameraRig_B] ← 新激活,正在混合进来 (blend=0.7) │
├─────────────────────────────────────────────────────────────────────┤
│ PostUpdate Services │
└─────────────────────────────────────────────────────────────────────┘
帧 N+1:
┌─────────────────────────────────────────────────────────────────────┐
│ PreUpdate Services │
├─────────────────────────────────────────────────────────────────────┤
│ Director: 无新请求 │
├─────────────────────────────────────────────────────────────────────┤
│ Main BlendStack: │
│ [CameraRig_A] ← 混合进度继续 (blend=0.1) │
│ [CameraRig_B] ← 混合进度继续 (blend=0.9) │
├─────────────────────────────────────────────────────────────────────┤
│ PostUpdate Services │
└─────────────────────────────────────────────────────────────────────┘
帧 N+2:
┌─────────────────────────────────────────────────────────────────────┐
│ PreUpdate Services │
├─────────────────────────────────────────────────────────────────────┤
│ Director: 无新请求 │
├─────────────────────────────────────────────────────────────────────┤
│ Main BlendStack: │
│ [CameraRig_B] ← 混合完成 (blend=1.0),CameraRig_A 被弹出 │
├─────────────────────────────────────────────────────────────────────┤
│ PostUpdate Services │
└─────────────────────────────────────────────────────────────────────┘
4. 关键模块详解
4.1 节点系统 (Nodes)
节点系统是 GameplayCameras 的核心,通过组合各种节点来构建复杂的相机行为。
4.1.1 节点组合模式
节点采用组合模式(Composite Pattern),允许将多个节点组合成树形结构:
UCameraRigAsset
└── RootNode: UArrayCameraNode (Sequence)
├── [0] UAttachToPlayerPawnCameraNode # 附加到玩家
├── [1] UBoomArmCameraNode # 吊臂控制
│ └── InputSlot: UInput2DCameraNode # 输入处理
├── [2] UCollisionPushCameraNode # 碰撞检测
└── [3] UDampenRotationCameraNode # 旋转阻尼
4.1.2 节点输入/输出槽
节点通过槽(Slot)机制连接,支持数据流和控制流:
// 输入槽示例 - BoomArmCameraNode.h:59
// ObjectTreeGraphPinDirection=Input 表示这是一个输入槽
UPROPERTY(meta=(ObjectTreeGraphPinDirection=Input))
TObjectPtr<UInput2DCameraNode> InputSlot;
4.1.3 节点评估流程
每个节点的评估器实现 OnRun 方法:
// 典型的节点评估实现
void FBoomArmCameraNodeEvaluator::OnRun(
const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
const UBoomArmCameraNode* Data = GetCameraNodeAs<UBoomArmCameraNode>();
// 1. 从变量表获取参数值
const FVector BoomOffset = Data->BoomOffset.GetValue(OutResult.VariableTable);
// 2. 获取输入(如果有子节点)
if (InputSlotEvaluator)
{
InputSlotEvaluator->Run(Params, OutResult);
}
// 3. 应用节点逻辑
FVector NewLocation = OutResult.CameraPose.GetLocation() + BoomOffset;
OutResult.CameraPose.SetLocation(NewLocation);
}
4.1.4 Sequence 节点详解
源码位置: Public/Nodes/Common/ArrayCameraNode.h
Sequence 节点是最常用的组合节点,按顺序执行子节点:
UCLASS(MinimalAPI, meta=(DisplayName="Sequence", CameraNodeCategories="Common,Utility"))
class UArrayCameraNode : public UCameraNode
{
GENERATED_BODY()
public:
// 子节点数组 - 按顺序执行
UPROPERTY()
TArray<TObjectPtr<UCameraNode>> Children;
};
评估器实现(简化版):
void FArrayCameraNodeEvaluator::OnRun(
const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
// 按顺序执行每个子节点
for (FCameraNodeEvaluator* ChildEvaluator : ChildEvaluators)
{
if (ChildEvaluator)
{
ChildEvaluator->Run(Params, OutResult);
}
}
}
4.2 混合系统 (Blends)
混合系统负责在多个相机装备之间平滑过渡。
4.2.1 混合节点基类
源码位置: Public/Core/BlendCameraNode.h:116
class FBlendCameraNodeEvaluator : public FCameraNodeEvaluator
{
public:
// 混合参数
void BlendParameters(const FCameraNodePreBlendParams& Params, FCameraNodePreBlendResult& OutResult);
// 混合结果
void BlendResults(const FCameraNodeBlendParams& Params, FCameraNodeBlendResult& OutResult);
// 从中断的混合初始化
bool InitializeFromInterruption(const FCameraNodeBlendInterruptionParams& Params);
// 反向混合
bool SetReversed(bool bInReverse);
protected:
virtual void OnBlendParameters(const FCameraNodePreBlendParams& Params, FCameraNodePreBlendResult& OutResult) {}
virtual void OnBlendResults(const FCameraNodeBlendParams& Params, FCameraNodeBlendResult& OutResult) {}
};
4.2.2 内置混合类型
| 混合节点 | 说明 | 源码位置 |
|---|---|---|
ULinearBlendCameraNode |
线性插值 | Nodes/Blends/LinearBlendCameraNode.h |
USmoothBlendCameraNode |
平滑步进插值 | Nodes/Blends/SmoothBlendCameraNode.h |
UOrbitBlendCameraNode |
轨道旋转混合 | Nodes/Blends/OrbitBlendCameraNode.h |
UPopBlendCameraNode |
立即切换(无混合) | Nodes/Blends/PopBlendCameraNode.h |
ULocationRotationBlendCameraNode |
位置/旋转分离混合 | Nodes/Blends/LocationRotationBlendCameraNode.h |
4.2.3 Smooth Blend 示例
源码位置: Public/Nodes/Blends/SmoothBlendCameraNode.h
UCLASS(MinimalAPI)
class USmoothBlendCameraNode : public USimpleFixedTimeBlendCameraNode
{
GENERATED_BODY()
public:
// 平滑算法类型
UPROPERTY(EditAnywhere, Category=Blending)
ESmoothCameraBlendType BlendType;
void SetCameraBlendType(ESmoothCameraBlendType BlendTypeIn) { BlendType = BlendTypeIn; }
};
// 平滑算法类型
enum class ESmoothCameraBlendType
{
SmoothStep, // 3t² - 2t³
SmootherStep // 6t⁵ - 15t⁴ + 10t³
};
评估器实现(简化版):
void FSmoothBlendCameraNodeEvaluator::OnBlendResults(
const FCameraNodeBlendParams& Params,
FCameraNodeBlendResult& OutResult)
{
const USmoothBlendCameraNode* Data = GetCameraNodeAs<USmoothBlendCameraNode>();
// 计算进度
float Progress = FMath::Clamp(ElapsedTime / TotalDuration, 0.f, 1.f);
// 应用平滑函数
float BlendFactor;
switch (Data->BlendType)
{
case ESmoothCameraBlendType::SmootherStep:
BlendFactor = 6.f * FMath::Pow(Progress, 5) -
15.f * FMath::Pow(Progress, 4) +
10.f * FMath::Pow(Progress, 3);
break;
case ESmoothCameraBlendType::SmoothStep:
default:
BlendFactor = Progress * Progress * (3.f - 2.f * Progress);
break;
}
// 执行插值
OutResult.BlendedResult.LerpAll(Params.ChildResult, BlendFactor);
// 更新状态
OutResult.bIsBlendFull = (Progress >= 1.f);
OutResult.bIsBlendFinished = (Progress >= 1.f);
ElapsedTime += Params.ChildParams.DeltaTime;
}
4.2.4 过渡系统
源码位置: Public/Core/CameraRigTransition.h
过渡定义了相机装备切换时的行为:
UCLASS(MinimalAPI)
class UCameraRigTransition : public UObject
{
GENERATED_BODY()
public:
// 条件列表 - 必须全部通过才使用此过渡
UPROPERTY(Instanced, meta=(ObjectTreeGraphPinDirection=Input))
TArray<TObjectPtr<UCameraRigTransitionCondition>> Conditions;
// 使用的混合节点
UPROPERTY(Instanced)
TObjectPtr<UBlendCameraNode> Blend;
// 初始朝向设置
UPROPERTY(EditAnywhere, Category="Transition")
ECameraRigInitialOrientation InitialOrientation;
// 是否允许相机装备合并
UPROPERTY(EditAnywhere, Category="Advanced")
bool bAllowCameraRigMerging = false;
};
初始朝向模式:
enum class ECameraRigInitialOrientation
{
None, // 默认朝向
ContextYawPitch, // 使用上下文的初始朝向
PreviousYawPitch, // 继承前一个相机的朝向
PreviousAbsoluteTarget, // 指向前一个相机的目标点
PreviousRelativeTarget // 指向前一个相机的相对目标
};
4.3 层系统 (Layers)
GameplayCameras 采用四层架构来组织相机效果:
4.3.1 层定义
// 源码位置: Public/Core/CameraRigEvaluationInfo.h
enum class ECameraRigLayer
{
None = 0,
// 基础层 - 持久化、叠加
// 适用于:始终运行的基础效果(如跟随、附加)
Base = 1 << 0,
// 主层 - 瞬态、混合
// 适用于:游戏玩法相机(如第三人称、第一人称)
Main = 1 << 1,
// 全局层 - 持久化、叠加
// 适用于:全局效果(如震动、天气)
Global = 1 << 2,
// 视觉层 - 持久化、叠加
// 适用于:后处理效果(如景深、色彩校正)
Visual = 1 << 3
};
4.3.2 层特性对比
| 层 | 混合栈类型 | 自动弹出 | 典型用途 |
|---|---|---|---|
| Base | AdditivePersistent | 否 | 跟随附加、基础变换 |
| Main | IsolatedTransient | 是 | 游戏玩法相机切换 |
| Global | AdditivePersistent | 否 | 全局效果、震动 |
| Visual | AdditivePersistent | 否 | 后处理效果 |
4.3.3 层评估顺序
// 源码位置: Private/Core/DefaultRootCameraNode.cpp:77
void FDefaultRootCameraNodeEvaluator::OnRun(
const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
// 1. Base Layer
BaseLayer->Run(Params, OutResult);
// 2. Main Layer
MainLayer->Run(Params, OutResult);
// 3. Global Layer
GlobalLayer->Run(Params, OutResult);
// 保存 Pre-Visual 结果
SetPreVisualLayerResult(OutResult);
// 4. Visual Layer (跳过 IK 和视角预览)
if (Params.EvaluationType != ECameraNodeEvaluationType::IK &&
Params.EvaluationType != ECameraNodeEvaluationType::ViewRotationPreview)
{
VisualLayer->Run(Params, OutResult);
}
}
4.3.4 层架构图
┌─────────────────────────────────────────────────────────────────────┐
│ 最终相机结果 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Visual Layer (视觉层) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ PostProcess │ │ Filmback │ │ AutoFocus │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ▲ 叠加 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Global Layer (全局层) │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Shake │ │ Modifier │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ▲ 叠加 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Main Layer (主层) - 混合栈 │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ 3rd Person │──│ Blend │──│ 1st Person │ │ │
│ │ │ (混合出) │ │ (100%) │ │ (混合入) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ▲ 叠加 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Base Layer (基础层) │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ AttachToPlayerPawn │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
4.4 服务系统 (Evaluation Services)
服务系统提供横切关注点,在相机评估前后执行额外逻辑。
4.4.1 服务基类
源码位置: Public/Core/CameraEvaluationService.h
class FCameraEvaluationService : public TSharedFromThis<FCameraEvaluationService>
{
public:
// 初始化
void Initialize(const FCameraEvaluationServiceInitializeParams& Params);
// 预更新(在节点树评估前)
void PreUpdate(const FCameraEvaluationServiceUpdateParams& Params,
FCameraEvaluationServiceUpdateResult& OutResult);
// 后更新(在节点树评估后)
void PostUpdate(const FCameraEvaluationServiceUpdateParams& Params,
FCameraEvaluationServiceUpdateResult& OutResult);
// 销毁
void Teardown(const FCameraEvaluationServiceTeardownParams& Params);
// 根节点事件通知
void NotifyRootCameraNodeEvent(const FRootCameraNodeCameraRigEvent& InEvent);
protected:
virtual void OnInitialize(...) {}
virtual void OnPreUpdate(...) {}
virtual void OnPostUpdate(...) {}
virtual void OnTeardown(...) {}
virtual void OnRootCameraNodeEvent(...) {}
};
4.4.2 服务标志
enum class ECameraEvaluationServiceFlags
{
None = 0,
NeedsPreUpdate = 1 << 0, // 需要预更新回调
NeedsPostUpdate = 1 << 1, // 需要后更新回调
NeedsRootCameraNodeEvents = 1 << 2, // 需要根节点事件
Default = NeedsPreUpdate | NeedsPostUpdate | NeedsRootCameraNodeEvents
};
4.4.3 内置服务
| 服务 | 功能 | 源码位置 |
|---|---|---|
FCameraShakeService |
管理相机震动 | Services/CameraShakeService.h |
FCameraModifierService |
应用相机修饰符 | Services/CameraModifierService.h |
FCameraParameterSetterService |
处理参数设置请求 | Services/CameraParameterSetterService.h |
FOrientationInitializationService |
初始化相机朝向 | Services/OrientationInitializationService.h |
4.4.4 服务注册与发现
// 源码位置: Private/Core/CameraSystemEvaluator.cpp:177
void FCameraSystemEvaluator::RegisterEvaluationService(
TSharedRef<FCameraEvaluationService> EvaluationService)
{
EvaluationServices.Add(EvaluationService);
FCameraEvaluationServiceInitializeParams InitParams;
InitParams.Evaluator = this;
EvaluationService->Initialize(InitParams);
}
// 查找服务
template<typename EvaluationServiceType>
TSharedPtr<EvaluationServiceType> FindEvaluationService() const
{
TSharedPtr<FCameraEvaluationService> Service =
FindEvaluationService(EvaluationServiceType::StaticTypeID());
return StaticCastSharedPtr<EvaluationServiceType>(Service);
}
4.5 调试系统 (Debug)
4.5.1 调试宏和开关
// 源码位置: Public/GameplayCameras.h
#ifndef UE_GAMEPLAY_CAMERAS_DEBUG
#if UE_ENABLE_DEBUG_DRAWING
#define UE_GAMEPLAY_CAMERAS_DEBUG 1
#else
#define UE_GAMEPLAY_CAMERAS_DEBUG 0
#endif
#endif
4.5.2 调试块系统
调试信息通过调试块(Debug Block)系统组织:
// 源码位置: Public/Debug/CameraDebugBlock.h
class FCameraDebugBlock
{
public:
virtual void OnDebugDraw(const FCameraDebugBlockDrawParams& Params,
FCameraDebugRenderer& Renderer) {}
virtual void OnSerialize(FArchive& Ar) {}
protected:
TArray<TSharedPtr<FCameraDebugBlock>> Children;
};
4.5.3 调试渲染器
class FCameraDebugRenderer
{
public:
// 绘制线条
void DrawLine(const FVector3d& Start, const FVector3d& End,
const FLinearColor& Color, float Thickness = 1.f);
// 绘制文字
void DrawText(const FString& Text, const FVector3d& Location,
const FLinearColor& Color);
// 绘制坐标轴
void DrawAxes(const FTransform3d& Transform, float Size = 10.f);
};
4.6 导演系统 (Directors)
导演系统控制相机装备的激活和停用。
4.6.1 导演基类
源码位置: Public/Core/CameraDirector.h
UCLASS(MinimalAPI, Abstract, DefaultToInstanced)
class UCameraDirector : public UObject
{
GENERATED_BODY()
public:
// 构建评估器
FCameraDirectorEvaluatorPtr BuildEvaluator(FCameraDirectorEvaluatorBuilder& Builder) const;
// 构建验证
void BuildCameraDirector(UE::Cameras::FCameraBuildLog& BuildLog);
// 收集使用的相机装备
void GatherRigUsageInfo(FCameraDirectorRigUsageInfo& UsageInfo) const;
protected:
virtual FCameraDirectorEvaluatorPtr OnBuildEvaluator(FCameraDirectorEvaluatorBuilder& Builder) const
{
return nullptr;
}
virtual void OnBuildCameraDirector(UE::Cameras::FCameraBuildLog& BuildLog) {}
virtual void OnGatherRigUsageInfo(FCameraDirectorRigUsageInfo& UsageInfo) const {}
public:
// 相机装备代理重定向表
UPROPERTY(EditAnywhere, Category="Evaluation")
FCameraRigProxyRedirectTable CameraRigProxyRedirectTable;
};
4.6.2 内置导演类型
| 导演 | 说明 | 源码位置 |
|---|---|---|
USingleCameraDirector |
返回单个固定相机装备 | Directors/SingleCameraDirector.h |
UBlueprintCameraDirector |
蓝图实现的导演 | Directors/BlueprintCameraDirector.h |
UPriorityQueueCameraDirector |
基于优先队列的导演 | Directors/PriorityQueueCameraDirector.h |
UStateTreeCameraDirector |
基于状态树的导演 | Directors/StateTreeCameraDirector.h |
4.6.3 SingleCameraDirector 示例
源码位置: Public/Directors/SingleCameraDirector.h
UCLASS(MinimalAPI, EditInlineNew)
class USingleCameraDirector : public UCameraDirector
{
GENERATED_BODY()
public:
USingleCameraDirector(const FObjectInitializer& ObjectInit);
protected:
virtual FCameraDirectorEvaluatorPtr OnBuildEvaluator(
FCameraDirectorEvaluatorBuilder& Builder) const override;
virtual void OnBuildCameraDirector(UE::Cameras::FCameraBuildLog& BuildLog) override;
virtual void OnGatherRigUsageInfo(FCameraDirectorRigUsageInfo& UsageInfo) const override;
public:
// 每帧运行的相机装备
UPROPERTY(EditAnywhere, Category=Common, meta=(UseSelfCameraRigPicker=true))
TObjectPtr<UCameraRigAsset> CameraRig;
};
4.6.4 导演评估流程
4.6.5 相机装备代理系统
导演可以使用代理(Proxy)来间接引用相机装备:
// 代理重定向表
UPROPERTY(EditAnywhere, Category="Evaluation")
FCameraRigProxyRedirectTable CameraRigProxyRedirectTable;
// 使用示例
// 在蓝图中:设置 CameraRigProxy = "CombatCamera"
// 在数据表中:将 "CombatCamera" 映射到实际的 UCameraRigAsset
这种设计允许:
- 在蓝图/状态树中使用逻辑名称
- 运行时通过数据表映射到实际资产
- 支持不同场景使用不同的相机装备实现
5. API 设计说明
5.1 公共 API
5.1.1 主要公共头文件
| 头文件 | 主要类/结构 | 用途 |
|---|---|---|
CameraSystemEvaluator.h |
FCameraSystemEvaluator |
相机系统入口 |
CameraPose.h |
FCameraPose |
相机姿态数据 |
CameraNode.h |
UCameraNode |
节点基类 |
CameraNodeEvaluator.h |
FCameraNodeEvaluator |
评估器基类 |
CameraRigAsset.h |
UCameraRigAsset |
相机装备资产 |
CameraAsset.h |
UCameraAsset |
相机资产 |
CameraVariableTable.h |
FCameraVariableTable |
变量表 |
CameraParameters.h |
F*CameraParameter |
参数类型 |
GameplayCameraComponent.h |
UGameplayCameraComponent |
组件集成 |
5.1.2 命名空间
所有核心类型都在 UE::Cameras 命名空间中:
namespace UE::Cameras
{
class FCameraSystemEvaluator;
class FCameraNodeEvaluator;
class FCameraEvaluationContext;
class FCameraEvaluationService;
// ...
}
5.2 核心数据结构
5.2.1 FCameraPose (相机姿态)
源码位置: Public/Core/CameraPose.h
FCameraPose 是描述相机完整状态的核心结构:
USTRUCT()
struct GAMEPLAYCAMERAS_API FCameraPose
{
GENERATED_BODY()
public:
// 变换属性
FVector3d Location; // 世界空间位置
FRotator3d Rotation; // 世界空间旋转
double TargetDistance; // 到目标的距离
// 视场属性
float FieldOfView; // 水平视场角(度)
float FocalLength; // 焦距(毫米)
float OrthographicWidth; // 正交宽度
// 物理相机属性
float Aperture; // 光圈(f-stops)
float ShutterSpeed; // 快门速度(1/秒)
float FocusDistance; // 对焦距离
float SensorWidth; // 传感器宽度(毫米)
float SensorHeight; // 传感器高度(毫米)
float ISO; // ISO 感光度
float SqueezeFactor; // 变形镜头压缩因子
float Overscan; // 过扫描百分比
int32 DiaphragmBladeCount; // 光圈叶片数
// 裁剪平面
float NearClippingPlane; // 近裁剪距离
float FarClippingPlane; // 远裁剪距离
// 标志
bool EnablePhysicalCamera; // 启用物理相机
bool ConstrainAspectRatio; // 约束宽高比
ECameraProjectionMode::Type ProjectionMode; // 投影模式
// 变更追踪
FCameraPoseFlags ChangedFlags;
public:
// 工具方法
FTransform3d GetTransform() const;
void SetTransform(FTransform3d Transform, bool bForceSet = false);
double GetEffectiveFieldOfView(bool bIncludeOverscan = true) const;
double GetSensorAspectRatio() const;
FRay3d GetAimRay() const;
FVector3d GetTarget() const;
// 插值方法
void OverrideAll(const FCameraPose& OtherPose);
void OverrideChanged(const FCameraPose& OtherPose);
void LerpAll(const FCameraPose& ToPose, float Factor);
void LerpChanged(const FCameraPose& ToPose, float Factor);
};
使用示例:
// 获取当前相机位置
FVector3d CurrentLocation = CameraPose.GetLocation();
// 设置新的相机位置(自动设置 ChangedFlags)
CameraPose.SetLocation(NewLocation);
// 插值到目标姿态
FromPose.LerpAll(ToPose, 0.5f); // 50% 插值
// 获取相机瞄准射线
FRay3d AimRay = CameraPose.GetAimRay();
// 计算有效视场角(考虑焦距或视场角设置)
double EffectiveFOV = CameraPose.GetEffectiveFieldOfView();
5.2.2 FCameraVariableTable (变量表)
源码位置: Public/Core/CameraVariableTable.h
FCameraVariableTable 是可混合的参数存储容器:
class FCameraVariableTable
{
public:
// 初始化
void Initialize(const FCameraVariableTableAllocationInfo& AllocationInfo);
void AddVariable(const FCameraVariableDefinition& VariableDefinition);
// Getter 方法
template<typename ValueType>
const ValueType* FindValue(FCameraVariableID VariableID) const;
template<typename ValueType>
const ValueType& GetValue(FCameraVariableID VariableID) const;
template<typename ValueType>
ValueType GetValue(FCameraVariableID VariableID, typename TCallTraits<ValueType>::ParamType DefaultValue) const;
template<typename VariableAssetType>
typename VariableAssetType::ValueType GetValue(const VariableAssetType* VariableAsset) const;
// Setter 方法
template<typename ValueType>
void SetValue(FCameraVariableID VariableID, typename TCallTraits<ValueType>::ParamType Value);
template<typename VariableAssetType>
void SetValue(const VariableAssetType* VariableAsset,
typename TCallTraits<typename VariableAssetType::ValueType>::ParamType Value,
bool bCreateIfMissing = false);
// 插值方法
void OverrideAll(const FCameraVariableTable& OtherTable, bool bIncludePrivateValues = false);
void Override(const FCameraVariableTable& OtherTable, ECameraVariableTableFilter Filter);
void LerpAll(const FCameraVariableTable& ToTable, float Factor, bool bIncludePrivateValues = false);
void Lerp(const FCameraVariableTable& ToTable, ECameraVariableTableFilter Filter, float Factor);
// 状态管理
bool IsValueWritten(FCameraVariableID VariableID) const;
bool IsValueWrittenThisFrame(FCameraVariableID VariableID) const;
void ClearAllWrittenThisFrameFlags();
void AutoResetValues();
};
过滤器类型:
enum class ECameraVariableTableFilter
{
None = 0,
PublicOnly = 1 << 0, // 只包含公共变量
InputOnly = 1 << 1, // 只包含输入变量
KnownOnly = 1 << 2, // 只包含两个表共有的数据
ChangedOnly = 1 << 3, // 只包含本帧写入的变量
};
使用示例:
// 设置变量值
VariableTable.SetValue<float>(ZoomVariableID, 2.0f);
// 通过变量资产设置
VariableTable.SetValue(ZoomVariableAsset, 2.0f);
// 获取变量值(带默认值)
float Zoom = VariableTable.GetValue<float>(ZoomVariableID, 1.0f);
// 通过变量资产获取
float Zoom = VariableTable.GetValue(ZoomVariableAsset);
// 混合两个变量表
FromTable.LerpAll(ToTable, BlendFactor);
// 只混合输入变量
FromTable.Lerp(ToTable, ECameraVariableTableFilter::InputOnly, BlendFactor);
5.2.3 FCameraContextDataTable (上下文数据表)
源码位置: Public/Core/CameraContextDataTable.h
FCameraContextDataTable 是不可混合的上下文数据存储:
class FCameraContextDataTable
{
public:
// 初始化
void Initialize(const FCameraContextDataTableAllocationInfo& AllocationInfo);
void AddData(const FCameraContextDataDefinition& DataDefinition);
// 类型化 Getter
const FName& GetNameData(FCameraContextDataID InID) const;
const FString& GetStringData(FCameraContextDataID InID) const;
UObject* GetObjectData(FCameraContextDataID InID) const;
UClass* GetClassData(FCameraContextDataID InID) const;
template<typename EnumType>
EnumType GetEnumData(FCameraContextDataID InID) const;
template<typename StructType>
const StructType& GetStructData(FCameraContextDataID InID) const;
// 类型化 Setter
void SetNameData(FCameraContextDataID InID, const FName& InData);
void SetStringData(FCameraContextDataID InID, const FString& InData);
void SetObjectData(FCameraContextDataID InID, UObject* InData);
template<typename EnumType>
void SetEnumData(FCameraContextDataID InID, EnumType InData);
template<typename StructType>
void SetStructData(FCameraContextDataID InID, const StructType& InData);
// 数组支持
template<typename ValueType>
TConstArrayView<ValueType> TryGetArrayData(FCameraContextDataID InID) const;
template<typename StructType>
void SetStructArrayData(FCameraContextDataID InID, TConstArrayView<StructType> InData);
// 覆盖方法(无插值)
void OverrideAll(const FCameraContextDataTable& OtherTable);
void OverrideKnown(const FCameraContextDataTable& OtherTable);
};
使用示例:
// 存储目标 Actor
ContextDataTable.SetObjectData(TargetActorID, MyTargetActor);
// 获取目标 Actor
AActor* Target = ContextDataTable.GetObjectData<AActor>(TargetActorID);
// 存储自定义结构体
FMyCameraSettings Settings;
Settings.bEnableFeature = true;
Settings.Intensity = 0.5f;
ContextDataTable.SetStructData(SettingsID, Settings);
// 读取自定义结构体
const FMyCameraSettings& Settings = ContextDataTable.GetStructData<FMyCameraSettings>(SettingsID);
// 存储枚举
ContextDataTable.SetEnumData<EMyCameraMode>(ModeID, EMyCameraMode::Combat);
// 存储数组
TArray<FVector> Waypoints;
ContextDataTable.SetStructArrayData(WaypointsID, Waypoints);
5.2.4 FCameraNodeEvaluationResult (评估结果)
源码位置: Public/Core/CameraNodeEvaluator.h:173
struct GAMEPLAYCAMERAS_API FCameraNodeEvaluationResult
{
// 相机姿态
FCameraPose CameraPose;
// 变量表(可混合)
FCameraVariableTable VariableTable;
// 上下文数据表(不可混合)
FCameraContextDataTable ContextDataTable;
// 相机装备关节
FCameraRigJoints CameraRigJoints;
// 后处理设置
FPostProcessSettingsCollection PostProcessSettings;
// 状态标志
bool bIsCameraCut = false; // 是否是相机切镜
bool bIsValid = false; // 结果是否有效
public:
// 重置方法
void Reset();
void ResetFrameFlags();
// 组合方法
void OverrideAll(const FCameraNodeEvaluationResult& OtherResult, bool bIncludePrivateValues = false);
void LerpAll(const FCameraNodeEvaluationResult& ToResult, float BlendFactor, bool bIncludePrivateValues = false);
// 序列化
void Serialize(FArchive& Ar);
// GC 支持
void AddReferencedObjects(FReferenceCollector& Collector);
};
数据流向:
FCameraNodeEvaluationResult
├── CameraPose (姿态)
│ ├── Location
│ ├── Rotation
│ ├── FieldOfView
│ └── ...
├── VariableTable (变量表 - 可混合)
│ ├── InputYaw
│ ├── InputPitch
│ └── CustomParameters...
├── ContextDataTable (上下文数据 - 不可混合)
│ ├── TargetActor
│ └── CustomStructs...
├── CameraRigJoints (关节)
├── PostProcessSettings (后处理)
└── Flags (bIsCameraCut, bIsValid)
5.3 参数系统 (Camera Parameters)
5.3.1 参数类型
源码位置: Public/Core/CameraParameters.h
GameplayCameras 提供了类型化的参数系统,支持从固定值或变量资产获取值:
// 基础类型参数
USTRUCT(BlueprintType)
struct FBooleanCameraParameter { ... };
USTRUCT(BlueprintType)
struct FInteger32CameraParameter { ... };
USTRUCT(BlueprintType)
struct FFloatCameraParameter { ... };
USTRUCT(BlueprintType)
struct FDoubleCameraParameter { ... };
// 向量类型参数
USTRUCT(BlueprintType)
struct FVector2fCameraParameter { ... };
USTRUCT(BlueprintType)
struct FVector3dCameraParameter { ... };
USTRUCT(BlueprintType)
struct FVector4dCameraParameter { ... };
// 旋转类型参数
USTRUCT(BlueprintType)
struct FRotator3dCameraParameter { ... };
// 变换类型参数
USTRUCT(BlueprintType)
struct FTransform3dCameraParameter { ... };
5.3.2 参数结构
每个参数包含三个关键属性:
USTRUCT(BlueprintType)
struct FFloatCameraParameter
{
GENERATED_BODY()
using ValueType = float;
using VariableAssetType = UFloatCameraVariable;
// 用户可调的默认值
UPROPERTY(EditAnywhere, Interp, Category=Common)
float Value = 0.f;
// 变量 ID(运行时设置)
UPROPERTY()
FCameraVariableID VariableID;
// 用户选择的变量资产
UPROPERTY(EditAnywhere, Category=Common)
TObjectPtr<UFloatCameraVariable> Variable;
// 获取值的方法
ValueType GetValue(const FCameraVariableTable& VariableTable) const;
// 检查是否有覆盖
bool HasOverride() const { return VariableID.IsValid(); }
bool HasUserOverride() const { return Variable != nullptr; }
};
5.3.3 参数使用模式
模式 1:固定值
// 在节点定义中
UPROPERTY(EditAnywhere, Category=Common)
FFloatCameraParameter Duration;
// 设置默认值(编辑器或代码)
Duration.Value = 1.0f;
// 在评估器中获取值
float DurationValue = Duration.GetValue(OutResult.VariableTable); // 返回 1.0f
模式 2:变量资产覆盖
// 创建变量资产
UFloatCameraVariable* SpeedVariable = NewObject<UFloatCameraVariable>();
SpeedVariable->Value = 5.0f;
// 在节点中设置变量引用
SpeedParameter.Variable = SpeedVariable;
// 在评估器中获取值
float Speed = SpeedParameter.GetValue(OutResult.VariableTable); // 返回 5.0f
模式 3:运行时变量覆盖
// 游戏代码中动态设置变量
VariableTable.SetValue(SpeedVariableID, 10.0f);
// 评估时自动使用覆盖值
float Speed = SpeedParameter.GetValue(VariableTable); // 返回 10.0f
5.3.4 实际案例:BoomArm 节点参数
// 节点定义
UCLASS()
class UBoomArmCameraNode : public UCameraNode
{
GENERATED_BODY()
public:
// 吊臂偏移 - 使用向量参数
UPROPERTY(EditAnywhere, Category=Common)
FVector3dCameraParameter BoomOffset;
// 最大前向插值因子 - 使用双精度参数
UPROPERTY(EditAnywhere, Category=Common)
FDoubleCameraParameter MaxForwardInterpolationFactor = -1.0;
};
// 评估器实现
void FBoomArmCameraNodeEvaluator::OnRun(
const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
const UBoomArmCameraNode* Data = GetCameraNodeAs<UBoomArmCameraNode>();
// 从参数获取当前值(考虑变量覆盖)
FVector BoomOffset = Data->BoomOffset.GetValue(OutResult.VariableTable);
double MaxForward = Data->MaxForwardInterpolationFactor.GetValue(OutResult.VariableTable);
// 使用值进行计算...
}
5.4 评估上下文 (Evaluation Context)
5.4.1 FCameraEvaluationContext 概述
源码位置: Public/Core/CameraEvaluationContext.h
class FCameraEvaluationContext : public TSharedFromThis<FCameraEvaluationContext>
{
public:
// 获取所有者
UObject* GetOwner() const;
// 获取世界
UWorld* GetWorld() const;
// 获取玩家控制器
APlayerController* GetPlayerController() const;
// 获取相机资产
const UCameraAsset* GetCameraAsset() const;
// 获取初始结果
const FCameraNodeEvaluationResult& GetInitialResult() const;
FCameraNodeEvaluationResult& GetInitialResult();
// 获取导演评估器
FCameraDirectorEvaluator* GetDirectorEvaluator() const;
// 获取父/子上下文
TSharedPtr<const FCameraEvaluationContext> GetParentContext() const;
TArrayView<const TSharedPtr<FCameraEvaluationContext>> GetChildrenContexts() const;
// 获取视口大小
FIntPoint GetViewportSize() const;
// 条件结果
const FCameraNodeEvaluationResult* GetConditionalResult(ECameraEvaluationDataCondition Condition) const;
FCameraNodeEvaluationResult& GetOrAddConditionalResult(ECameraEvaluationDataCondition Condition);
// 激活/停用
void Activate(const FCameraEvaluationContextActivateParams& Params);
void Deactivate(const FCameraEvaluationContextDeactivateParams& Params);
bool IsActive() const;
// 子上下文管理
bool AddChildContext(TSharedRef<FCameraEvaluationContext> ChildContext);
bool RemoveChildContext(TSharedRef<FCameraEvaluationContext> ChildContext);
};
5.4.2 上下文层次结构
┌─────────────────────────────────────────────────────────────────────┐
│ FCameraEvaluationContextStack │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Context 2 (Top - Active) │ │
│ │ └── UGameplayCameraComponent (CinematicCamera) │ │
│ │ └── UCameraAsset (Cinematic) │ │
│ │ └── InitialResult │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ▲ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Context 1 (Middle) │ │
│ │ └── UGameplayCameraComponent (MainCamera) │ │
│ │ └── UCameraAsset (ThirdPerson) │ │
│ │ └── InitialResult │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ▲ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Context 0 (Bottom) │ │
│ │ └── APlayerController │ │
│ │ └── Default Context │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
5.4.3 初始结果使用
// 在评估前设置初始结果
FCameraNodeEvaluationResult& InitialResult = EvaluationContext->GetInitialResult();
// 设置初始相机姿态
InitialResult.CameraPose.SetLocation(PlayerLocation);
InitialResult.CameraPose.SetRotation(PlayerRotation);
// 设置初始变量
InitialResult.VariableTable.SetValue(MovementSpeedVariable, PlayerSpeed);
// 节点评估时会基于这些初始值进行计算
5.5 扩展点说明
5.5.1 扩展点概览
| 扩展点 | 基类 | 用途 | 文件位置 |
|---|---|---|---|
| 自定义节点 | UCameraNode |
新的相机行为 | Core/CameraNode.h |
| 自定义评估器 | FCameraNodeEvaluator |
节点的运行时逻辑 | Core/CameraNodeEvaluator.h |
| 自定义混合 | UBlendCameraNode |
新的混合算法 | Core/BlendCameraNode.h |
| 自定义导演 | UCameraDirector |
控制相机激活逻辑 | Core/CameraDirector.h |
| 自定义服务 | FCameraEvaluationService |
横切关注点 | Core/CameraEvaluationService.h |
| 自定义过渡条件 | UCameraRigTransitionCondition |
过渡条件判断 | Core/CameraRigTransition.h |
5.5.2 扩展宏速查
// 声明节点评估器
#define UE_DECLARE_CAMERA_NODE_EVALUATOR(ApiDeclSpec, ClassName)\
UE_GAMEPLAY_CAMERAS_DECLARE_RTTI(ApiDeclSpec, ClassName, FCameraNodeEvaluator)
// 定义节点评估器
#define UE_DEFINE_CAMERA_NODE_EVALUATOR(ClassName)\
UE_GAMEPLAY_CAMERAS_DEFINE_RTTI(ClassName)
// 声明混合节点评估器
#define UE_DECLARE_BLEND_CAMERA_NODE_EVALUATOR(ApiDeclSpec, ClassName)\
UE_DECLARE_CAMERA_NODE_EVALUATOR_EX(ApiDeclSpec, ClassName, ::UE::Cameras::FBlendCameraNodeEvaluator)
// 声明评估服务
#define UE_DECLARE_CAMERA_EVALUATION_SERVICE(ApiDeclSpec, ClassName)\
UE_GAMEPLAY_CAMERAS_DECLARE_RTTI(ApiDeclSpec, ClassName, FCameraEvaluationService)
// 定义评估服务
#define UE_DEFINE_CAMERA_EVALUATION_SERVICE(ClassName)\
UE_GAMEPLAY_CAMERAS_DEFINE_RTTI(ClassName)
5.5.3 类型安全转换
// 安全转换
if (FBoomArmCameraNodeEvaluator* BoomEval = Evaluator->CastThis<FBoomArmCameraNodeEvaluator>())
{
// 成功转换,可以安全使用
}
// 断言转换(确保类型正确)
FBoomArmCameraNodeEvaluator* BoomEval = Evaluator->CastThisChecked<FBoomArmCameraNodeEvaluator>();
// 类型检查
if (Evaluator->IsKindOf<FBoomArmCameraNodeEvaluator>())
{
// 是 BoomArm 评估器
}
6. 节点类型参考
6.1 通用节点 (Common)
| 节点名称 | 说明 | 源文件 |
|---|---|---|
| Sequence | 按顺序执行多个子节点 | ArrayCameraNode.h |
| Boom Arm | 吊臂相机控制器,支持跟随和插值 | BoomArmCameraNode.h |
| Offset | 应用位置/旋转偏移 | OffsetCameraNode.h |
| Set Location | 直接设置相机位置 | SetLocationCameraNode.h |
| Set Rotation | 直接设置相机旋转 | SetRotationCameraNode.h |
| Dampen Position | 位置阻尼/平滑 | DampenPositionCameraNode.h |
| Dampen Rotation | 旋转阻尼/平滑 | DampenRotationCameraNode.h |
| Field of View | 设置视场角 | FieldOfViewCameraNode.h |
| Clipping Planes | 设置裁剪平面 | ClippingPlanesCameraNode.h |
| Orthographic | 正交投影设置 | OrthographicCameraNode.h |
| Post Process | 后处理效果设置 | PostProcessCameraNode.h |
| Auto Focus | 自动对焦 | AutoFocusCameraNode.h |
| Body Parameters | 物理相机机身参数 | BodyParametersCameraNode.h |
| Lens Parameters | 物理相机镜头参数 | LensParametersCameraNode.h |
| Lens Calibration | 镜头校准 | LensCalibrationCameraNode.h |
| Filmback | 胶片背设置 | FilmbackCameraNode.h |
| Target Ray Cast | 目标射线检测 | TargetRayCastCameraNode.h |
| Spline Offset | 沿样条偏移 | SplineOffsetCameraNode.h |
| Spline Orbit | 沿样条轨道 | SplineOrbitCameraNode.h |
| Spline FOV | 样条驱动的视场角 | SplineFieldOfViewCameraNode.h |
| Camera Rig | 嵌套相机装备 | CameraRigCameraNode.h |
6.1.1 Sequence 节点详解
┌─────────────────────────────────────────────────────────────┐
│ Sequence (ArrayCameraNode) │
├─────────────────────────────────────────────────────────────┤
│ 按顺序执行所有子节点,后面的节点可以覆盖前面的结果 │
│ │
│ 典型用途: │
│ [0] AttachToPlayerPawn → 附加到玩家 │
│ [1] BoomArm → 吊臂控制 │
│ [2] CollisionPush → 碰撞检测 │
│ [3] DampenRotation → 旋转平滑 │
└─────────────────────────────────────────────────────────────┘
6.1.2 Boom Arm 节点详解
// 源码位置: Public/Nodes/Common/BoomArmCameraNode.h
UCLASS()
class UBoomArmCameraNode : public UCameraNode
{
// 吊臂偏移(相对于附加点)
UPROPERTY(EditAnywhere, Category=Common)
FVector3dCameraParameter BoomOffset;
// 吊臂长度
UPROPERTY(EditAnywhere, Category=Common)
FDoubleCameraParameter BoomLength;
// 是否允许角色旋转影响相机
UPROPERTY(EditAnywhere, Category=Common)
bool bAllowUserControlRotation = true;
// 输入槽
UPROPERTY(meta=(ObjectTreeGraphPinDirection=Input))
TObjectPtr<UInput2DCameraNode> InputSlot;
};
6.2 混合节点 (Blends)
| 节点名称 | 说明 | 源文件 |
|---|---|---|
| Smooth Blend | 平滑步进混合 | SmoothBlendCameraNode.h |
| Linear Blend | 线性插值混合 | LinearBlendCameraNode.h |
| Simple Blend | 简单固定时间混合 | SimpleBlendCameraNode.h |
| Orbit Blend | 轨道旋转混合 | OrbitBlendCameraNode.h |
| Location Rotation Blend | 位置/旋转分离混合 | LocationRotationBlendCameraNode.h |
| Pop Blend | 立即切换(无混合) | PopBlendCameraNode.h |
| Reverse Blend | 反向混合 | ReverseBlendCameraNode.h |
| Interrupted Blend | 中断后继续的混合 | InterruptedBlendCameraNode.h |
6.2.1 混合曲线对比
混合因子
1.0 ┤ ●────────── SmoothStep
│ ●────
│ ●───
0.5 ┤ ●─── ○────────────── Linear
│ ●─── ○───
│ ●─── ○───
0.0 ┤────────────────────────────────────► 时间
0 0.5 1.0
SmoothStep: y = 3t² - 2t³ (更平滑的开始和结束)
Linear: y = t (匀速)
6.2.2 使用场景
| 场景 | 推荐混合节点 | 原因 |
|---|---|---|
| 一般切换 | SmoothBlend | 自然流畅 |
| 快速切换 | LinearBlend (短时间) | 可控速度 |
| 剧情切镜 | PopBlend | 无过渡 |
| 绕目标旋转 | OrbitBlend | 保持注视目标 |
| 不同维度混合 | LocationRotationBlend | 位置和旋转独立控制 |
6.3 附加节点 (Attach)
| 节点名称 | 说明 | 源文件 |
|---|---|---|
| Attach to Player Pawn | 附加到玩家 Pawn | AttachToPlayerPawnCameraNode.h |
| Attach to Actor | 附加到指定 Actor | AttachToActorCameraNode.h |
| Attach to Actor Group | 附加到 Actor 组 | AttachToActorGroupCameraNode.h |
6.3.1 Attach to Player Pawn 详解
// 源码位置: Public/Nodes/Attach/AttachToPlayerPawnCameraNode.h
UCLASS()
class UAttachToPlayerPawnCameraNode : public UCameraNode
{
// 骨骼/Socket 附加信息
UPROPERTY(EditAnywhere, Category=Common)
FBoneSocketCameraAttachment TargetAttachment;
// 是否应用玩家控制旋转
UPROPERTY(EditAnywhere, Category=Common)
bool bApplyPlayerControlRotation = true;
// 附加偏移
UPROPERTY(EditAnywhere, Category=Common)
FVector3dCameraParameter AttachmentOffset;
};
6.3.2 附加信息结构
// 源码位置: Public/Nodes/Attach/CameraActorAttachmentInfo.h
USTRUCT()
struct FBoneSocketCameraAttachment
{
GENERATED_BODY()
// 附加目标类型
UPROPERTY(EditAnywhere, Category=Common)
ECameraAttachmentTarget AttachmentTarget;
// Socket 名称
UPROPERTY(EditAnywhere, Category=Common)
FName SocketName;
// 骨骼名称
UPROPERTY(EditAnywhere, Category=Common)
FName BoneName;
};
6.4 构图节点 (Framing)
| 节点名称 | 说明 | 源文件 |
|---|---|---|
| Dolly Framing | 推拉构图 | DollyFramingCameraNode.h |
| Panning Framing | 平移构图 | PanningFramingCameraNode.h |
| Camera Framing Zone | 取景区域 | CameraFramingZone.h |
6.4.1 Dolly Framing 详解
// 源码位置: Public/Nodes/Framing/DollyFramingCameraNode.h
UCLASS()
class UDollyFramingCameraNode : public UBaseFramingCameraNode
{
// 目标演员信息
UPROPERTY(EditAnywhere, Category=Common)
FCameraActorTargetInfo TargetInfo;
// 构图规则
UPROPERTY(EditAnywhere, Category=Common)
ECameraFramingMode FramingMode;
// 目标距离
UPROPERTY(EditAnywhere, Category=Common)
FDoubleCameraParameter TargetDistance;
};
6.5 输入节点 (Input)
| 节点名称 | 说明 | 源文件 |
|---|---|---|
| Input 2D | 2D 输入处理(Yaw/Pitch) | Input2DCameraNode.h |
| Input 1D | 1D 输入处理 | Input1DCameraNode.h |
| Input Axis Binding 2D | 2D 输入轴绑定 | InputAxisBinding2DCameraNode.h |
| Auto Rotate Input 2D | 自动旋转 2D 输入 | AutoRotateInput2DCameraNode.h |
| Input 2D Slot | 相机装备输入槽 | CameraRigInput2DSlot.h |
| Input 1D Slot | 相机装备输入槽 | CameraRigInput1DSlot.h |
6.5.1 Input 2D 详解
// 源码位置: Public/Nodes/Input/Input2DCameraNode.h
UCLASS()
class UInput2DCameraNode : public UCameraNode
{
// Yaw 旋转速度(度/秒)
UPROPERTY(EditAnywhere, Category=Common)
FFloatCameraParameter YawSpeed;
// Pitch 旋转速度(度/秒)
UPROPERTY(EditAnywhere, Category=Common)
FFloatCameraParameter PitchSpeed;
// Pitch 限制
UPROPERTY(EditAnywhere, Category=Common)
FVector2fCameraParameter PitchLimits;
// 是否反转 Y 轴
UPROPERTY(EditAnywhere, Category=Common)
bool bInvertY = false;
};
6.6 抖动节点 (Shakes)
| 节点名称 | 说明 | 源文件 |
|---|---|---|
| Camera Shake | 相机震动(旧版兼容) | CameraShakeCameraNode.h |
| Envelope Shake | 包络震动 | EnvelopeShakeCameraNode.h |
| Composite Shake | 复合震动 | CompositeShakeCameraNode.h |
| Perlin Noise Location Shake | 柏林噪声位置震动 | PerlinNoiseLocationShakeCameraNode.h |
| Perlin Noise Rotation Shake | 柏林噪声旋转震动 | PerlinNoiseRotationShakeCameraNode.h |
6.6.1 包络震动详解
// 源码位置: Public/Nodes/Shakes/EnvelopeShakeCameraNode.h
UCLASS()
class UEnvelopeShakeCameraNode : public UCameraNode
{
// 震动持续时间
UPROPERTY(EditAnywhere, Category=Common)
float Duration = 1.0f;
// 位置震动幅度
UPROPERTY(EditAnywhere, Category=Location)
FVector LocationAmplitude;
// 位置震动频率
UPROPERTY(EditAnywhere, Category=Location)
FVector LocationFrequency;
// 旋转震动幅度
UPROPERTY(EditAnywhere, Category=Rotation)
FRotator RotationAmplitude;
// 旋转震动频率
UPROPERTY(EditAnywhere, Category=Rotation)
FRotator RotationFrequency;
// 淡入时间
UPROPERTY(EditAnywhere, Category=Common)
float BlendInTime = 0.1f;
// 淡出时间
UPROPERTY(EditAnywhere, Category=Common)
float BlendOutTime = 0.2f;
};
6.6.2 震动包络曲线
振幅
1.0 ┤ ●━━━━━━━━━━━━━━━━━━●
│ ╱ ╲
│ ╱ ╲
0.5 ┤ ╱ ╲
│╱ ╲
0.0 ┤─────────────────────────────► 时间
│←BlendIn→│← Sustain →│←BlendOut→│
6.7 碰撞节点 (Collision)
| 节点名称 | 说明 | 源文件 |
|---|---|---|
| Collision Push | 碰撞推挤检测 | CollisionPushCameraNode.h |
| Occlusion Material | 遮挡材质处理 | OcclusionMaterialCameraNode.h |
6.7.1 Collision Push 详解
// 源码位置: Public/Nodes/Collision/CollisionPushCameraNode.h
UCLASS()
class UCollisionPushCameraNode : public UCameraNode
{
// 碰撞检测通道
UPROPERTY(EditAnywhere, Category=Common)
TEnumAsByte<ECollisionChannel> CollisionChannel = ECC_Camera;
// 最小推挤距离
UPROPERTY(EditAnywhere, Category=Common)
FDoubleCameraParameter MinimumDistance;
// 探针半径
UPROPERTY(EditAnywhere, Category=Common)
FDoubleCameraParameter ProbeRadius;
// 探针通道
UPROPERTY(EditAnywhere, Category=Common)
TEnumAsByte<ECollisionChannel> ProbeChannel = ECC_Camera;
// 是否绘制调试线
UPROPERTY(EditAnywhere, Category=Debug)
bool bDrawDebugLines = false;
};
6.7.2 碰撞检测流程
1. 从目标点发射射线到相机目标位置
2. 检测射线与碰撞体的相交
3. 如果相交,将相机推到碰撞点前
4. 应用平滑插值避免抖动
目标位置 ●───────────────────────► 相机位置
▲
碰撞体
推挤后:
目标位置 ●────────► 新相机位置
7. 扩展开发指南
7.1 自定义相机节点开发
7.1.1 完整示例:跟随延迟节点
Step 1: 创建节点类
// MyFollowDelayCameraNode.h
#pragma once
#include "Core/CameraNode.h"
#include "MyFollowDelayCameraNode.generated.h"
/**
* 一个自定义节点,实现相机跟随目标时的延迟效果。
* 相机会在指定时间内平滑地跟随目标位置。
*/
UCLASS(MinimalAPI, meta=(CameraNodeCategories="Common,Transform"))
class UMyFollowDelayCameraNode : public UCameraNode
{
GENERATED_BODY()
public:
UMyFollowDelayCameraNode(const FObjectInitializer& ObjInit);
protected:
// 返回子节点
virtual FCameraNodeChildrenView OnGetChildren() override;
// 构建评估器
virtual FCameraNodeEvaluatorPtr OnBuildEvaluator(FCameraNodeEvaluatorBuilder& Builder) const override;
public:
// 延迟时间(秒)
UPROPERTY(EditAnywhere, Category=Common)
FFloatCameraParameter DelayTime;
// 跟随速度倍数
UPROPERTY(EditAnywhere, Category=Common)
FFloatCameraParameter FollowSpeed = 5.0f;
// 最大跟随距离
UPROPERTY(EditAnywhere, Category=Common)
FDoubleCameraParameter MaxDistance = 500.0;
// 是否在 X/Y 平面跟随
UPROPERTY(EditAnywhere, Category=Common)
bool bFollowXY = true;
// 是否在 Z 轴跟随
UPROPERTY(EditAnywhere, Category=Common)
bool bFollowZ = true;
};
Step 2: 创建评估器类
// MyFollowDelayCameraNodeEvaluator.h
#pragma once
#include "Core/CameraNodeEvaluator.h"
#include "MyFollowDelayCameraNodeEvaluator.generated.h"
namespace UE::Cameras
{
class FMyFollowDelayCameraNodeEvaluator : public FCameraNodeEvaluator
{
UE_DECLARE_CAMERA_NODE_EVALUATOR(GAMEPLAYCAMERAS_API, FMyFollowDelayCameraNodeEvaluator)
public:
FMyFollowDelayCameraNodeEvaluator();
protected:
// FCameraNodeEvaluator 接口
virtual void OnInitialize(const FCameraNodeEvaluatorInitializeParams& Params,
FCameraNodeEvaluationResult& OutResult) override;
virtual void OnRun(const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult) override;
virtual void OnSerialize(const FCameraNodeEvaluatorSerializeParams& Params,
FArchive& Ar) override;
private:
// 历史位置缓冲
struct FHistoryEntry
{
FVector3d Location;
double Timestamp;
};
TArray<FHistoryEntry> LocationHistory;
// 当前目标位置
FVector3d CurrentTargetLocation;
// 当前相机位置(用于插值)
FVector3d CurrentCameraLocation;
// 是否已初始化
bool bInitialized = false;
};
} // namespace UE::Cameras
Step 3: 实现评估器
// MyFollowDelayCameraNodeEvaluator.cpp
#include "MyFollowDelayCameraNodeEvaluator.h"
#include "MyFollowDelayCameraNode.h"
namespace UE::Cameras
{
UE_DEFINE_CAMERA_NODE_EVALUATOR(FMyFollowDelayCameraNodeEvaluator)
FMyFollowDelayCameraNodeEvaluator::FMyFollowDelayCameraNodeEvaluator()
: CurrentTargetLocation(FVector3d::ZeroVector)
, CurrentCameraLocation(FVector3d::ZeroVector)
{
}
void FMyFollowDelayCameraNodeEvaluator::OnInitialize(
const FCameraNodeEvaluatorInitializeParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
bInitialized = false;
LocationHistory.Empty();
}
void FMyFollowDelayCameraNodeEvaluator::OnRun(
const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
const UMyFollowDelayCameraNode* NodeData = GetCameraNodeAs<UMyFollowDelayCameraNode>();
// 获取参数值
const float DelayTime = NodeData->DelayTime.GetValue(OutResult.VariableTable);
const float FollowSpeed = NodeData->FollowSpeed.GetValue(OutResult.VariableTable);
const double MaxDistance = NodeData->MaxDistance.GetValue(OutResult.VariableTable);
// 获取当前相机位置
const FVector3d CurrentPoseLocation = OutResult.CameraPose.GetLocation();
const double CurrentTime = FPlatformTime::Seconds();
// 第一帧初始化
if (!bInitialized)
{
CurrentTargetLocation = CurrentPoseLocation;
CurrentCameraLocation = CurrentPoseLocation;
bInitialized = true;
LocationHistory.Add({CurrentPoseLocation, CurrentTime});
return;
}
// 添加到历史记录
FHistoryEntry NewEntry;
NewEntry.Location = CurrentPoseLocation;
NewEntry.Timestamp = CurrentTime;
LocationHistory.Add(NewEntry);
// 清理过期历史
const double CutoffTime = CurrentTime - DelayTime;
while (LocationHistory.Num() > 1 && LocationHistory[0].Timestamp < CutoffTime)
{
LocationHistory.RemoveAt(0);
}
// 计算延迟目标位置(插值)
FVector3d DelayedTarget = LocationHistory[0].Location;
for (int32 i = 1; i < LocationHistory.Num(); ++i)
{
if (LocationHistory[i].Timestamp <= CutoffTime)
{
DelayedTarget = LocationHistory[i].Location;
}
else
{
// 在两个历史点之间插值
const double Alpha = (CutoffTime - LocationHistory[i-1].Timestamp) /
(LocationHistory[i].Timestamp - LocationHistory[i-1].Timestamp);
DelayedTarget = FMath::Lerp(LocationHistory[i-1].Location,
LocationHistory[i].Location, Alpha);
break;
}
}
// 平滑跟随到延迟目标
const float DeltaTime = Params.DeltaTime;
FVector3d NewLocation = CurrentCameraLocation;
if (NodeData->bFollowXY)
{
NewLocation.X = FMath::FInterpTo(CurrentCameraLocation.X, DelayedTarget.X, DeltaTime, FollowSpeed);
NewLocation.Y = FMath::FInterpTo(CurrentCameraLocation.Y, DelayedTarget.Y, DeltaTime, FollowSpeed);
}
if (NodeData->bFollowZ)
{
NewLocation.Z = FMath::FInterpTo(CurrentCameraLocation.Z, DelayedTarget.Z, DeltaTime, FollowSpeed);
}
// 限制最大距离
const FVector3d Direction = NewLocation - CurrentPoseLocation;
const double Distance = Direction.Length();
if (Distance > MaxDistance)
{
NewLocation = CurrentPoseLocation + Direction.GetSafeNormal() * MaxDistance;
}
// 更新状态
CurrentCameraLocation = NewLocation;
OutResult.CameraPose.SetLocation(NewLocation);
}
void FMyFollowDelayCameraNodeEvaluator::OnSerialize(
const FCameraNodeEvaluatorSerializeParams& Params,
FArchive& Ar)
{
Super::OnSerialize(Params, Ar);
Ar << CurrentTargetLocation;
Ar << CurrentCameraLocation;
Ar << bInitialized;
// 序列化历史记录
int32 HistoryCount = LocationHistory.Num();
Ar << HistoryCount;
if (Ar.IsLoading())
{
LocationHistory.SetNum(HistoryCount);
}
for (int32 i = 0; i < HistoryCount; ++i)
{
Ar << LocationHistory[i].Location;
Ar << LocationHistory[i].Timestamp;
}
}
} // namespace UE::Cameras
Step 4: 实现节点类
// MyFollowDelayCameraNode.cpp
#include "MyFollowDelayCameraNode.h"
#include "MyFollowDelayCameraNodeEvaluator.h"
UMyFollowDelayCameraNode::UMyFollowDelayCameraNode(const FObjectInitializer& ObjInit)
: Super(ObjInit)
{
DelayTime.Value = 0.3f;
}
FCameraNodeChildrenView UMyFollowDelayCameraNode::OnGetChildren()
{
return FCameraNodeChildrenView();
}
FCameraNodeEvaluatorPtr UMyFollowDelayCameraNode::OnBuildEvaluator(
FCameraNodeEvaluatorBuilder& Builder) const
{
using namespace UE::Cameras;
return Builder.BuildEvaluator<FMyFollowDelayCameraNodeEvaluator>();
}
7.1.2 节点开发最佳实践
| 实践 | 说明 |
|---|---|
| 参数化 | 使用 F*CameraParameter 类型,支持变量覆盖 |
| 状态管理 | 在评估器中维护状态,支持序列化 |
| 性能优化 | 避免每帧分配内存,预分配缓冲区 |
| 调试支持 | 实现 OnBuildDebugBlocks 提供调试可视化 |
| 验证检查 | 在 OnPreBuild 中验证参数有效性 |
7.2 自定义评估服务开发
7.2.1 完整示例:镜头效果服务
// MyLensEffectService.h
#pragma once
#include "Core/CameraEvaluationService.h"
#include "MyLensEffectService.generated.h"
namespace UE::Cameras
{
/**
* 自定义评估服务:在相机上应用镜头效果(如光晕、色散等)
*/
class FMyLensEffectService : public FCameraEvaluationService
{
UE_DECLARE_CAMERA_EVALUATION_SERVICE(GAMEPLAYCAMERAS_API, FMyLensEffectService)
public:
FMyLensEffectService();
// 设置镜头效果强度
void SetLensEffectIntensity(float Intensity);
// 设置色散强度
void SetChromaticAberration(float Strength);
protected:
// FCameraEvaluationService 接口
virtual void OnInitialize(const FCameraEvaluationServiceInitializeParams& Params) override;
virtual void OnPreUpdate(const FCameraEvaluationServiceUpdateParams& Params,
FCameraEvaluationServiceUpdateResult& OutResult) override;
virtual void OnPostUpdate(const FCameraEvaluationServiceUpdateParams& Params,
FCameraEvaluationServiceUpdateResult& OutResult) override;
virtual void OnRootCameraNodeEvent(const FRootCameraNodeCameraRigEvent& InEvent) override;
private:
// 效果参数
float LensFlareIntensity = 0.0f;
float ChromaticAberrationStrength = 0.0f;
// 动态更新标志
bool bNeedsUpdate = false;
};
} // namespace UE::Cameras
// MyLensEffectService.cpp
#include "MyLensEffectService.h"
namespace UE::Cameras
{
UE_DEFINE_CAMERA_EVALUATION_SERVICE(FMyLensEffectService)
FMyLensEffectService::FMyLensEffectService()
{
// 设置需要接收的回调
SetEvaluationServiceFlags(
ECameraEvaluationServiceFlags::NeedsPreUpdate |
ECameraEvaluationServiceFlags::NeedsPostUpdate |
ECameraEvaluationServiceFlags::NeedsRootCameraNodeEvents
);
}
void FMyLensEffectService::OnInitialize(const FCameraEvaluationServiceInitializeParams& Params)
{
LensFlareIntensity = 0.0f;
ChromaticAberrationStrength = 0.0f;
bNeedsUpdate = false;
}
void FMyLensEffectService::SetLensEffectIntensity(float Intensity)
{
LensFlareIntensity = FMath::Clamp(Intensity, 0.0f, 1.0f);
bNeedsUpdate = true;
}
void FMyLensEffectService::SetChromaticAberration(float Strength)
{
ChromaticAberrationStrength = FMath::Clamp(Strength, 0.0f, 1.0f);
bNeedsUpdate = true;
}
void FMyLensEffectService::OnPreUpdate(
const FCameraEvaluationServiceUpdateParams& Params,
FCameraEvaluationServiceUpdateResult& OutResult)
{
// 在节点评估前执行的逻辑
// 例如:根据游戏状态自动调整效果强度
}
void FMyLensEffectService::OnPostUpdate(
const FCameraEvaluationServiceUpdateParams& Params,
FCameraEvaluationServiceUpdateResult& OutResult)
{
if (!bNeedsUpdate)
{
return;
}
// 应用镜头效果到后处理设置
FPostProcessSettings& PostProcessSettings =
OutResult.EvaluationResult.PostProcessSettings.GetWriteableObject();
// 设置色散
PostProcessSettings.bOverride_SceneFringeIntensity = true;
PostProcessSettings.SceneFringeIntensity = ChromaticAberrationStrength;
// 设置光晕(通过 bloom 模拟)
PostProcessSettings.bOverride_BloomIntensity = true;
PostProcessSettings.BloomIntensity = LensFlareIntensity;
bNeedsUpdate = false;
}
void FMyLensEffectService::OnRootCameraNodeEvent(const FRootCameraNodeCameraRigEvent& InEvent)
{
// 响应相机装备激活/停用事件
if (InEvent.EventType == ERootCameraNodeCameraRigEventType::Activated)
{
// 新相机激活时重置效果
LensFlareIntensity = 0.0f;
ChromaticAberrationStrength = 0.0f;
bNeedsUpdate = true;
}
}
} // namespace UE::Cameras
7.2.2 服务注册
// 在游戏模块启动时注册服务
void FMyGameModule::StartupModule()
{
// ...
// 注册自定义服务
GetOnCameraSystemEvaluatorCreated().AddLambda([](FCameraSystemEvaluator& Evaluator)
{
TSharedRef<FMyLensEffectService> LensEffectService = MakeShared<FMyLensEffectService>();
Evaluator.RegisterEvaluationService(LensEffectService);
});
}
7.3 自定义导演开发
7.3.1 完整示例:战斗状态导演
// MyCombatCameraDirector.h
#pragma once
#include "Core/CameraDirector.h"
#include "MyCombatCameraDirector.generated.h"
class UCameraRigAsset;
/**
* 根据战斗状态切换相机的导演
*/
UCLASS(MinimalAPI)
class UMyCombatCameraDirector : public UCameraDirector
{
GENERATED_BODY()
public:
UMyCombatCameraDirector(const FObjectInitializer& ObjInit);
protected:
// UCameraDirector 接口
virtual FCameraDirectorEvaluatorPtr OnBuildEvaluator(
FCameraDirectorEvaluatorBuilder& Builder) const override;
virtual void OnBuildCameraDirector(UE::Cameras::FCameraBuildLog& BuildLog) override;
virtual void OnGatherRigUsageInfo(FCameraDirectorRigUsageInfo& UsageInfo) const override;
public:
// 非战斗状态的相机装备
UPROPERTY(EditAnywhere, Category=Common)
TObjectPtr<UCameraRigAsset> ExplorationCameraRig;
// 战斗状态的相机装备
UPROPERTY(EditAnywhere, Category=Common)
TObjectPtr<UCameraRigAsset> CombatCameraRig;
// 进入战斗的过渡
UPROPERTY(EditAnywhere, Category=Transitions)
TObjectPtr<UCameraRigTransition> EnterCombatTransition;
// 退出战斗的过渡
UPROPERTY(EditAnywhere, Category=Transitions)
TObjectPtr<UCameraRigTransition> ExitCombatTransition;
};
// MyCombatCameraDirector.cpp
#include "MyCombatCameraDirector.h"
#include "MyCombatCameraDirectorEvaluator.h"
UMyCombatCameraDirector::UMyCombatCameraDirector(const FObjectInitializer& ObjInit)
: Super(ObjInit)
{
}
FCameraDirectorEvaluatorPtr UMyCombatCameraDirector::OnBuildEvaluator(
FCameraDirectorEvaluatorBuilder& Builder) const
{
using namespace UE::Cameras;
return Builder.BuildEvaluator<FMyCombatCameraDirectorEvaluator>();
}
void UMyCombatCameraDirector::OnBuildCameraDirector(UE::Cameras::FCameraBuildLog& BuildLog)
{
// 验证相机装备是否设置
if (!ExplorationCameraRig)
{
BuildLog.AddMessage(EMessageSeverity::Warning,
TEXT("ExplorationCameraRig is not set"));
}
if (!CombatCameraRig)
{
BuildLog.AddMessage(EMessageSeverity::Warning,
TEXT("CombatCameraRig is not set"));
}
}
void UMyCombatCameraDirector::OnGatherRigUsageInfo(FCameraDirectorRigUsageInfo& UsageInfo) const
{
if (ExplorationCameraRig)
{
UsageInfo.CameraRigs.Add(ExplorationCameraRig);
}
if (CombatCameraRig)
{
UsageInfo.CameraRigs.Add(CombatCameraRig);
}
}
// MyCombatCameraDirectorEvaluator.h
#pragma once
#include "Core/CameraDirectorEvaluator.h"
namespace UE::Cameras
{
class FMyCombatCameraDirectorEvaluator : public FCameraDirectorEvaluator
{
UE_DECLARE_CAMERA_DIRECTOR_EVALUATOR(FMyCombatCameraDirectorEvaluator)
public:
// 设置战斗状态
void SetCombatState(bool bInCombat);
protected:
virtual void OnRun(const FCameraDirectorEvaluateParams& Params,
FCameraDirectorEvaluateResult& OutResult) override;
private:
bool bInCombat = false;
bool bWasInCombat = false;
};
} // namespace UE::Cameras
// MyCombatCameraDirectorEvaluator.cpp
#include "MyCombatCameraDirectorEvaluator.h"
#include "MyCombatCameraDirector.h"
namespace UE::Cameras
{
UE_DEFINE_CAMERA_DIRECTOR_EVALUATOR(FMyCombatCameraDirectorEvaluator)
void FMyCombatCameraDirectorEvaluator::SetCombatState(bool bInCombatIn)
{
bInCombat = bInCombatIn;
}
void FMyCombatCameraDirectorEvaluator::OnRun(
const FCameraDirectorEvaluateParams& Params,
FCameraDirectorEvaluateResult& OutResult)
{
const UMyCombatCameraDirector* Director = GetCameraDirectorAs<UMyCombatCameraDirector>();
// 检测战斗状态变化
if (bInCombat && !bWasInCombat)
{
// 进入战斗 - 请求激活战斗相机
FCameraDirectorActivationRequest Request;
Request.CameraRig = Director->CombatCameraRig;
Request.TransitionOverride = Director->EnterCombatTransition;
OutResult.ActivationRequests.Add(Request);
}
else if (!bInCombat && bWasInCombat)
{
// 退出战斗 - 请求激活探索相机
FCameraDirectorActivationRequest Request;
Request.CameraRig = Director->ExplorationCameraRig;
Request.TransitionOverride = Director->ExitCombatTransition;
OutResult.ActivationRequests.Add(Request);
}
bWasInCombat = bInCombat;
}
} // namespace UE::Cameras
8. 实际示例
8.1 示例1:简单的第三人称相机
8.1.1 创建相机装备资产
在编辑器中创建 UCameraRigAsset,设置以下节点结构:
ThirdPersonCameraRig (UCameraRigAsset)
└── RootNode: Sequence
├── [0] AttachToPlayerPawn
│ └── 附加到: Pawn 的 Mesh
│ └── Socket: Head 或自定义 Socket
├── [1] BoomArm
│ └── BoomOffset: (0, 0, 50)
│ └── BoomLength: 300
│ └── InputSlot: Input2D
├── [2] CollisionPush
│ └── CollisionChannel: Camera
│ └── MinimumDistance: 50
└── [3] DampenRotation
└── InterpolationSpeed: 10
8.1.2 代码创建方式
// 创建相机装备
UCameraRigAsset* ThirdPersonRig = NewObject<UCameraRigAsset>();
// 创建 Sequence 节点作为根
UArrayCameraNode* SequenceNode = NewObject<UArrayCameraNode>(ThirdPersonRig);
ThirdPersonRig->RootNode = SequenceNode;
// 添加 AttachToPlayerPawn 节点
UAttachToPlayerPawnCameraNode* AttachNode = NewObject<UAttachToPlayerPawnCameraNode>(SequenceNode);
AttachNode->TargetAttachment.SocketName = FName("Head");
SequenceNode->Children.Add(AttachNode);
// 添加 BoomArm 节点
UBoomArmCameraNode* BoomArmNode = NewObject<UBoomArmCameraNode>(SequenceNode);
BoomArmNode->BoomOffset.Value = FVector(0, 0, 50);
BoomArmNode->BoomLength.Value = 300.0;
SequenceNode->Children.Add(BoomArmNode);
// 添加 Input2D 节点作为 BoomArm 的输入
UInput2DCameraNode* InputNode = NewObject<UInput2DCameraNode>(BoomArmNode);
InputNode->YawSpeed.Value = 90.0f;
InputNode->PitchSpeed.Value = 45.0f;
BoomArmNode->InputSlot = InputNode;
// 添加 CollisionPush 节点
UCollisionPushCameraNode* CollisionNode = NewObject<UCollisionPushCameraNode>(SequenceNode);
CollisionNode->MinimumDistance.Value = 50.0;
SequenceNode->Children.Add(CollisionNode);
// 添加 DampenRotation 节点
UDampenRotationCameraNode* DampenNode = NewObject<UDampenRotationCameraNode>(SequenceNode);
DampenNode->InterpolationSpeed.Value = 10.0f;
SequenceNode->Children.Add(DampenNode);
8.1.3 使用相机装备
// 在 PlayerController 或 Character 中
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
// 获取相机组件
if (UGameplayCameraComponent* CameraComp = FindComponentByClass<UGameplayCameraComponent>())
{
// 相机组件会自动使用其 CameraAsset
// 确保在编辑器中设置了正确的 CameraAsset
}
}
// 或者通过蓝图函数库激活
#include "GameFramework/ActivateCameraRigFunctions.h"
void AMyCharacter::ActivateThirdPersonCamera()
{
FActivateCameraRigParams Params;
Params.CameraRig = ThirdPersonRig;
Params.Layer = ECameraRigLayer::Main;
UActivateCameraRigFunctions::ActivateCameraRig(Params);
}
8.2 示例2:相机混合过渡
8.2.1 创建过渡资产
// 创建平滑过渡
UCameraRigTransition* SmoothTransition = NewObject<UCameraRigTransition>();
// 设置混合节点
USmoothBlendCameraNode* BlendNode = NewObject<USmoothBlendCameraNode>(SmoothTransition);
BlendNode->BlendType = ESmoothCameraBlendType::SmootherStep;
BlendNode->Duration.Value = 1.0f;
SmoothTransition->Blend = BlendNode;
// 设置初始朝向
SmoothTransition->bOverrideInitialOrientation = true;
SmoothTransition->InitialOrientation = ECameraRigInitialOrientation::PreviousYawPitch;
8.2.2 运行时切换相机
// 从第三人称切换到第一人称
void AMyCharacter::SwitchToFirstPerson()
{
// 停用第三人称相机
FDeactivateCameraRigParams DeactivateParams;
DeactivateParams.CameraRig = ThirdPersonRig;
DeactivateParams.Layer = ECameraRigLayer::Main;
// 注意:Main 层使用 Freeze 而非 Remove
UActivateCameraRigFunctions::DeactivateCameraRig(DeactivateParams);
// 激活第一人称相机
FActivateCameraRigParams ActivateParams;
ActivateParams.CameraRig = FirstPersonRig;
ActivateParams.Layer = ECameraRigLayer::Main;
ActivateParams.TransitionOverride = SmoothTransition; // 使用自定义过渡
UActivateCameraRigFunctions::ActivateCameraRig(ActivateParams);
}
8.2.3 过渡条件示例
// 创建带条件的过渡
UCameraRigTransition* ConditionalTransition = NewObject<UCameraRigTransition>();
// 添加条件:只从探索相机切换到战斗相机
UTransitionCondition_CameraRig* FromCondition = NewObject<UTransitionCondition_CameraRig>();
FromCondition->ExpectedCameraRig = ExplorationRig;
ConditionalTransition->Conditions.Add(FromCondition);
// 添加条件:目标必须是战斗相机
UTransitionCondition_CameraRig* ToCondition = NewObject<UTransitionCondition_CameraRig>();
ToCondition->ExpectedCameraRig = CombatRig;
ConditionalTransition->Conditions.Add(ToCondition);
// 设置混合
USmoothBlendCameraNode* Blend = NewObject<USmoothBlendCameraNode>(ConditionalTransition);
Blend->Duration.Value = 0.5f;
ConditionalTransition->Blend = Blend;
8.3 示例3:自定义相机节点
8.3.1 需求:速度响应 FOV 节点
创建一个根据角色移动速度自动调整 FOV 的节点:
// SpeedResponsiveFOVCameraNode.h
UCLASS(MinimalAPI, meta=(CameraNodeCategories="Common,FOV"))
class USpeedResponsiveFOVCameraNode : public UCameraNode
{
GENERATED_BODY()
public:
USpeedResponsiveFOVCameraNode(const FObjectInitializer& ObjInit);
protected:
virtual FCameraNodeEvaluatorPtr OnBuildEvaluator(
FCameraNodeEvaluatorBuilder& Builder) const override;
public:
// 最小 FOV(静止时)
UPROPERTY(EditAnywhere, Category=Common)
FFloatCameraParameter MinFOV = 75.0f;
// 最大 FOV(最大速度时)
UPROPERTY(EditAnywhere, Category=Common)
FFloatCameraParameter MaxFOV = 90.0f;
// 触发最大 FOV 的速度阈值
UPROPERTY(EditAnywhere, Category=Common)
FFloatCameraParameter MaxSpeed = 1000.0f;
// 插值速度
UPROPERTY(EditAnywhere, Category=Common)
FFloatCameraParameter InterpolationSpeed = 5.0f;
};
// SpeedResponsiveFOVCameraNodeEvaluator.cpp
void FSpeedResponsiveFOVCameraNodeEvaluator::OnRun(
const FCameraNodeEvaluationParams& Params,
FCameraNodeEvaluationResult& OutResult)
{
const USpeedResponsiveFOVCameraNode* NodeData = GetCameraNodeAs<USpeedResponsiveFOVCameraNode>();
// 获取参数
const float MinFOV = NodeData->MinFOV.GetValue(OutResult.VariableTable);
const float MaxFOV = NodeData->MaxFOV.GetValue(OutResult.VariableTable);
const float MaxSpeed = NodeData->MaxSpeed.GetValue(OutResult.VariableTable);
const float InterpSpeed = NodeData->InterpolationSpeed.GetValue(OutResult.VariableTable);
// 获取角色速度(从上下文获取)
APlayerController* PC = Params.EvaluationContext->GetPlayerController();
float CurrentSpeed = 0.0f;
if (APawn* Pawn = PC ? PC->GetPawn() : nullptr)
{
CurrentSpeed = Pawn->GetVelocity().Size();
}
// 计算目标 FOV
const float SpeedRatio = FMath::Clamp(CurrentSpeed / MaxSpeed, 0.0f, 1.0f);
const float TargetFOV = FMath::Lerp(MinFOV, MaxFOV, SpeedRatio);
// 平滑插值
CurrentFOV = FMath::FInterpTo(CurrentFOV, TargetFOV, Params.DeltaTime, InterpSpeed);
// 应用 FOV
OutResult.CameraPose.FieldOfView = CurrentFOV;
}
8.4 示例4:使用混合栈管理多个相机
8.4.1 多层相机架构
// 游戏中同时运行多个相机层
// === Base Layer ===
// 始终运行的附加效果
FActivateCameraRigParams BaseParams;
BaseParams.CameraRig = AttachToPlayerRig; // 附加到玩家
BaseParams.Layer = ECameraRigLayer::Base;
UActivateCameraRigFunctions::ActivateCameraRig(BaseParams);
// === Main Layer ===
// 游戏玩法相机(会混合切换)
FActivateCameraRigParams MainParams;
MainParams.CameraRig = ThirdPersonRig;
MainParams.Layer = ECameraRigLayer::Main;
UActivateCameraRigFunctions::ActivateCameraRig(MainParams);
// === Global Layer ===
// 全局效果(震动等)
FActivateCameraRigParams GlobalParams;
GlobalParams.CameraRig = CameraShakeRig;
GlobalParams.Layer = ECameraRigLayer::Global;
UActivateCameraRigFunctions::ActivateCameraRig(GlobalParams);
// === Visual Layer ===
// 后处理效果
FActivateCameraRigParams VisualParams;
VisualParams.CameraRig = PostProcessRig;
VisualParams.Layer = ECameraRigLayer::Visual;
UActivateCameraRigFunctions::ActivateCameraRig(VisualParams);
8.4.2 动态相机切换系统
// MyCameraManager.h
UCLASS()
class UMyCameraManager : public UActorComponent
{
GENERATED_BODY()
public:
// 相机模式枚举
UENUM(BlueprintType)
enum class ECameraMode : uint8
{
Exploration,
Combat,
Dialogue,
Cinematic,
Death
};
// 切换相机模式
UFUNCTION(BlueprintCallable, Category="Camera")
void SetCameraMode(ECameraMode NewMode);
// 获取当前模式
UFUNCTION(BlueprintPure, Category="Camera")
ECameraMode GetCurrentMode() const { return CurrentMode; }
protected:
// 相机资产映射
UPROPERTY(EditDefaultsOnly, Category="Config")
TMap<ECameraMode, TSoftObjectPtr<UCameraRigAsset>> CameraRigMap;
// 过渡资产映射
UPROPERTY(EditDefaultsOnly, Category="Config")
TMap<TPair<ECameraMode, ECameraMode>, UCameraRigTransition*> TransitionMap;
private:
ECameraMode CurrentMode = ECameraMode::Exploration;
FCameraRigInstanceID CurrentInstanceID;
};
// MyCameraManager.cpp
void UMyCameraManager::SetCameraMode(ECameraMode NewMode)
{
if (CurrentMode == NewMode)
{
return;
}
// 获取目标相机装备
UCameraRigAsset* TargetRig = CameraRigMap[NewMode].LoadSynchronous();
if (!TargetRig)
{
return;
}
// 查找过渡
UCameraRigTransition* Transition = nullptr;
TPair<ECameraMode, ECameraMode> TransitionKey(CurrentMode, NewMode);
if (UCameraRigTransition** FoundTransition = TransitionMap.Find(TransitionKey))
{
Transition = *FoundTransition;
}
// 激活新相机
FActivateCameraRigParams Params;
Params.CameraRig = TargetRig;
Params.Layer = ECameraRigLayer::Main;
Params.TransitionOverride = Transition;
CurrentInstanceID = UActivateCameraRigFunctions::ActivateCameraRig(Params);
CurrentMode = NewMode;
}
8.4.3 相机装备组合示例
// 创建一个组合相机装备:第三人称 + 远程瞄准
UCameraRigAsset* CreateAimingRig()
{
UCameraRigAsset* Rig = NewObject<UCameraRigAsset>();
UArrayCameraNode* Sequence = NewObject<UArrayCameraNode>(Rig);
Rig->RootNode = Sequence;
// 1. 附加到玩家
UAttachToPlayerPawnCameraNode* Attach = NewObject<UAttachToPlayerPawnCameraNode>(Sequence);
Sequence->Children.Add(Attach);
// 2. 瞄准偏移
UOffsetCameraNode* AimOffset = NewObject<UOffsetCameraNode>(Sequence);
AimOffset->LocationOffset.Value = FVector(-100, 50, 0); // 肩膀位置
Sequence->Children.Add(AimOffset);
// 3. 缩放 FOV
UFieldOfViewCameraNode* FOV = NewObject<UFieldOfViewCameraNode>(Sequence);
FOV->FieldOfView.Value = 45.0f; // 缩小 FOV 实现瞄准效果
Sequence->Children.Add(FOV);
// 4. 减少输入灵敏度
UInput2DCameraNode* Input = NewObject<UInput2DCameraNode>(Sequence);
Input->YawSpeed.Value = 30.0f;
Input->PitchSpeed.Value = 20.0f;
Sequence->Children.Add(Input);
// 5. 碰撞检测
UCollisionPushCameraNode* Collision = NewObject<UCollisionPushCameraNode>(Sequence);
Sequence->Children.Add(Collision);
return Rig;
}
9. 关键文件索引
9.1 核心评估系统
| 文件 | 说明 |
|---|---|
Public/Core/CameraSystemEvaluator.h |
相机系统评估器入口 |
Public/Core/CameraNodeEvaluator.h |
节点评估器基类 |
Public/Core/CameraNodeEvaluatorHierarchy.h |
评估器层次结构工具 |
Public/Core/CameraNode.h |
相机节点基类 |
Public/Core/CameraPose.h |
相机姿态数据结构 |
Public/Core/CameraVariableTable.h |
变量表(可混合参数) |
Public/Core/CameraContextDataTable.h |
上下文数据表(不可混合数据) |
Public/Core/CameraEvaluationContext.h |
评估上下文 |
Public/Core/CameraObjectRtti.h |
自定义 RTTI 系统 |
Public/Core/CameraObjectInterface.h |
对象接口系统 |
9.2 混合系统
| 文件 | 说明 |
|---|---|
Public/Core/BlendCameraNode.h |
混合节点基类 |
Public/Core/BlendStackCameraNode.h |
混合栈节点基类 |
Public/Core/PersistentBlendStackCameraNode.h |
持久化混合栈 |
Public/Core/TransientBlendStackCameraNode.h |
瞬态混合栈 |
Public/Core/RootCameraNode.h |
根节点基类 |
Public/Core/DefaultRootCameraNode.h |
默认根节点(四层架构) |
Public/Core/CameraRigTransition.h |
过渡系统 |
Public/Core/CameraValueInterpolator.h |
值插值器 |
9.3 节点实现
9.3.1 通用节点
Public/Nodes/Common/
├── ArrayCameraNode.h # Sequence 节点
├── BoomArmCameraNode.h # 吊臂节点
├── OffsetCameraNode.h # 偏移节点
├── SetLocationCameraNode.h # 设置位置
├── SetRotationCameraNode.h # 设置旋转
├── DampenPositionCameraNode.h # 位置阻尼
├── DampenRotationCameraNode.h # 旋转阻尼
├── FieldOfViewCameraNode.h # 视场角
├── ClippingPlanesCameraNode.h # 裁剪平面
├── PostProcessCameraNode.h # 后处理
├── AutoFocusCameraNode.h # 自动对焦
└── CameraRigCameraNode.h # 嵌套相机装备
9.3.2 混合节点
Public/Nodes/Blends/
├── SmoothBlendCameraNode.h # 平滑混合
├── LinearBlendCameraNode.h # 线性混合
├── PopBlendCameraNode.h # 立即切换
├── OrbitBlendCameraNode.h # 轨道混合
└── LocationRotationBlendCameraNode.h
9.3.3 其他节点
Public/Nodes/Attach/
├── AttachToPlayerPawnCameraNode.h
├── AttachToActorCameraNode.h
└── AttachToActorGroupCameraNode.h
Public/Nodes/Input/
├── Input2DCameraNode.h
├── Input1DCameraNode.h
└── AutoRotateInput2DCameraNode.h
Public/Nodes/Shakes/
├── EnvelopeShakeCameraNode.h
├── PerlinNoiseLocationShakeCameraNode.h
└── PerlinNoiseRotationShakeCameraNode.h
Public/Nodes/Framing/
├── DollyFramingCameraNode.h
└── PanningFramingCameraNode.h
Public/Nodes/Collision/
├── CollisionPushCameraNode.h
└── OcclusionMaterialCameraNode.h
9.4 游戏框架集成
| 文件 | 说明 |
|---|---|
Public/GameFramework/GameplayCameraComponent.h |
相机组件 |
Public/GameFramework/GameplayCameraActor.h |
相机 Actor |
Public/GameFramework/GameplayCameraRigActor.h |
相机装备 Actor |
Public/GameFramework/ActivateCameraRigFunctions.h |
激活函数库 |
Public/GameFramework/ControllerGameplayCameraEvaluationComponent.h |
控制器组件 |
Public/GameFramework/GameplayCameraSystemComponent.h |
系统组件 |
9.5 服务系统
| 文件 | 说明 |
|---|---|
Public/Core/CameraEvaluationService.h |
服务基类 |
Public/Services/CameraShakeService.h |
震动服务 |
Public/Services/CameraModifierService.h |
修饰符服务 |
Public/Services/CameraParameterSetterService.h |
参数设置服务 |
Public/Services/PlayerControlRotationService.h |
控制旋转服务 |
9.6 导演系统
| 文件 | 说明 |
|---|---|
Public/Core/CameraDirector.h |
导演基类 |
Public/Core/CameraDirectorEvaluator.h |
导演评估器 |
Public/Directors/SingleCameraDirector.h |
单一相机导演 |
Public/Directors/BlueprintCameraDirector.h |
蓝图导演 |
Public/Directors/PriorityQueueCameraDirector.h |
优先队列导演 |
Public/Directors/StateTreeCameraDirector.h |
状态树导演 |
9.7 资产系统
| 文件 | 说明 |
|---|---|
Public/Core/CameraAsset.h |
相机资产 |
Public/Core/CameraRigAsset.h |
相机装备资产 |
Public/Core/CameraShakeAsset.h |
震动资产 |
Public/Core/CameraRigProxyAsset.h |
代理资产 |
Public/Core/CameraVariableAssets.h |
变量资产 |
9.8 调试系统
| 文件 | 说明 |
|---|---|
Public/Debug/CameraDebugBlock.h |
调试块基类 |
Public/Debug/CameraDebugRenderer.h |
调试渲染器 |
Public/Debug/CameraDebugCategories.h |
调试分类 |
Public/Debug/BlendStacksCameraDebugBlock.h |
混合栈调试 |
9.9 构建系统
| 文件 | 说明 |
|---|---|
Public/Build/CameraAssetBuilder.h |
资产构建器 |
Public/Build/CameraRigAssetBuilder.h |
装备构建器 |
Public/Build/CameraBuildLog.h |
构建日志 |
Public/Build/CameraObjectBuildContext.h |
构建上下文 |
附录
A. 术语表
| 术语 | 英文 | 说明 |
|---|---|---|
| 相机资产 | Camera Asset | 顶层相机配置容器,包含导演、过渡、参数 |
| 相机装备 | Camera Rig | 可复用的相机行为定义,包含节点树 |
| 相机节点 | Camera Node | 最小功能单元,可组合实现复杂行为 |
| 评估器 | Evaluator | 节点的运行时逻辑实现 |
| 混合栈 | Blend Stack | 管理多个相机装备的容器,支持混合过渡 |
| 评估上下文 | Evaluation Context | 提供相机运行环境和数据的容器 |
| 导演 | Director | 控制相机装备激活/停用的逻辑 |
| 服务 | Service | 横切关注点,在评估前后执行 |
| 过渡 | Transition | 定义相机切换时的行为和条件 |
| 变量表 | Variable Table | 可混合的参数存储 |
| 上下文数据表 | Context Data Table | 不可混合的数据存储 |
| 相机姿态 | Camera Pose | 相机的完整状态描述 |
B. 参考资料
B.1 官方文档
B.2 源码位置
- 插件根目录:
Engine/Plugins/Cameras/GameplayCameras/ - 公共头文件:
Source/GameplayCameras/Public/ - 私有实现:
Source/GameplayCameras/Private/
B.3 相关模块
CameraCalibrationCore- 相机校准FilmStock- 胶片效果OpenColorIO- 色彩管理
本文档基于 Unreal Engine 5.6 GameplayCameras 插件源码编写
最后更新: 2026-02-16