physics_rapier_vehicle_controller.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>three.js physics - rapier3d vehicle controller</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  7. <meta property="og:title" content="three.js physics - rapier3d vehicle controller">
  8. <meta property="og:type" content="website">
  9. <meta property="og:url" content="https://threejs.org/examples/physics_rapier_vehicle_controller.html">
  10. <meta property="og:image" content="https://threejs.org/examples/screenshots/physics_rapier_vehicle_controller.jpg">
  11. <link type="text/css" rel="stylesheet" href="main.css">
  12. <style>
  13. body {
  14. color: #333;
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div id="info">
  20. <a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> physics - <a href="https://github.com/dimforge/rapier.js" target="_blank">rapier</a> vehicle controller
  21. <p>WASD or Arrow keys to move</p>
  22. <p>Space to brake</p>
  23. <p>R to reset</p>
  24. </div>
  25. <script type="importmap">
  26. {
  27. "imports": {
  28. "three": "../build/three.module.js",
  29. "three/addons/": "./jsm/"
  30. }
  31. }
  32. </script>
  33. <script type="module">
  34. import * as THREE from 'three';
  35. import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
  36. import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js';
  37. import { RapierHelper } from 'three/addons/helpers/RapierHelper.js';
  38. import Stats from 'three/addons/libs/stats.module.js';
  39. let camera, scene, renderer, stats;
  40. let physics, physicsHelper, controls;
  41. let car, chassis, wheels, movement, vehicleController;
  42. init();
  43. async function init() {
  44. scene = new THREE.Scene();
  45. scene.background = new THREE.Color( 0xbfd1e5 );
  46. camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 );
  47. camera.position.set( 0, 4, 10 );
  48. const ambient = new THREE.HemisphereLight( 0x555555, 0xFFFFFF );
  49. scene.add( ambient );
  50. const light = new THREE.DirectionalLight( 0xffffff, 4 );
  51. light.position.set( 0, 12.5, 12.5 );
  52. light.castShadow = true;
  53. light.shadow.radius = 3;
  54. light.shadow.blurSamples = 8;
  55. light.shadow.mapSize.width = 2048;
  56. light.shadow.mapSize.height = 2048;
  57. const size = 40;
  58. light.shadow.camera.left = - size;
  59. light.shadow.camera.bottom = - size;
  60. light.shadow.camera.right = size;
  61. light.shadow.camera.top = size;
  62. light.shadow.camera.near = 1;
  63. light.shadow.camera.far = 50;
  64. scene.add( light );
  65. renderer = new THREE.WebGLRenderer( { antialias: true } );
  66. renderer.setPixelRatio( window.devicePixelRatio );
  67. renderer.setSize( window.innerWidth, window.innerHeight );
  68. renderer.shadowMap.enabled = true;
  69. document.body.appendChild( renderer.domElement );
  70. renderer.setAnimationLoop( animate );
  71. controls = new OrbitControls( camera, renderer.domElement );
  72. controls.target = new THREE.Vector3( 0, 2, 0 );
  73. controls.update();
  74. const geometry = new THREE.BoxGeometry( 100, 0.5, 100 );
  75. const material = new THREE.MeshStandardMaterial( { color: 0xFFFFFF } );
  76. const ground = new THREE.Mesh( geometry, material );
  77. ground.receiveShadow = true;
  78. ground.position.set( 0, - 0.25, - 20 );
  79. ground.userData.physics = { mass: 0 };
  80. scene.add( ground );
  81. new THREE.TextureLoader().load( 'textures/grid.png', function ( texture ) {
  82. texture.wrapS = THREE.RepeatWrapping;
  83. texture.wrapT = THREE.RepeatWrapping;
  84. texture.repeat.set( 80, 80 );
  85. ground.material.map = texture;
  86. ground.material.needsUpdate = true;
  87. } );
  88. stats = new Stats();
  89. document.body.appendChild( stats.dom );
  90. initPhysics();
  91. onWindowResize();
  92. // Movement input
  93. movement = {
  94. forward: 0,
  95. right: 0,
  96. brake: 0,
  97. reset: false,
  98. accelerateForce: { value: 0, min: - 30, max: 30, step: 1 },
  99. brakeForce: { value: 0, min: 0, max: 1, step: 0.05 }
  100. };
  101. window.addEventListener( 'keydown', ( event ) => {
  102. //console.log( event.key );
  103. if ( event.key === 'w' || event.key === 'ArrowUp' ) movement.forward = - 1;
  104. if ( event.key === 's' || event.key === 'ArrowDown' ) movement.forward = 1;
  105. if ( event.key === 'a' || event.key === 'ArrowLeft' ) movement.right = 1;
  106. if ( event.key === 'd' || event.key === 'ArrowRight' ) movement.right = - 1;
  107. if ( event.key === 'r' ) movement.reset = true;
  108. if ( event.key === ' ' ) movement.brake = 1;
  109. } );
  110. window.addEventListener( 'keyup', ( event ) => {
  111. if ( event.key === 'w' || event.key === 's' || event.key === 'ArrowUp' || event.key === 'ArrowDown' ) movement.forward = 0;
  112. if ( event.key === 'a' || event.key === 'd' || event.key === 'ArrowLeft' || event.key === 'ArrowRight' ) movement.right = 0;
  113. if ( event.key === 'r' ) movement.reset = false;
  114. if ( event.key === ' ' ) movement.brake = 0;
  115. } );
  116. window.addEventListener( 'resize', onWindowResize, false );
  117. }
  118. async function initPhysics() {
  119. //Initialize physics engine using the script in the jsm/physics folder
  120. physics = await RapierPhysics();
  121. //Optionally display collider outlines
  122. physicsHelper = new RapierHelper( physics.world );
  123. scene.add( physicsHelper );
  124. physics.addScene( scene );
  125. createCar();
  126. }
  127. function createCar( ) {
  128. const geometry = new THREE.BoxGeometry( 2, 1, 4 );
  129. const material = new THREE.MeshStandardMaterial( { color: 0xFF0000 } );
  130. const mesh = new THREE.Mesh( geometry, material );
  131. mesh.castShadow = true;
  132. scene.add( mesh );
  133. car = mesh;
  134. mesh.position.y = 1;
  135. physics.addMesh( mesh, 10, 0.8 ); // addMesh places the RigidBody in the mesh.userData.physics object
  136. chassis = mesh.userData.physics.body;
  137. vehicleController = physics.world.createVehicleController( chassis );
  138. wheels = [];
  139. addWheel( 0, { x: - 1, y: 0, z: - 1.5 }, mesh );
  140. addWheel( 1, { x: 1, y: 0, z: - 1.5 }, mesh );
  141. addWheel( 2, { x: - 1, y: 0, z: 1.5 }, mesh );
  142. addWheel( 3, { x: 1, y: 0, z: 1.5 }, mesh );
  143. vehicleController.setWheelSteering( 0, Math.PI / 4 );
  144. vehicleController.setWheelSteering( 1, Math.PI / 4 );
  145. }
  146. function addWheel( index, pos, carMesh ) {
  147. // Define wheel properties
  148. const wheelRadius = 0.3;
  149. const wheelWidth = 0.4;
  150. const suspensionRestLength = 0.8;
  151. const wheelPosition = pos; // Position relative to chassis
  152. const wheelDirection = { x: 0.0, y: - 1.0, z: 0.0 }; // Downward direction
  153. const wheelAxle = { x: - 1.0, y: 0.0, z: 0.0 }; // Axle direction
  154. // Add the wheel to the vehicle controller
  155. vehicleController.addWheel(
  156. wheelPosition,
  157. wheelDirection,
  158. wheelAxle,
  159. suspensionRestLength,
  160. wheelRadius
  161. );
  162. // Set suspension stiffness for wheel
  163. vehicleController.setWheelSuspensionStiffness( index, 24.0 );
  164. // Set wheel friction
  165. vehicleController.setWheelFrictionSlip( index, 1000.0 );
  166. // Enable steering for the wheel
  167. vehicleController.setWheelSteering( index, pos.z < 0 );
  168. // Create a wheel mesh
  169. const geometry = new THREE.CylinderGeometry( wheelRadius, wheelRadius, wheelWidth, 16 );
  170. geometry.rotateZ( Math.PI * 0.5 );
  171. const material = new THREE.MeshStandardMaterial( { color: 0x000000 } );
  172. const wheel = new THREE.Mesh( geometry, material );
  173. wheel.castShadow = true;
  174. wheel.position.copy( pos );
  175. wheels.push( wheel );
  176. carMesh.add( wheel );
  177. }
  178. function updateWheels() {
  179. if ( vehicleController === undefined ) return;
  180. const wheelSteeringQuat = new THREE.Quaternion();
  181. const wheelRotationQuat = new THREE.Quaternion();
  182. const up = new THREE.Vector3( 0, 1, 0 );
  183. //const chassisPosition = chassis.translation();
  184. wheels.forEach( ( wheel, index ) => {
  185. const wheelAxleCs = vehicleController.wheelAxleCs( index );
  186. const connection = vehicleController.wheelChassisConnectionPointCs( index ).y || 0;
  187. const suspension = vehicleController.wheelSuspensionLength( index ) || 0;
  188. const steering = vehicleController.wheelSteering( index ) || 0;
  189. const rotationRad = vehicleController.wheelRotation( index ) || 0;
  190. wheel.position.y = connection - suspension;
  191. wheelSteeringQuat.setFromAxisAngle( up, steering );
  192. wheelRotationQuat.setFromAxisAngle( wheelAxleCs, rotationRad );
  193. wheel.quaternion.multiplyQuaternions( wheelSteeringQuat, wheelRotationQuat );
  194. } );
  195. }
  196. function updateCarControl() {
  197. if ( movement.reset ) {
  198. chassis.setTranslation( new physics.RAPIER.Vector3( 0, 1, 0 ), true );
  199. chassis.setRotation( new physics.RAPIER.Quaternion( 0, 0, 0, 1 ), true );
  200. chassis.setLinvel( new physics.RAPIER.Vector3( 0, 0, 0 ), true );
  201. chassis.setAngvel( new physics.RAPIER.Vector3( 0, 0, 0 ), true );
  202. movement.accelerateForce.value = 0;
  203. movement.brakeForce.value = 0;
  204. return;
  205. }
  206. let accelerateForce = 0;
  207. if ( movement.forward < 0 ) {
  208. //if (movement.accelerateForce.value === 0) chassis.wakeUp();
  209. accelerateForce = movement.accelerateForce.value - movement.accelerateForce.step;
  210. if ( accelerateForce < movement.accelerateForce.min ) accelerateForce = movement.accelerateForce.min;
  211. } else if ( movement.forward > 0 ) {
  212. //if (movement.accelerateForce.value === 0) chassis.wakeUp();
  213. accelerateForce = movement.accelerateForce.value + movement.accelerateForce.step;
  214. if ( accelerateForce > movement.accelerateForce.max ) accelerateForce = movement.accelerateForce.max;
  215. } else {
  216. if ( chassis.isSleeping() ) chassis.wakeUp();
  217. }
  218. movement.accelerateForce.value = accelerateForce;
  219. //console.log(accelerateForce);
  220. let brakeForce = 0;
  221. if ( movement.brake > 0 ) {
  222. brakeForce = movement.brakeForce.value + movement.brakeForce.step;
  223. if ( brakeForce > movement.brakeForce.max ) brakeForce = movement.brakeForce.max;
  224. }
  225. movement.brakeForce.value = brakeForce;
  226. const engineForce = accelerateForce;
  227. vehicleController.setWheelEngineForce( 0, engineForce );
  228. vehicleController.setWheelEngineForce( 1, engineForce );
  229. const currentSteering = vehicleController.wheelSteering( 0 );
  230. const steerDirection = movement.right;
  231. const steerAngle = Math.PI / 4;
  232. const steering = THREE.MathUtils.lerp( currentSteering, steerAngle * steerDirection, 0.25 );
  233. vehicleController.setWheelSteering( 0, steering );
  234. vehicleController.setWheelSteering( 1, steering );
  235. const wheelBrake = movement.brake * brakeForce;
  236. vehicleController.setWheelBrake( 0, wheelBrake );
  237. vehicleController.setWheelBrake( 1, wheelBrake );
  238. vehicleController.setWheelBrake( 2, wheelBrake );
  239. vehicleController.setWheelBrake( 3, wheelBrake );
  240. }
  241. function onWindowResize( ) {
  242. camera.aspect = window.innerWidth / window.innerHeight;
  243. camera.updateProjectionMatrix();
  244. renderer.setSize( window.innerWidth, window.innerHeight );
  245. }
  246. function animate() {
  247. if ( vehicleController ) {
  248. updateCarControl();
  249. vehicleController.updateVehicle( 1 / 60 );
  250. updateWheels();
  251. }
  252. if ( controls && car ) {
  253. controls.target.copy( car.position );
  254. controls.update();
  255. }
  256. if ( physicsHelper ) physicsHelper.update();
  257. renderer.render( scene, camera );
  258. stats.update();
  259. }
  260. </script>
  261. </body>
  262. </html>
粤ICP备19079148号