V7投稿|3D 摄像机弹簧臂组件

预览视频

gitee | github

概要

如视频所示,这是个控制摄像机的组件。

摄像机过于贴近其他物体的时候,摄像机就会像弹簧一样被挤压到一起,避免摄像机穿模的尴尬情况

可以对比一下不使用弹簧臂的情况

弹簧臂无碰撞

使用

为了逻辑解耦和易用,功能被我封装成了俩组件

弹簧臂组件

每个参数都有详细的注释以及 Tooltip 方便快速上手

弹簧臂组件

把弹簧臂组件挂载到玩家角色身上,设置跟随目标和摄像机,这个目标通常是角色的头

image

使用调试组件,在编辑器里调整弹簧臂参数能够实时预览

弹簧臂编辑器实时预览

弹簧臂调试组件

弹簧臂调试组件

调试组件在我的示例项目中,是挂载到了旁观者角色身上。

这样运行时按 Q 键切换到旁观者身上,以自由视角观察玩家位置方便调试

挂载弹簧臂调试组件

弹簧臂调试旁观者视角

其他

比如输入系统是我开源的另外一个仓库了,感兴趣可以去翻翻 gitee

实现原理

计算位置

弹簧臂计算摄像机位置分三段

第一段是目标偏移(targetOffset)

角色位置加上偏移得到目标偏移位置

let worldTargetOffset = Vec3.transformMat4(v3(), this.targetOffset, rotation);
this.targetOffsetPosition = Vec3.add(v3(), trans.worldPosition, worldTargetOffset);

第二段是弹簧臂终点

弹簧臂长度乘以角色背后方向得到弹簧臂尺寸

目标偏移点加弹簧臂尺寸得到弹簧臂终点

let v3_1 = Vec3.negate(v3(), trans.forward);
let v3_2 = Vec3.multiplyScalar(v3(), v3_1, this.armLength);
let v3_3 = Vec3.add(v3(), v3_2, this.targetOffsetPosition);

第三段是插槽偏移点

弹簧臂终点加插槽偏移得到插槽偏移点

let worldSocketOffset = Vec3.transformMat4(v3(), this.socketOffset, rotation);
this.socketOffsetPosition = Vec3.add(v3(), v3_3, worldSocketOffset);

计算碰撞

组件通过射线扫掠的方式,检测摄像机跟场景中其他物体的碰撞。

射线扫掠是 Creator 3.8 新增的一套射线 API 。

能够使用一个形状往指定的方向扫掠过去,过程中形成的轨迹碰撞到物体就会被记录。

const phySys = PhysicsSystem.instance;

let sweepRay = geometry.Ray.create(
    this.targetOffsetPosition.x,
    this.targetOffsetPosition.y,
    this.targetOffsetPosition.z,
    this.raycastDirection.x,
    this.raycastDirection.y,
    this.raycastDirection.z
);

let isSweep = phySys.sweepSphereClosest(
    sweepRay,
    this.collisionProbeSize,
    this.collisionLayerMask,
    this.armLength,
    false
);

扫掠检测到碰撞后,再做一次从目标偏移点到插槽偏移点的射线检测,把这次碰撞到的位置设置为摄像机位置

这么做是因为扫掠是一个几何形状,碰撞的地方可能是几何体边缘,摄像机的位置应该在几何体正中心

if (isSweep) {
    let ray = geometry.Ray.create(
        this.targetOffsetPosition.x,
        this.targetOffsetPosition.y,
        this.targetOffsetPosition.z,
        this.raycastDirection.x,
        this.raycastDirection.y,
        this.raycastDirection.z
    );

    if (phySys.raycastClosest(
        ray,
        this.collisionLayerMask,
        this._armLength,
        false
    )) {
        this.socketOffsetPosition = phySys.raycastClosestResult.hitPoint;
    }
}

弹簧臂调试

使用了引擎自带组件 Line

设置完了基础参数后,使用目标偏移点和插槽偏移点绘制出来一条线

暂时扫掠轨迹无法绘制,这个估计要等引擎完善射线绘制才会添加

let line = this.getComponent(Line);
line.worldSpace = true;
line.width.mode = CurveRange.Mode.Constant;
line.width.constant = 0.05;
line.color.mode = GradientRange.Mode.Color;
line.color.color = math.Color.RED;

line.positions = [
    this.springArm.targetOffsetPosition,
    this.springArm.socketOffsetPosition
];

愿景

后续会继续完善,加上位置和旋转延迟,支持更多情景的角色摄像机需求。

仓库接受社区提交代码,期待大家的 PR。

同时希望 Creator 引擎组把绘制射线安排上

11赞

大佬牛皮。

不错 有用

:grinning:

更新了摄像机移动、旋转滞后,还得再优化优化

赞,感觉很实用,请问我能不能添加到我框架的扩展包里面

可以的,保留原仓库信息就行 :grin:

:ok_hand:哈哈,那必须要给出你源地址

牛逼666666666666