threejs 实现一个简易的泊车
简易版小车 因为之前用的模型比较大,加载很慢,这里就先自己简单实现一辆小车(后面统称自车),如下图:
将车轮、车体和边框组合成一个 Group,便于后面做自车的一些操作,实现代码如下:
//自车车体
constgeometry=newTHREE.BoxGeometry(2,0.6,3);
constmaterial=newTHREE.MeshBasicMaterial({
color:0x00ffff,
side:THREE.DoubleSide,
});
constvehicle=newTHREE.Mesh(geometry,material);
vehicle.position.set(0,1,0);
scene.add(vehicle);
//增加自车边框
constbox=geometry.clone();
constedges=newTHREE.EdgesGeometry(box);
constedgesMaterial=newTHREE.LineBasicMaterial({
color:0x333333,
});
constline=newTHREE.LineSegments(edges,edgesMaterial);
line.position.x=0;
line.position.y=1;
line.position.z=0;
scene.add(line);
//组成一个Group
constegoCar=newTHREE.Group();
egoCar.name="自车";
egoCar.add(vehicle,line);
scene.add(egoCar);
//车轮
constaxlewidth=0.7;
constradius=0.4;
constwheels:any[]=
constwheelObjects:any[]=
wheels.push({position:[axlewidth,0.4,-1],radius
wheels.push({
position:[-axlewidth,0.4,-1],
radius,
});
wheels.push({position:[axlewidth,0.4,1],radius
wheels.push({position:[-axlewidth,0.4,1],radius
wheels.forEach(function(wheel){
constgeometry=newTHREE.CylinderGeometry(
wheel.radius,
wheel.radius,
0.4,
32
constmaterial=newTHREE.MeshPhongMaterial({
color:0xd0901d,
emissive:0xee0000,
side:THREE.DoubleSide,
flatShading:true,
constcylinder=newTHREE.Mesh(geometry,material);
cylinder.geometry.rotateZ(Math.PI/2);
cylinder.position.set(
wheel.position[0],
wheel.position[1],
wheel.position[2]
egoCar.add(cylinder);
//后面修改车轮方向会用到
wheelObjects.push(cylinder);
});
跟车相机 让相机一直跟着自车,体验更好一点
//...
constcamera=newTHREE.PerspectiveCamera(45,width/height,0.1,800);
//设置摄像机位置,并将其朝向场景中心
camera.position.x=0;
//camera.position.y=
//camera.position.z=
//camera.lookAt(scene.position);
camera.lookAt(egoCar.position);
//...
functionanimate(){
stats.begin();
controls.update();
//相机跟随自车
camera.position.y=egoCar.position.y+15;
camera.position.z=egoCar.position.z+25;
camera.lookAt(egoCar.position);
renderer.render(scene,camera);
stats.end();
requestAnimationFrame(animate);
}
//...
自车行驶 实现自车前行后退和左右转向
//...
//记录开始按下的时间
letstartTime=0;
constactiveKeys=newSet();
lett=0;
document.addEventListener("keydown",(e)={
activeKeys.add(e.key);
if(startTime===0){
startTime=Date.now();
}
t=(Date.now()-startTime)/1000;
if(t10){
t=10;
}
});
document.addEventListener("keyup",(e)={
activeKeys.delete(e.key);
if(activeKeys.size===0){
startTime=0;
}
});
functionanimate(){
stats.begin();
controls.update();
//相机跟随自车
camera.position.y=egoCar.position.y+15;
camera.position.z=egoCar.position.z+25;
camera.lookAt(egoCar.position);
if(activeKeys.has("ArrowUp")){
//估算对应方向的移动距离
egoCar.position.z-=t*0.1*Math.cos(egoCar.rotation.y);
egoCar.position.x-=t*0.1*Math.sin(egoCar.rotation.y);
}
if(activeKeys.has("ArrowDown")){
egoCar.position.z+=t*0.1*Math.cos(egoCar.rotation.y);
egoCar.position.x+=t*0.1*Math.sin(egoCar.rotation.y);
}
if(activeKeys.has("ArrowLeft")){
egoCar.rotation.y+=0.01;
}
if(activeKeys.has("ArrowRight")){
egoCar.rotation.y-=0.01;
}
renderer.render(scene,camera);
stats.end();
requestAnimationFrame(animate);
}
//...
车轮转动 遍历车轮对象,动态修改车轮的偏转角 rotation,以车头方向为基准偏转固定的角度
functionanimate(){
//...
if(activeKeys.has("ArrowLeft")){
egoCar.rotation.y+=0.01;
wheelObjects.forEach((wheel)={
wheel.rotation.y=egoCar.rotation.y+Math.PI/4;
}
if(activeKeys.has("ArrowRight")){
egoCar.rotation.y-=0.01;
wheelObjects.forEach((wheel)={
wheel.rotation.y=egoCar.rotation.y-Math.PI/4;
}
//...
}
行进效果还是有点僵硬(能用就行),这里的问题是行进方向应该是按车头方向,而不是固定按某个坐标轴方向,不过这里也只是简单模拟这个行进效果,后面再引入物理库 cannon.js优化下这块控制逻辑
泊车功能 车位实现做一个贴地面的矩形框来模拟车位,可以使用 THREE.PlaneGeometry 来创建平面几何体
createParkingSpace(){
constplane=newTHREE.PlaneGeometry(8,5);
constmaterial=newTHREE.MeshPhongMaterial({
color:0x666666,
side:THREE.DoubleSide,
constmesh=newTHREE.Mesh(plane,material);
mesh.rotation.x=-Math.PI/2;
mesh.position.set(10,0.12,-20);
this.scene?.add(mesh);
//增加自定义type,便于后面处理车位的选中逻辑
mesh.userData.type="parkingSpace";
}
现在咱们把小车开过去停到那个位置
自动泊车需要实现点击车位后高亮对应的车位,之后小车自动行驶到对应的位置并停好。点击原理是用射线的方式采集第一个碰到的车位物体,当点击鼠标时,会发生以下步骤:
基于屏幕上的点击位置创建一个 THREE.Vector3 向量使用 vector.unproject 方法将屏幕上点击位置的坐标转换成 three.js 场景中的坐标创建 THREE.Raycaster可以从摄像机的位置向场景中鼠标的点击位置发出一条射线raycaster.intersectObjects 返回包含了所有被射线穿过的对象信息的数组(从摄像机位置开始由短到长)functionhandleParkSpaceClick(event:any){
letvector=newTHREE.Vector3(
(event.clientX/window.innerWidth)*2-1,
-(event.clientY/window.innerHeight)*2+1,
0.5
vector=vector.unproject(camera);
constraycaster=newTHREE.Raycaster(
camera.position,
vector.sub(camera.position).normalize()
constintersects=raycaster.intersectObjects(scene.children);
for(leti=0;iintersects.length;i++){
constobj=intersects[i];
//@ts-ignore
if(obj.object.userData.type==="parkingSpace")
//@ts-ignore
obj.object.material.color.set(0x00ff00);
}
}
document.addEventListener("click",handleParkSpaceClick);
自动泊车的实现逻辑也比较简单,这里简单记住了车位的位置信息,然后让小车按一定的偏移驶入,其实实际场景可能还要考虑躲避障碍物、加减速、偏转角等,一般也不由前端操心这些。
实现代码参考 three-gta v0.1.1 -- 在线体验
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线