CarControls.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /**
  2. * @author alteredq / http://alteredqualia.com/
  3. * @author Lewy Blue https://github.com/looeee
  4. *
  5. * The model is expected to follow real world car proportions. You can try unusual car types
  6. * but your results may be unexpected. Scaled models are also not supported.
  7. *
  8. * Defaults are rough estimates for a real world scale car model
  9. *
  10. */
  11. import {
  12. Box3,
  13. Group,
  14. Math as _Math,
  15. Vector3
  16. } from "../../../build/three.module.js";
  17. var CarControls = ( function ( ) {
  18. // private variables
  19. var steeringWheelSpeed = 1.5;
  20. var maxSteeringRotation = 0.6;
  21. var acceleration = 0;
  22. var maxSpeedReverse, accelerationReverse, deceleration;
  23. var controlKeys = { LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, BRAKE: 32 };
  24. var wheelOrientation = 0;
  25. var carOrientation = 0;
  26. var root = null;
  27. var frontLeftWheelRoot = null;
  28. var frontRightWheelRoot = null;
  29. var frontLeftWheel = new Group();
  30. var frontRightWheel = new Group();
  31. var backLeftWheel = null;
  32. var backRightWheel = null;
  33. var steeringWheel = null;
  34. var wheelDiameter = 1;
  35. var length = 1;
  36. var loaded = false;
  37. var controls = {
  38. brake: false,
  39. moveForward: false,
  40. moveBackward: false,
  41. moveLeft: false,
  42. moveRight: false
  43. };
  44. function CarControls( maxSpeed, acceleration, brakePower, turningRadius, keys ) {
  45. this.enabled = true;
  46. this.elemNames = {
  47. flWheel: 'wheel_fl',
  48. frWheel: 'wheel_fr',
  49. rlWheel: 'wheel_rl',
  50. rrWheel: 'wheel_rr',
  51. steeringWheel: 'steering_wheel', // set to null to disable
  52. };
  53. // km/hr
  54. this.maxSpeed = maxSpeed || 180;
  55. maxSpeedReverse = - this.maxSpeed * 0.25;
  56. // m/s
  57. this.acceleration = acceleration || 10;
  58. accelerationReverse = this.acceleration * 0.5;
  59. // metres
  60. this.turningRadius = turningRadius || 6;
  61. // m/s
  62. deceleration = this.acceleration * 2;
  63. // multiplied with deceleration, so breaking deceleration = ( acceleration * 2 * brakePower ) m/s
  64. this.brakePower = brakePower || 10;
  65. // exposed so that a user can use this for various effect, e.g blur
  66. this.speed = 0;
  67. // keys used to control car - by default the arrow keys and space to brake
  68. controlKeys = keys || controlKeys;
  69. // local axes of rotation - these are likely to vary between models
  70. this.wheelRotationAxis = 'x';
  71. this.wheelTurnAxis = 'z';
  72. this.steeringWheelTurnAxis = 'y';
  73. document.addEventListener( 'keydown', this.onKeyDown, false );
  74. document.addEventListener( 'keyup', this.onKeyUp, false );
  75. }
  76. CarControls.prototype = {
  77. constructor: CarControls,
  78. onKeyDown: function ( event ) {
  79. switch ( event.keyCode ) {
  80. case controlKeys.BRAKE:
  81. controls.brake = true;
  82. controls.moveForward = false;
  83. controls.moveBackward = false;
  84. break;
  85. case controlKeys.UP: controls.moveForward = true; break;
  86. case controlKeys.DOWN: controls.moveBackward = true; break;
  87. case controlKeys.LEFT: controls.moveLeft = true; break;
  88. case controlKeys.RIGHT: controls.moveRight = true; break;
  89. }
  90. },
  91. onKeyUp: function ( event ) {
  92. switch ( event.keyCode ) {
  93. case controlKeys.BRAKE: controls.brake = false; break;
  94. case controlKeys.UP: controls.moveForward = false; break;
  95. case controlKeys.DOWN: controls.moveBackward = false; break;
  96. case controlKeys.LEFT: controls.moveLeft = false; break;
  97. case controlKeys.RIGHT: controls.moveRight = false; break;
  98. }
  99. },
  100. dispose: function () {
  101. document.removeEventListener( 'keydown', this.onKeyDown, false );
  102. document.removeEventListener( 'keyup', this.onKeyUp, false );
  103. },
  104. update: function ( delta ) {
  105. if ( ! loaded || ! this.enabled ) return;
  106. var brakingDeceleration = 1;
  107. if ( controls.brake ) brakingDeceleration = this.brakePower;
  108. if ( controls.moveForward ) {
  109. this.speed = _Math.clamp( this.speed + delta * this.acceleration, maxSpeedReverse, this.maxSpeed );
  110. acceleration = _Math.clamp( acceleration + delta, - 1, 1 );
  111. }
  112. if ( controls.moveBackward ) {
  113. this.speed = _Math.clamp( this.speed - delta * accelerationReverse, maxSpeedReverse, this.maxSpeed );
  114. acceleration = _Math.clamp( acceleration - delta, - 1, 1 );
  115. }
  116. if ( controls.moveLeft ) {
  117. wheelOrientation = _Math.clamp( wheelOrientation + delta * steeringWheelSpeed, - maxSteeringRotation, maxSteeringRotation );
  118. }
  119. if ( controls.moveRight ) {
  120. wheelOrientation = _Math.clamp( wheelOrientation - delta * steeringWheelSpeed, - maxSteeringRotation, maxSteeringRotation );
  121. }
  122. // this.speed decay
  123. if ( ! ( controls.moveForward || controls.moveBackward ) ) {
  124. if ( this.speed > 0 ) {
  125. var k = exponentialEaseOut( this.speed / this.maxSpeed );
  126. this.speed = _Math.clamp( this.speed - k * delta * deceleration * brakingDeceleration, 0, this.maxSpeed );
  127. acceleration = _Math.clamp( acceleration - k * delta, 0, 1 );
  128. } else {
  129. var k = exponentialEaseOut( this.speed / maxSpeedReverse );
  130. this.speed = _Math.clamp( this.speed + k * delta * accelerationReverse * brakingDeceleration, maxSpeedReverse, 0 );
  131. acceleration = _Math.clamp( acceleration + k * delta, - 1, 0 );
  132. }
  133. }
  134. // steering decay
  135. if ( ! ( controls.moveLeft || controls.moveRight ) ) {
  136. if ( wheelOrientation > 0 ) {
  137. wheelOrientation = _Math.clamp( wheelOrientation - delta * steeringWheelSpeed, 0, maxSteeringRotation );
  138. } else {
  139. wheelOrientation = _Math.clamp( wheelOrientation + delta * steeringWheelSpeed, - maxSteeringRotation, 0 );
  140. }
  141. }
  142. var forwardDelta = - this.speed * delta;
  143. carOrientation -= ( forwardDelta * this.turningRadius * 0.02 ) * wheelOrientation;
  144. // movement of car
  145. root.position.x += Math.sin( carOrientation ) * forwardDelta * length;
  146. root.position.z += Math.cos( carOrientation ) * forwardDelta * length;
  147. // angle of car
  148. root.rotation.y = carOrientation;
  149. // wheels rolling
  150. var angularSpeedRatio = - 2 / wheelDiameter;
  151. var wheelDelta = forwardDelta * angularSpeedRatio * length;
  152. frontLeftWheel.rotation[ this.wheelRotationAxis ] -= wheelDelta;
  153. frontRightWheel.rotation[ this.wheelRotationAxis ] -= wheelDelta;
  154. backLeftWheel.rotation[ this.wheelRotationAxis ] -= wheelDelta;
  155. backRightWheel.rotation[ this.wheelRotationAxis ] -= wheelDelta;
  156. // rotation while steering
  157. frontLeftWheelRoot.rotation[ this.wheelTurnAxis ] = wheelOrientation;
  158. frontRightWheelRoot.rotation[ this.wheelTurnAxis ] = wheelOrientation;
  159. steeringWheel.rotation[ this.steeringWheelTurnAxis ] = -wheelOrientation * 6;
  160. },
  161. setModel: function ( model, elemNames ) {
  162. if ( elemNames ) this.elemNames = elemNames;
  163. root = model;
  164. this.setupWheels();
  165. this.computeDimensions();
  166. loaded = true;
  167. },
  168. setupWheels: function () {
  169. frontLeftWheelRoot = root.getObjectByName( this.elemNames.flWheel );
  170. frontRightWheelRoot = root.getObjectByName( this.elemNames.frWheel );
  171. backLeftWheel = root.getObjectByName( this.elemNames.rlWheel );
  172. backRightWheel = root.getObjectByName( this.elemNames.rrWheel );
  173. if ( this.elemNames.steeringWheel !== null ) steeringWheel = root.getObjectByName( this.elemNames.steeringWheel );
  174. while ( frontLeftWheelRoot.children.length > 0 ) frontLeftWheel.add( frontLeftWheelRoot.children[ 0 ] );
  175. while ( frontRightWheelRoot.children.length > 0 ) frontRightWheel.add( frontRightWheelRoot.children[ 0 ] );
  176. frontLeftWheelRoot.add( frontLeftWheel );
  177. frontRightWheelRoot.add( frontRightWheel );
  178. },
  179. computeDimensions: function () {
  180. var bb = new Box3().setFromObject( frontLeftWheelRoot );
  181. var size = new Vector3();
  182. bb.getSize( size );
  183. wheelDiameter = Math.max( size.x, size.y, size.z );
  184. bb.setFromObject( root );
  185. size = bb.getSize( size );
  186. length = Math.max( size.x, size.y, size.z );
  187. }
  188. };
  189. function exponentialEaseOut( k ) {
  190. return k === 1 ? 1 : - Math.pow( 2, - 10 * k ) + 1;
  191. }
  192. return CarControls;
  193. } )();
  194. export { CarControls };