自己动手写一个方法比分析他人的写的方法困难很多,由此而来的对程序的进一步理解也是分析别人的代码很难得到的。
一、先来几张效果图:
1、场景中有两个半径为1的球体,蓝色线段从球心出发指向球体的“正向”
2、物体被选中后改变纹理图片和透明度,可以使用“w、s、a、d、空格、ctrl”控制物体相对于物体的正向“前、后、左、右、上、下”移动,按住按键时间越长移动速度越快,绿色线段由球心指向物体运动方向,速度越快露出物体表面的部分越长,按“g”停止所有移动,再次点击物体取消选中状态。
3、可以选取多个物体同时移动。
4、两物体发生碰撞后停止移动,红色线段由物体球心指向运动方向上遇到的第一个其他物体
二、碰撞检测原理:
借用THREE.Raycaster来进行碰撞检测,因为Raycaster不能检测到物体的“内表面”,所以使用反射法。
三、程序实现:
完整程序代码可以在下载查看,其中包括详细注释,这里解释一下几个比较重要的段落。
1、绘制示意物体运动情况的线段
.line2;.line3;vector2=.line1=.line2=this.createLine2(this.object3D.position,this.object3D.position,0x00ff00,this.planetGroup,"line2"); 7 this.line3=this.createLine2(this.object3D.position,this.object3D.position,0xff0000,this.planetGroup,"line3");
其中点vector2由向量v0乘以2加上this.object3D.position得到,作为直线line1的结束点。注意v0后的“.clone()”如果去掉则v0本身也会应用这些变化,最终变得与vector2相同。
line2和line3被初始化为一个点。
这里建立的“line”对象并没有碰撞检测功能,纯粹是用来看的。
1 if (this.speedw != 0 || this.speeda != 0||this.speedc!=0) vector4=new THREE.Vector3(0,this.speedc*1000,0); 4 var vector3=(this.v1.clone().multiplyScalar(this.speeda*1000)).add(this.v0.clone().multiplyScalar(this.speedw*1000)).add(vector4); 5 var vector5=vector3.clone().normalize().multiplyScalar(this.size); 6 this.vector3=vector3.clone().add(vector5); .testCollision();( { 14 this.object3D.position.add(this.v0.clone().multiplyScalar(this.speedw)); 15 this.object3D.position.add(this.v1.clone().multiplyScalar(this.speeda)); } { 20 this.speeda=0; 21 this.speedc=0; 22 this.speedw=0; currentlyPressedKeys[65]=false; 25 currentlyPressedKeys[68]=false; 26 currentlyPressedKeys[87]=false; 27 currentlyPressedKeys[83]=false; 28 currentlyPressedKeys[32]=false; 29 currentlyPressedKeys[17]=false; 30 } 31 }
根据物体的运动情况更新line2的方向和长度。在这里vector3由物体在各个方向上的速度分量组合而成,vector5负责把vector3调整为适合显示的长度,this.vector3和vector3是不同的对象,表示line2一个端点的位移。
updateLine2更新line2的端点,表现出速度的变化。我们可以看到它的两个端点位置参数是(0,0,0)和一个向量而不是前面图中的球心位置和“球心位置加向量得到的点”,这体现出了Three.js父子物体间的相对性。(简单的碰撞示例原本不需要使用子物体,我是不是自讨苦吃?)
该示例延续自上一篇中的太阳系模型,物体间的关系如下图:
planetOrbitGroup和planetGroup是“Object3D”对象,这种“物体”没有大小、颜色属性只有位置和姿态属性(默认位于父物体的原点),“Mesh”和“Line”多重继承于Object3D具有顶点(geometry)、纹理(material)属性。
planetOrbitGroup是Scene的子物体位于世界坐标系原点,planetGroup是planetOrbitGroup的子物体强制定位于世界坐标系x=3处,globeMesh和line2是planetGroup的子物体默认位于planetGroup的原点。
当planetOrbitGroup移动时(改变this.object3D.position),这个移动效果会被它所有的后代对象继承,所以我们把line2的一个顶点设为(0,0,0)后会自动继承它所有祖先元素的移动效果(this.object3D.position+(3,0,0))。
2、基于射线的碰撞测试
raycaster.far=intersects = raycaster.intersectObjects(scene.children,true);