Skip to content

第 6.4 章:相机系统 ​


简介 ​

DayZ 使用多层相机系统。玩家相机由引擎通过 DayZPlayerCamera 子类管理。用于模组开发和调试时,FreeDebugCamera 可以实现自由飞行。引擎还提供了全局访问器来获取当前相机状态。本章介绍相机类型、如何访问相机数据以及如何使用脚本化相机工具。


当前相机状态(全局访问器) ​

这些方法在任何地方都可用,无论相机类型如何,都返回当前活动相机的状态:

c
// 当前相机世界位置
proto native vector GetGame().GetCurrentCameraPosition();

// 当前相机前方方向(单位向量)
proto native vector GetGame().GetCurrentCameraDirection();

// 将世界位置转换为屏幕坐标
proto native vector GetGame().GetScreenPos(vector world_pos);
// 返回值:x = 屏幕 X(像素),y = 屏幕 Y(像素),z = 深度(到相机的距离)

示例 --- 检查一个位置是否在屏幕上:

c
bool IsPositionOnScreen(vector worldPos)
{
    vector screenPos = GetGame().GetScreenPos(worldPos);

    // z < 0 表示在相机后面
    if (screenPos[2] < 0)
        return false;

    int screenW, screenH;
    GetScreenSize(screenW, screenH);

    return (screenPos[0] >= 0 && screenPos[0] <= screenW &&
            screenPos[1] >= 0 && screenPos[1] <= screenH);
}

示例 --- 获取相机到某点的距离:

c
float DistanceFromCamera(vector worldPos)
{
    return vector.Distance(GetGame().GetCurrentCameraPosition(), worldPos);
}

DayZPlayerCamera 系统 ​

DayZ 玩家相机是由引擎的玩家控制器管理的原生类。它们不能从脚本中直接实例化 --- 引擎会根据玩家的状态(站立、趴下、游泳、载具、昏迷等)自动选择合适的相机。

相机类型(DayZPlayerCameras 常量) ​

相机类型 ID 定义为常量:

常量描述
DayZPlayerCameras.DAYZCAMERA_1ST第一人称相机
DayZPlayerCameras.DAYZCAMERA_3RD_ERC第三人称直立(站立)
DayZPlayerCameras.DAYZCAMERA_3RD_CRO第三人称蹲下
DayZPlayerCameras.DAYZCAMERA_3RD_PRO第三人称趴下
DayZPlayerCameras.DAYZCAMERA_3RD_ERC_SPR第三人称冲刺
DayZPlayerCameras.DAYZCAMERA_3RD_ERC_RAISED第三人称举枪
DayZPlayerCameras.DAYZCAMERA_3RD_CRO_RAISED第三人称蹲下举枪
DayZPlayerCameras.DAYZCAMERA_IRONSIGHTS机械瞄准
DayZPlayerCameras.DAYZCAMERA_OPTICS光学/瞄准镜瞄准
DayZPlayerCameras.DAYZCAMERA_3RD_VEHICLE第三人称载具
DayZPlayerCameras.DAYZCAMERA_1ST_VEHICLE第一人称载具
DayZPlayerCameras.DAYZCAMERA_1ST_UNCONSCIOUS第一人称昏迷
DayZPlayerCameras.DAYZCAMERA_3RD_CLIMB第三人称攀爬
DayZPlayerCameras.DAYZCAMERA_3RD_JUMP第三人称跳跃

获取当前相机类型 ​

c
DayZPlayer player = GetGame().GetPlayer();
if (player)
{
    DayZPlayerCamera cam = player.GetCurrentCamera();
    if (cam && cam.GetCameraName() == "DayZPlayerCamera1stPerson")
    {
        Print("Player is in first person");
    }
}

FreeDebugCamera ​

文件: 3_Game/entities/camera.c

用于调试和影视制作的自由飞行相机。在诊断版本中可用,或通过模组启用。

获取实例 ​

c
static proto native FreeDebugCamera GetInstance();

此静态方法返回自由相机的单例实例(如果不存在则返回 null)。请通过 FreeDebugCamera.GetInstance() 调用它。

主要方法 ​

c
// 启用/禁用自由相机(继承自 Camera)
proto native void SetActive(bool active);
proto native bool IsActive();

// 位置和朝向
vector GetPosition();
void   SetPosition(vector pos);
vector GetOrientation();
void   SetOrientation(vector ori);   // 偏航、俯仰、翻滚

// 相机方向
vector GetDirection();

示例 --- 激活自由相机并传送:

c
void ActivateDebugCamera(vector pos)
{
    FreeDebugCamera cam = FreeDebugCamera.GetInstance();
    if (cam)
    {
        cam.SetActive(true);
        cam.SetPosition(pos);
        cam.SetOrientation(Vector(0, -30, 0));  // 略微向下看
    }
}

视场角(FOV) ​

引擎原生控制 FOV。你可以通过玩家相机系统读取和修改它:

读取 FOV ​

c
// 获取当前相机 FOV(弧度)
float fov = Camera.GetCurrentFOV();

DayZPlayerCamera FOV 覆盖 ​

在继承自 DayZPlayerCamera 的自定义相机类中,你可以覆盖 FOV:

c
class MyCustomCamera extends DayZPlayerCamera1stPerson
{
    override void OnUpdate(float pDt, out DayZPlayerCameraResult pOutResult)
    {
        super.OnUpdate(pDt, pOutResult);
        pOutResult.m_fFovAbsolute = 0.7854;  // 约 45 度(弧度)
    }
}

景深(DOF) ​

景深通过后处理效果系统控制(见第 6.5 章)。然而,相机系统通过以下机制与 DOF 配合工作:

通过 CGame 设置 DOF ​

c
// OverrideDOF(enable, focusDistance, focusLength, focusLengthNear, blur, focusDepthOffset)
GetGame().OverrideDOF(true, 5.0, 100.0, 0.5, 0.3, 0.0);

禁用 DOF ​

c
GetGame().OverrideDOF(false, 0, 0, 0, 0, 1);  // 第一个参数为 false 可禁用 DOF

ScriptCamera(GameLib) ​

文件: 2_GameLib/entities/scriptcamera.c

GameLib 层的底层脚本化相机实体。这是自定义相机实现的基类。

创建相机 ​

c
ScriptCamera camera = ScriptCamera.Cast(
    GetGame().CreateObject("ScriptCamera", pos, true)  // 仅本地
);

主要方法 ​

注意: ScriptCamera 仅在 #ifdef GAME_TEMPLATE 下编译,DayZ 游戏构建中不存在。它通过 GenericEntity 的相机方法(SetCameraVerticalFOV、SetCameraNearPlane、SetCameraFarPlane、SetCameraType)进行自我配置。下面的方法属于引擎 Camera 类(3_Game/entities/camera.c),FreeDebugCamera 继承自该类:

c
proto native void SetFOV(float fov);                 // FOV(弧度)
proto native void SetNearPlane(float nearPlane);     // 内部钳制到 0.01m
proto native float GetNearPlane();
proto native void SetFocus(float distance, float blur);  // 景深

激活相机 ​

c
// 使此相机成为活动渲染相机
GetGame().SelectPlayer(null, null);   // 从玩家分离
GetGame().ObjectRelease(camera);      // 释放给引擎

注意: 从玩家相机切换出去需要仔细处理输入和 HUD。大多数模组使用自由调试相机或 PPE 叠加效果,而不是创建自定义相机。


从相机射线检测 ​

一个常见模式是从相机位置沿相机方向进行射线检测,以找到玩家正在看的物体:

c
Object GetObjectInCrosshair(float maxDistance)
{
    vector from = GetGame().GetCurrentCameraPosition();
    vector to = from + (GetGame().GetCurrentCameraDirection() * maxDistance);

    vector contactPos;
    vector contactDir;
    int contactComponent;
    set<Object> hitObjects = new set<Object>;

    if (DayZPhysics.RaycastRV(from, to, contactPos, contactDir,
                               contactComponent, hitObjects, null, null,
                               false, false, ObjIntersectView, 0.0))
    {
        if (hitObjects.Count() > 0)
            return hitObjects[0];
    }

    return null;
}

总结 ​

概念要点
全局访问器GetCurrentCameraPosition(), GetCurrentCameraDirection(), GetScreenPos()
相机类型DayZPlayerCameras 常量(1ST, 3RD_ERC, IRONSIGHTS, OPTICS, VEHICLE 等)
当前类型player.GetCurrentCamera().GetCameraName()
自由相机FreeDebugCamera.GetInstance(),然后 cam.SetActive(true)
FOVCamera.GetCurrentFOV() 读取,在 OnUpdate 中设置 pOutResult.m_fFovAbsolute
DOFGetGame().OverrideDOF(enable, focusDist, focusLen, focusLenNear, blur, offset)
屏幕转换GetScreenPos(worldPos) 返回像素 XY + 深度 Z

最佳实践 ​

  • 在一帧内多次查询时缓存相机位置。 GetGame().GetCurrentCameraPosition() 和 GetCurrentCameraDirection() 是引擎调用 -- 如果在同一帧内多次计算需要使用,请将结果存储在局部变量中。
  • 在放置 UI 前使用 GetScreenPos() 深度检查。 在世界位置绘制 HUD 标记之前,始终验证 screenPos[2] > 0(在相机前方),否则标记会镜像显示在玩家身后。
  • 避免为简单效果创建自定义 ScriptCamera 实例。 FreeDebugCamera 和 PPE 系统涵盖了大多数影视和视觉需求。自定义相机需要仔细的输入/HUD 管理,容易出问题。
  • 尊重引擎的相机类型切换。 除非你完全处理了玩家控制器状态,否则不要从脚本强制切换相机类型。意外的相机切换可能导致玩家移动锁定或不同步。
  • 将自由相机使用限制在管理员/调试检查之后。 FreeDebugCamera 提供了类似上帝视角的世界检查功能。仅为经过验证的管理员或诊断版本启用它,以防止滥用。

兼容性与影响 ​

  • 多模组: 相机访问器是只读全局函数,因此多个模组可以安全地同时读取相机状态。仅当两个模组都尝试激活 FreeDebugCamera 或自定义 ScriptCamera 实例时才会产生冲突。
  • 性能: GetScreenPos() 和 GetCurrentCameraPosition() 是轻量级引擎调用。从相机进行射线检测(DayZPhysics.RaycastRV)更耗费资源 -- 限制为每帧一次,而不是每个实体一次。
  • 服务端/客户端: 相机状态仅存在于客户端。所有相机方法在专用服务器上返回无意义的数据。切勿在服务端逻辑中使用相机查询。

<< 上一章:天气 | 相机 | 下一章:后处理效果 >>

Released under CC BY-SA 4.0 | Code examples under MIT License