【可视化学习】47-cannon-es的简单使用(二)
发表于:2023-09-28 |

前言

本篇文章内容会比较少,是继续上文的效果讲解,你问我为什么不在一起写,因为我学习的时候因为有事没一起学,间隔了一段时间,如果想要有更好的体验,就先看上一篇文章,再来看这篇文章就好了

流体效果

这里我们创建下基础代码

基础代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<template>
<div></div>
</template>

<script setup>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import * as CANNON from "cannon-es";
// 初始化物理世界
const world = new CANNON.World();
// 设置重力
world.gravity.set(0, -9.82, 0);

// 初始化3D场景
const scene = new THREE.Scene();
// 初始化相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 10, 20);
camera.lookAt(0, 0, 0);

// 初始化渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

let phyMeshes = [];
let meshes = [];

// 渲染
let clock = new THREE.Clock();
function animate() {
let delta = clock.getDelta();
world.step(1 / 60, delta);

for (let i = 0; i < phyMeshes.length; i++) {
meshes[i].position.copy(phyMeshes[i].position);
meshes[i].quaternion.copy(phyMeshes[i].quaternion);
}

controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}

animate();
</script>

<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

canvas {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
}
</style>

添加五面立方体

创建地面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建地面
const groundShape = new CANNON.Plane();
const groundBody = new CANNON.Body({
mass: 0,
shape: groundShape,
material: new CANNON.Material(),
});
groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
world.addBody(groundBody);
phyMeshes.push(groundBody);

const groundMesh = new THREE.Mesh(
new THREE.PlaneGeometry(10, 10),
new THREE.MeshBasicMaterial({
color: 0x666666,
})
);
groundMesh.rotation.x = -Math.PI / 2;
scene.add(groundMesh);
meshes.push(groundMesh);

效果图

创建其他四面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
 // 创建前后左右四个平面
const planeShape = new CANNON.Plane();
const planeBody1 = new CANNON.Body({
mass: 0,
shape: planeShape,
material: new CANNON.Material(),
});
planeBody1.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), -Math.PI / 2);
planeBody1.position.set(5, 5, 0);
world.addBody(planeBody1);
phyMeshes.push(planeBody1);

const planeMesh1 = new THREE.Mesh(
new THREE.PlaneGeometry(10, 10),
new THREE.MeshBasicMaterial({
color: 0x666666,
wireframe: true,
})
);
planeMesh1.position.copy(planeBody1.position);
planeMesh1.quaternion.copy(planeBody1.quaternion);
scene.add(planeMesh1);
meshes.push(planeMesh1);

const planeBody2 = new CANNON.Body({
mass: 0,
shape: planeShape,
material: new CANNON.Material(),
});
planeBody2.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), Math.PI / 2);
planeBody2.position.set(-5, 5, 0);
world.addBody(planeBody2);
phyMeshes.push(planeBody2);

const planeMesh2 = new THREE.Mesh(
new THREE.PlaneGeometry(10, 10),
new THREE.MeshBasicMaterial({
color: 0x666666,
wireframe: true,
})
);
planeMesh2.position.copy(planeBody2.position);
planeMesh2.quaternion.copy(planeBody2.quaternion);
scene.add(planeMesh2);
meshes.push(planeMesh2);

const planeBody3 = new CANNON.Body({
mass: 0,
shape: planeShape,
material: new CANNON.Material(),
});
planeBody3.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), Math.PI);
planeBody3.position.set(0, 5, 5);
world.addBody(planeBody3);
phyMeshes.push(planeBody3);

const planeMesh3 = new THREE.Mesh(
new THREE.PlaneGeometry(10, 10),
new THREE.MeshBasicMaterial({
color: 0x666666,
wireframe: true,
})
);
planeMesh3.position.copy(planeBody3.position);
planeMesh3.quaternion.copy(planeBody3.quaternion);
scene.add(planeMesh3);
meshes.push(planeMesh3);

const planeBody4 = new CANNON.Body({
mass: 0,
shape: planeShape,
material: new CANNON.Material(),
});
planeBody4.position.set(0, 5, -5);

world.addBody(planeBody4);
phyMeshes.push(planeBody4);

const planeMesh4 = new THREE.Mesh(
new THREE.PlaneGeometry(10, 10),
new THREE.MeshBasicMaterial({
color: 0x666666,
wireframe: true,
})
);
planeMesh4.position.copy(planeBody4.position);
planeMesh4.quaternion.copy(planeBody4.quaternion);
scene.add(planeMesh4);
meshes.push(planeMesh4);

效果图

添加流体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 创建SPH流体系统
const sphSystem = new CANNON.SPHSystem();
// 流体密度
sphSystem.density = 1;
// 流体粘度
sphSystem.viscosity = 0.01;
// 流体交互距离
sphSystem.smoothingRadius = 1;
// 将流体系统添加到世界中
world.subsystems.push(sphSystem);

// 创建流体粒子
const particleShape = new CANNON.Particle();
const sphereGeometry = new THREE.SphereGeometry(0.1, 10, 10);
const sphereMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000,
});
const particleMaterial = new CANNON.Material();
for (let i = 0; i < 400; i++) {
const particleBody = new CANNON.Body({
mass: 0.01,
shape: particleShape,
material: particleMaterial,
});
particleBody.position.set(Math.random() * -0.5, 10 + i, Math.random() * -0.5);
world.addBody(particleBody);
phyMeshes.push(particleBody);
// 将粒子添加到流体系统中
// sphSystem.particles.push(particleBody);
sphSystem.add(particleBody);

// threejs小球
const particleMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
particleMesh.position.copy(particleBody.position);
scene.add(particleMesh);
meshes.push(particleMesh);
}

刚体车辆效果

基础代码保持不变

创建地面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建地面
const groundShape = new CANNON.Plane();
const groundBody = new CANNON.Body({
mass: 0,
shape: groundShape,
material: new CANNON.Material(),
});
groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
world.addBody(groundBody);
phyMeshes.push(groundBody);

const groundMesh = new THREE.Mesh(
new THREE.PlaneGeometry(100, 100),
new THREE.MeshBasicMaterial({
color: 0x666666,
})
);
groundMesh.rotation.x = -Math.PI / 2;
scene.add(groundMesh);
meshes.push(groundMesh);

效果图

创建车身

这里我们不把他直接加到物理世界去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 创建车身
const chassisShape = new CANNON.Box(new CANNON.Vec3(5, 0.5, 2));
const chassisBody = new CANNON.Body({
mass: 100,
shape: chassisShape,
});
chassisBody.position.set(0, 5, 0);
// world.addBody(chassisBody);
phyMeshes.push(chassisBody);

// three.js的车身
const chassisMesh = new THREE.Mesh(
new THREE.BoxGeometry(10, 1, 4),
new THREE.MeshBasicMaterial({
color: 0x660066,
})
);
scene.add(chassisMesh);
meshes.push(chassisMesh);

效果图

创建车轮以及将整车添加到物理世界

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// 创建轮子
// 创建车轮形状
// const wheelShape = new CANNON.Cylinder(0.5, 0.5, 1, 20);
const wheelShape = new CANNON.Sphere(1.5);
const wheelBody1 = new CANNON.Body({
mass: 1,
shape: wheelShape,
});
vehicle.addWheel({
body: wheelBody1,
// 轮子位置
position: new CANNON.Vec3(-4, -0.5, 3.5),
// 旋转轴
axis: new CANNON.Vec3(0, 0, -1),
direction: new CANNON.Vec3(0, -1, 0),
});
// world.addBody(wheelBody1);
phyMeshes.push(wheelBody1);

// three.js的车轮
const wheelMaterial = new THREE.MeshBasicMaterial({
color: 0x660000,
wireframe: true,
});
const wheelGeometry = new THREE.SphereGeometry(1.5, 8, 8);
const wheelMesh1 = new THREE.Mesh(
new THREE.SphereGeometry(1.5, 8, 8),
wheelMaterial
);
scene.add(wheelMesh1);
meshes.push(wheelMesh1);

const wheelBody2 = new CANNON.Body({
mass: 1,
shape: wheelShape,
});
vehicle.addWheel({
body: wheelBody2,
// 轮子位置
position: new CANNON.Vec3(4, -0.5, 3.5),
// 旋转轴
axis: new CANNON.Vec3(0, 0, -1),
direction: new CANNON.Vec3(0, -1, 0),
});
// world.addBody(wheelBody2);
phyMeshes.push(wheelBody2);

// three.js的车轮
const wheelMesh2 = new THREE.Mesh(
new THREE.SphereGeometry(1.5, 8, 8),
wheelMaterial
);
scene.add(wheelMesh2);
meshes.push(wheelMesh2);

const wheelBody3 = new CANNON.Body({
mass: 1,
shape: wheelShape,
});
vehicle.addWheel({
body: wheelBody3,
// 轮子位置
position: new CANNON.Vec3(-4, -0.5, -3.5),
// 旋转轴
axis: new CANNON.Vec3(0, 0, -1),
direction: new CANNON.Vec3(0, -1, 0),
});
// world.addBody(wheelBody3);
phyMeshes.push(wheelBody3);

// three.js的车轮
const wheelMesh3 = new THREE.Mesh(
new THREE.SphereGeometry(1.5, 8, 8),
wheelMaterial
);
scene.add(wheelMesh3);
meshes.push(wheelMesh3);

const wheelBody4 = new CANNON.Body({
mass: 1,
shape: wheelShape,
});
vehicle.addWheel({
body: wheelBody4,
// 轮子位置
position: new CANNON.Vec3(4, -0.5, -3.5),
// 旋转轴
axis: new CANNON.Vec3(0, 0, -1),
direction: new CANNON.Vec3(0, -1, 0),
});
// world.addBody(wheelBody4);
phyMeshes.push(wheelBody4);

// three.js的车轮
const wheelMesh4 = new THREE.Mesh(
new THREE.SphereGeometry(1.5, 8, 8),
wheelMaterial
);
scene.add(wheelMesh4);
meshes.push(wheelMesh4);

// 设置完毕之后,将车子添加到世界中
vehicle.addToWorld(world);

控制车子运动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 控制车子
window.addEventListener("keydown", (event) => {
console.log(event.key);
if (event.key === "w") {
// setWheelForce(力,轮子编号)
vehicle.setWheelForce(-100, 0);
vehicle.setWheelForce(-100, 2);
}
if (event.key === "s") {
vehicle.setWheelForce(100, 0);
vehicle.setWheelForce(100, 2);
}
if (event.key == "a") {
vehicle.setSteeringValue(Math.PI / 4, 0);
vehicle.setSteeringValue(Math.PI / 4, 2);
}
if (event.key == "d") {
vehicle.setSteeringValue(-Math.PI / 4, 0);
vehicle.setSteeringValue(-Math.PI / 4, 2);
}
});
window.addEventListener("keyup", (event) => {
if (event.key === "w") {
vehicle.setWheelForce(0, 0);
vehicle.setWheelForce(0, 2);
}
if (event.key === "s") {
vehicle.setWheelForce(0, 0);
vehicle.setWheelForce(0, 2);
}
if (event.key == "a") {
vehicle.setSteeringValue(0, 0);
vehicle.setSteeringValue(0, 2);
}
if (event.key == "d") {
vehicle.setSteeringValue(0, 0);
vehicle.setSteeringValue(0, 2);
}
});

悬架车辆效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
<template>
<div></div>
</template>

<script setup>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import * as CANNON from "cannon-es";
// 初始化物理世界
const world = new CANNON.World();
// 设置重力
world.gravity.set(0, -9.82, 0);

// Sweep and prune broadphase
world.broadphase = new CANNON.SAPBroadphase(world);

// Disable friction by default
world.defaultContactMaterial.friction = 0;

// 初始化3D场景
const scene = new THREE.Scene();
// 初始化相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 10, 20);
camera.lookAt(0, 0, 0);

// 初始化渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

let phyMeshes = [];
let meshes = [];

// 创建地面
// const groundShape = new CANNON.Plane();
const groundPhyMaterial = new CANNON.Material("ground");
const groundShape = new CANNON.Box(new CANNON.Vec3(50, 0.2, 50));
const groundBody = new CANNON.Body({
mass: 0,
shape: groundShape,
material: groundPhyMaterial,
});
// groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
world.addBody(groundBody);
phyMeshes.push(groundBody);

const groundMesh = new THREE.Mesh(
// new THREE.PlaneGeometry(100, 100),
new THREE.BoxGeometry(100, 0.4, 100),
new THREE.MeshBasicMaterial({
color: 0x666666,
})
);
// groundMesh.rotation.x = -Math.PI / 2;
scene.add(groundMesh);
meshes.push(groundMesh);

// 创建车身
const chassisShape = new CANNON.Box(new CANNON.Vec3(2, 0.5, 1));
const chassisBody = new CANNON.Body({
mass: 150,
shape: chassisShape,
});
chassisBody.position.set(0, 14, 0);
world.addBody(chassisBody);
phyMeshes.push(chassisBody);

// three.js的车身
const chassisMesh = new THREE.Mesh(
new THREE.BoxGeometry(4, 1, 2),
new THREE.MeshBasicMaterial({
color: 0x660066,
})
);
scene.add(chassisMesh);
meshes.push(chassisMesh);

// 创建拥有悬架的车辆
const vehicle = new CANNON.RaycastVehicle({
chassisBody,
});

const wheelOptions = {
// 轮子半径
radius: 1,
// 本地坐标系中的轮子位置
// directionLocal: new CANNON.Vec3(0, -1, 0),
suspensionStiffness: 30, // 悬架的刚度
suspensionRestLength: 0.3, // 悬架的休息长度
// frictionSlip: 1.4, // 滑动摩擦力
dampingRelaxation: 2.3, // 振动
dampingCompression: 4.4, // 压缩
maxSuspensionForce: 100000, // 最大悬架力
// rollInfluence: 0.1, // 滚动影响
axleLocal: new CANNON.Vec3(0, 0, 1), // 本地坐标系中的轴
chassisConnectionPointLocal: new CANNON.Vec3(-1, 0, 1), // 本地坐标系中的车身连接点
maxSuspensionTravel: 0.2, // 最大悬架行程
// customSlidingRotationalSpeed: -30,
// useCustomSlidingRotationalSpeed: true,
};

vehicle.addWheel({
...wheelOptions,
chassisConnectionPointLocal: new CANNON.Vec3(-1, 0, 1),
});

vehicle.addWheel({
...wheelOptions,
chassisConnectionPointLocal: new CANNON.Vec3(-1, 0, -1),
});

vehicle.addWheel({
...wheelOptions,
chassisConnectionPointLocal: new CANNON.Vec3(1, 0, 1),
});

vehicle.addWheel({
...wheelOptions,
chassisConnectionPointLocal: new CANNON.Vec3(1, 0, -1),
});

vehicle.addToWorld(world);

const wheelBodies = [];

// 创建轮子
// 创建车轮形状
const wheelShape = new CANNON.Cylinder(0.5, 0.5, 0.2, 20);
// const wheelShape = new CANNON.Sphere(0.5);
// three.js的车轮
const wheelMaterial = new THREE.MeshBasicMaterial({
color: 0x660000,
wireframe: true,
});
// const wheelGeometry = new THREE.SphereGeometry(0.5, 8, 8);
const wheelGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.2, 20);

const wheelPhyMaterial = new CANNON.Material("wheel");

const wheel_ground = new CANNON.ContactMaterial(
wheelPhyMaterial,
groundPhyMaterial,
{
friction: 0,
restitution: 0,
contactEquationStiffness: 1000,
}
);
world.addContactMaterial(wheel_ground);

for (let i = 0; i < vehicle.wheelInfos.length; i++) {
const wheelBody = new CANNON.Body({
mass: 0,
// shape: wheelShape,
// type: CANNON.Body.KINEMATIC,
// collisionFilterGroup: 0, // turn off collisions
material: wheelPhyMaterial,
});
wheelBody.type = CANNON.Body.KINEMATIC;
wheelBody.collisionFilterGroup = 0;
// wheelBody.addShape(wheelShape);
wheelBodies.push(wheelBody);
// world.addBody(wheelBody);
phyMeshes.push(wheelBody);

const wheelMesh = new THREE.Mesh(wheelGeometry, wheelMaterial);
wheelMesh.rotation.x = -Math.PI / 2;
const wheelObj = new THREE.Object3D();
wheelObj.add(wheelMesh);
scene.add(wheelObj);
meshes.push(wheelObj);
}

world.addEventListener("postStep", () => {
// 更新车轮的位置
wheelBodies.forEach((wheelBody, index) => {
vehicle.updateWheelTransform(index);
wheelBody.position.copy(vehicle.wheelInfos[index].worldTransform.position);
wheelBody.quaternion.copy(
vehicle.wheelInfos[index].worldTransform.quaternion
);
});
});

// 设置完毕之后,将车子添加到世界中
// vehicle.addToWorld(world);

// console.log(vehicle);
// console.log(world);

// 控制车子
window.addEventListener("keydown", (event) => {
// console.log(event.key);
if (event.key === "w") {
// setWheelForce(力,轮子编号)
vehicle.applyEngineForce(1000, 0);
vehicle.applyEngineForce(1000, 1);
// vehicle.applyEngineForce(10000, 2);
// vehicle.applyEngineForce(10000, 3);
}
if (event.key === "s") {
vehicle.applyEngineForce(-1000, 0);
vehicle.applyEngineForce(-1000, 1);
}
if (event.key == "a") {
vehicle.setSteeringValue(Math.PI / 4, 2);
vehicle.setSteeringValue(Math.PI / 4, 3);
}
if (event.key == "d") {
vehicle.setSteeringValue(-Math.PI / 4, 2);
vehicle.setSteeringValue(-Math.PI / 4, 3);
}
});
window.addEventListener("keyup", (event) => {
if (event.key === "w") {
vehicle.applyEngineForce(0, 0);
vehicle.applyEngineForce(0, 1);
}
if (event.key === "s") {
vehicle.applyEngineForce(0, 0);
vehicle.applyEngineForce(0, 1);
}
if (event.key == "a") {
vehicle.setSteeringValue(0, 2);
vehicle.setSteeringValue(0, 3);
}
if (event.key == "d") {
vehicle.setSteeringValue(0, 2);
vehicle.setSteeringValue(0, 3);
}
});

// 渲染
let clock = new THREE.Clock();
function animate() {
let delta = clock.getDelta();
world.step(1 / 60, delta);

for (let i = 0; i < phyMeshes.length; i++) {
meshes[i].position.copy(phyMeshes[i].position);
meshes[i].quaternion.copy(phyMeshes[i].quaternion);
}

controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}

animate();
</script>

<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

canvas {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
}
</style>

效果图

增加效果展示

这里我添加了刹车和重置,以及给车体换了个颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
<template>
<div></div>
</template>

<script setup>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import * as CANNON from "cannon-es";
// 初始化物理世界
const world = new CANNON.World();
// 设置重力
world.gravity.set(0, -9.82, 0);

// Sweep and prune broadphase
world.broadphase = new CANNON.SAPBroadphase(world);

// Disable friction by default
world.defaultContactMaterial.friction = 0;

// 初始化3D场景
const scene = new THREE.Scene();
// 初始化相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 10, 20);
camera.lookAt(0, 0, 0);

// 初始化渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

let phyMeshes = [];
let meshes = [];

// 创建地面
let groundShape = new CANNON.Box(new CANNON.Vec3(50, 0.5, 50));

const groundPhyMaterial = new CANNON.Material("groundMaterial");
let groundBody = new CANNON.Body({
mass: 0,
shape: groundShape,
});
world.addBody(groundBody);

// 创建threejs地面
let groundMesh = new THREE.Mesh(
new THREE.BoxGeometry(100, 1, 100),
new THREE.MeshBasicMaterial({ color: 0x888888 })
);
scene.add(groundMesh);

// 创建车身
let chassisShape = new CANNON.Box(new CANNON.Vec3(2, 0.5, 1));
let chassisBody = new CANNON.Body({
mass: 150,
shape: chassisShape,
});
chassisBody.position.set(0, 5, 0);
world.addBody(chassisBody);
phyMeshes.push(chassisBody);

// 创建threejs车身
let chassisMesh = new THREE.Mesh(
new THREE.BoxGeometry(4, 1, 2),
new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
scene.add(chassisMesh);
meshes.push(chassisMesh);

// 创建拥有悬架的车辆
const vehicle = new CANNON.RaycastVehicle({
chassisBody: chassisBody,
});

// 设置车轮配置
const wheelOptions = {
// 车轮半径
radius: 0.5,
directionLocal: new CANNON.Vec3(0, -1, 0),
// 设置悬架的刚度
suspensionStiffness: 30,
// 设置悬架的休息长度
suspensionRestLength: 0.3,
// 设置车轮的滑动摩擦力
frictionSlip: 1.4,
// 设置拉伸的阻尼
dampingRelaxation: 2.3,
// 设置压缩的阻尼
dampingCompression: 4.4,
// 最大的悬架力
maxSuspensionForce: 100000,
// 设置最大的悬架行程
maxSuspensionTravel: 0.2,
// 车轮的转向轴
axleLocal: new CANNON.Vec3(0, 0, 1),
};

// 添加车轮
vehicle.addWheel({
...wheelOptions,
// 设置车轮的位置
chassisConnectionPointLocal: new CANNON.Vec3(-1, 0, 1),
});

vehicle.addWheel({
...wheelOptions,
// 设置车轮的位置
chassisConnectionPointLocal: new CANNON.Vec3(-1, 0, -1),
});

vehicle.addWheel({
...wheelOptions,
// 设置车轮的位置
chassisConnectionPointLocal: new CANNON.Vec3(1, 0, 1),
});

vehicle.addWheel({
...wheelOptions,
// 设置车轮的位置
chassisConnectionPointLocal: new CANNON.Vec3(1, 0, -1),
});

vehicle.addToWorld(world);

// 创建threejs车轮
const wheelBodies = [];

// 车轮形状
const wheelShape = new CANNON.Cylinder(0.5, 0.5, 0.2, 20);
const wheelGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.2, 20);
const wheelMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000,
wireframe: true,
});

for (let i = 0; i < vehicle.wheelInfos.length; i++) {
const wheel = vehicle.wheelInfos[i];
const cylinderBody = new CANNON.Body({
mass: 0,
shape: wheelShape,
});
cylinderBody.addShape(wheelShape);
// world.addBody(cylinderBody);
phyMeshes.push(cylinderBody);
wheelBodies.push(cylinderBody);

const cylinderMesh = new THREE.Mesh(wheelGeometry, wheelMaterial);
cylinderMesh.rotation.x = -Math.PI / 2;
const wheelObj = new THREE.Object3D();
wheelObj.add(cylinderMesh);
scene.add(wheelObj);
meshes.push(wheelObj);
}

world.addEventListener("postStep", () => {
for (let i = 0; i < vehicle.wheelInfos.length; i++) {
vehicle.updateWheelTransform(i);
const t = vehicle.wheelInfos[i].worldTransform;
const wheelBody = wheelBodies[i];
wheelBody.position.copy(t.position);
wheelBody.quaternion.copy(t.quaternion);
}
});

window.addEventListener("keydown", (event) => {
if (event.key == "w") {
vehicle.applyEngineForce(1000, 2);
vehicle.applyEngineForce(1000, 3);
}
if (event.key == "s") {
vehicle.applyEngineForce(-1000, 2);
vehicle.applyEngineForce(-1000, 3);
}
if (event.key == "a") {
vehicle.setSteeringValue(Math.PI / 4, 2);
vehicle.setSteeringValue(Math.PI / 4, 3);
}
if (event.key == "d") {
vehicle.setSteeringValue(-Math.PI / 4, 2);
vehicle.setSteeringValue(-Math.PI / 4, 3);
}
// 按下r重置车辆
// console.log(event.key);
if (event.key == "r") {
chassisBody.velocity.set(0, 0, 0);
chassisBody.angularVelocity.set(0, 0, 0);
chassisBody.position.set(0, 10, 0);
}
// 空格刹车
if (event.key == " ") {
vehicle.setBrake(100, 0);
vehicle.setBrake(100, 1);
}
});

window.addEventListener("keyup", (event) => {
if (event.key == "w" || event.key == "s") {
vehicle.applyEngineForce(0, 2);
vehicle.applyEngineForce(0, 3);
}
if (event.key == "a" || event.key == "d") {
vehicle.setSteeringValue(0, 2);
vehicle.setSteeringValue(0, 3);
}
if (event.key == " ") {
vehicle.setBrake(0, 0);
vehicle.setBrake(0, 1);
}
});

// 渲染
let clock = new THREE.Clock();
function animate() {
let delta = clock.getDelta();
world.step(1 / 60, delta);

for (let i = 0; i < phyMeshes.length; i++) {
meshes[i].position.copy(phyMeshes[i].position);
meshes[i].quaternion.copy(phyMeshes[i].quaternion);
}

controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}

animate();
</script>

<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

canvas {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
}
</style>

结语

本篇文章就到此结束了,更多内容敬请期待~

上一篇:
一篇文章教会你如何抓包-fidder
下一篇:
【可视化学习】46-cannon-es的简单使用(一)