gitee | github
概要
如视频所示,这是个控制摄像机的组件。
摄像机过于贴近其他物体的时候,摄像机就会像弹簧一样被挤压到一起,避免摄像机穿模的尴尬情况
可以对比一下不使用弹簧臂的情况
使用
为了逻辑解耦和易用,功能被我封装成了俩组件
弹簧臂组件
每个参数都有详细的注释以及 Tooltip 方便快速上手
把弹簧臂组件挂载到玩家角色身上,设置跟随目标和摄像机,这个目标通常是角色的头
使用调试组件,在编辑器里调整弹簧臂参数能够实时预览
弹簧臂调试组件
调试组件在我的示例项目中,是挂载到了旁观者角色身上。
这样运行时按 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 引擎组把绘制射线安排上