上节实现了鼠标拖动来发球:

基于鼠标拖动的距离、方向来确定保龄球发球的方向、力度。
但发球的过程并没有可视化的展示,不知道现在力度有多大。
我们加一下可视化的展示。
方式很简单,就是根据拖动的方向、距离绘制一条线就行。
基于 2 个顶点画一条线,用 BufferGeomety + Line 就行。

let dragLine = null;
function createDragLine() {
const lineGeometry = new THREE.BufferGeometry();
const lineMaterial = new THREE.LineBasicMaterial({
color: 0xffff00,
transparent: true
});
dragLine = new THREE.Line(lineGeometry, lineMaterial);
dragLine.visible = false;
group.add(dragLine);
}
createDragLine();
刚开始是隐藏的,也没指定顶点。
拖拽的时候把它显示出来,并更新下顶点:

if (dragLine) dragLine.visible = true;
updateDragLine();
if (dragLine) dragLine.visible = false;
鼠标按下,显示辅助线。
鼠标拖动,更新辅助线位置、方向。
鼠标抬起,隐藏辅助线。
然后实现下这个 updateDragLine 方法:

function updateDragLine() {
if (!dragLine || !ball.mesh) return;
const dragVector = new THREE.Vector2().subVectors(dragEnd, dragStart);
const dragDistance = dragVector.length(); // 拖拽距离
// 计算力度:距离越大力度越大,最大 2000
const force = Math.min(dragDistance * 500, 2000);
// 线的起点是球的位置
const startPos = ball.mesh.position.clone();
// 计算发射方向:
const forwardComponent = Math.max(0.1, -dragVector.y); // 确保有向前的力
const sideComponent = dragVector.x; // 左右方向
// 计算3D方向向量(在XZ平面上)
// 系数1.5和1.2用于调整方向控制的灵敏度
const direction = new THREE.Vector3(
sideComponent * 1.5, // X方向(左右)
0, // Y方向(不向上发射)
-forwardComponent * 1.2 - 0.5 // Z方向(向前,负值)
).normalize();
// 线的长度与力度成正比
const lineLength = force * 0.15;
const endPos = startPos.clone().add(direction.multiplyScalar(lineLength));
// 更新线的几何体顶点位置
const positions = new Float32Array([
startPos.x, startPos.y, startPos.z,
endPos.x, endPos.y, endPos.z
]);
dragLine.geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
dragLine.geometry.attributes.position.needsUpdate = true;
}
其实和发球的计算逻辑差不多。
首先用两个位置的差,算出拖动的距离。
根据距离计算力度。
根据拖动向量的方向计算绘制线的方向。
基于力和方向来绘制线。
把两个顶点位置设置到 BufferGeometry,然后设置下 needsUpdate 提示更新了顶点位置就可以了。
试下效果:

这样,就可以可视化的看到拖动的距离了,直观感受到方向、力度。
因为我们是用圆柱作为保龄球瓶的物理刚体,和它真实的形状有点差距,所以效果有时候不不太好:

在真实项目里,可以用 blender 之类的软件画一些顶点,然后代码里基于这些顶点生成自定义形状的凸多面体就好了。
案例代码上传了小册仓库
总结
这节我们添加了发球的辅助线。
基于 BufferGeometry + Line 来绘制线,只要确定两个顶点位置就行。
一个顶点位置是球的位置,另一个根据鼠标拖拽来确定。
根据开始、结束位置的向量,计算出辅助线的方向、长度,这样就确定了另一个顶点位置,然后把线画出来。
这样,发球过程就更直观了。