player.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. window.THREE = require('three')
  2. import {Vector3} from 'three'
  3. import {Main} from './main'
  4. import {iterateGeometries, createHullShape} from 'three-to-ammo'
  5. import {sphere_radius_by_volume, consensus_float} from './util'
  6. import nipplejs from 'nipplejs'
  7. function staticRaycast(origin, dir){
  8. var raycaster = new THREE.Raycaster(origin, dir, 0, 1000)
  9. var hits = raycaster.intersectObjects(Main.scene.children, true)
  10. for (let index = 0; index < hits.length; index++) {
  11. if (hits[index].object.userData.static){
  12. return hits[index]
  13. }
  14. }
  15. }
  16. window.addEventListener("gamepadconnected", function(e) {
  17. var gp = navigator.getGamepads()[e.gamepad.index];
  18. window.gamepad_index = e.gamepad.index
  19. console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
  20. gp.index, gp.id,
  21. gp.buttons.length, gp.axes.length);
  22. });
  23. window.addEventListener("load", function(e){
  24. window.left_touch = nipplejs.create({
  25. zone: document.querySelector(".left")
  26. })
  27. window.right_touch = nipplejs.create({
  28. zone: document.querySelector(".right")
  29. })
  30. })
  31. export class Player {
  32. constructor(animations) {
  33. this.ball = Main.scene.getObjectByName("ball_collider")
  34. this.ball_display = Main.scene.getObjectByName("ball_display")
  35. this.prince = Main.scene.getObjectByName("prince_armature")
  36. this.sun = Main.scene.getObjectByName("sun")
  37. this.animations = {}
  38. this.ball_radius = 2
  39. this.heading = new Vector3(0,0,1)
  40. this.last_position = new Vector3(0,0,0)
  41. this.strafe_left = 0
  42. this.strafe_right = 0
  43. this.volume = 29.26
  44. this.ball.material.visible = false
  45. var bdscale_ratio = this.ball_display.scale.x / this.ball.scale.x
  46. this.ball_display.parent = this.ball
  47. this.ball_display.position.copy(new Vector3(0,0,0))
  48. this.ball_display.scale.set(bdscale_ratio,bdscale_ratio,bdscale_ratio)
  49. this.ball.userData.physicsBody.setFriction( 10 );
  50. this.ball.userData.physicsBody.setDamping(0.1, 0.2)
  51. this.ball.onCollide = this.on_collide.bind(this)
  52. // var skeleton = new THREE.SkeletonHelper( prince );
  53. // skeleton.visible = true;
  54. // Main.scene.add( skeleton );
  55. this.mixer = new THREE.AnimationMixer( this.prince );
  56. animations.forEach(o => {
  57. var anim = this.mixer.clipAction(o)
  58. this.animations[o.name] = anim
  59. anim.setEffectiveTimeScale(1)
  60. anim.setEffectiveWeight(0)
  61. anim.play()
  62. });
  63. }
  64. rebuild_shell(){
  65. this.ball_radius = sphere_radius_by_volume(this.volume)
  66. var raycaster = new THREE.Raycaster()
  67. var ball = this.ball
  68. var position_buffer = ball.geometry.attributes.position
  69. var rebuild = false
  70. for (let index = 0; index < position_buffer.count; index++) {
  71. var position = new Vector3(
  72. position_buffer.array[index*3],
  73. position_buffer.array[index*3+1],
  74. position_buffer.array[index*3+2])
  75. // need to set the magnitude of the position to the ball_radius
  76. position = ball.localToWorld(position.multiply(new THREE.Vector3(30,30,30)))
  77. var direction = ball.position.clone().sub(position).normalize()
  78. raycaster.set(position, direction)
  79. raycaster.far = ball.position.distanceTo(position)
  80. var intersects = raycaster.intersectObjects(ball.children)
  81. if (intersects[0] && intersects[0].object != ball){
  82. //gizmos.line(intersects[0].point, ball.position, 0xff00ff)
  83. var point = ball.worldToLocal(intersects[0].point)
  84. // here we make sure the bump does not get out of controll
  85. // it should:
  86. // be the min of the cast point and the distance of the hit object center
  87. // be a max of 1.5 the ball radius
  88. //normalize point and mult as the distance of the hit object to the ball
  89. var len = Math.min(
  90. point.length(),
  91. this.ball_radius*1.1,
  92. (ball.position.distanceTo(intersects[0].object.position)))
  93. point.normalize().multiplyScalar(len)
  94. position_buffer.array[index*3] = point.x
  95. position_buffer.array[index*3+1] = point.y
  96. position_buffer.array[index*3+2] = point.z
  97. position_buffer.needsUpdate = true
  98. rebuild = true
  99. }
  100. // make sure every vertex is as far away as the ball's new radius
  101. var position = new Vector3(
  102. position_buffer.array[index*3],
  103. position_buffer.array[index*3+1],
  104. position_buffer.array[index*3+2])
  105. if (position.length() < player.ball_radius){
  106. rebuild = true
  107. position.normalize().multiplyScalar(player.ball_radius)
  108. position_buffer.array[index*3] = position.x
  109. position_buffer.array[index*3+1] = position.y
  110. position_buffer.array[index*3+2] = position.z
  111. }
  112. }
  113. if (rebuild){
  114. const matrixWorld = new THREE.Matrix4();
  115. var vertices = [];
  116. var matrices = [];
  117. var indexes = [];
  118. var temp = ball.children
  119. ball.children = []
  120. iterateGeometries(ball, {}, (vertexArray, matrixArray, indexArray) => {
  121. vertices.push(vertexArray);
  122. matrices.push(matrixArray);
  123. indexes.push(indexArray);
  124. });
  125. ball.children = temp
  126. var shape = createHullShape(vertices, matrices, matrixWorld.elements)
  127. shape.setLocalScaling(new Ammo.btVector3(ball.scale.x, ball.scale.y, ball.scale.z))
  128. ball.userData.physicsBody.setCollisionShape(shape)
  129. }
  130. }
  131. on_collide(object, manifold){
  132. if (!object.userData.static
  133. && object.userData.physicsBody
  134. && (object.userData.volume < this.volume*0.3)){
  135. this.volume += object.userData.volume
  136. var shape = object.userData.physicsBody.getCollisionShape()
  137. this.ball.attach(object)
  138. Main.removeRigidBody(object)
  139. var rebuild = this.rebuild_shell.bind(this)
  140. setTimeout(function(){rebuild()}, 1)
  141. }
  142. }
  143. update(dt){
  144. var body = this.ball.userData.physicsBody
  145. var ballforce = 200
  146. var axis_LH = 0
  147. var axis_LV = 0
  148. var axis_RH = 0
  149. var axis_RV = 0
  150. // 65 73
  151. // 87 68 83 74 75 76
  152. if (74 in Main.pressed_keys) { axis_RH = -1 }
  153. if (76 in Main.pressed_keys) { axis_RH = 1 }
  154. if (73 in Main.pressed_keys) { axis_RV = 1 }
  155. if (75 in Main.pressed_keys) { axis_RV = -1 }
  156. if (65 in Main.pressed_keys) { axis_LH = -1 }
  157. if (68 in Main.pressed_keys) { axis_LH = 1 }
  158. if (87 in Main.pressed_keys) { axis_LV = 1 }
  159. if (83 in Main.pressed_keys) { axis_LV = -1 }
  160. if (window.gamepad_index != undefined){
  161. var gp = navigator.getGamepads()[window.gamepad_index]
  162. gp.buttons.forEach(b => {
  163. if (b.pressed){
  164. }
  165. })
  166. axis_LH = gp.axes[0]
  167. axis_LV = -gp.axes[1]
  168. axis_RH = gp.axes[2]
  169. axis_RV = -gp.axes[3]
  170. }
  171. if (left_touch.ids.length > 0){
  172. var stick = left_touch.get(left_touch.ids[0])
  173. axis_LV = -(stick.frontPosition.y / 50)
  174. axis_LH = (stick.frontPosition.x / 50)
  175. }
  176. if (right_touch.ids.length > 0){
  177. var stick = right_touch.get(right_touch.ids[0])
  178. axis_RV = -(stick.frontPosition.y / 50)
  179. axis_RH = (stick.frontPosition.x / 50)
  180. }
  181. // both sticks need to be engaged for any force to transfer (hence min)
  182. var v = ballforce * consensus_float(axis_RV, axis_LV)
  183. body.applyForce(new Ammo.btVector3(this.heading.x*-v, 0, this.heading.z*-v))
  184. var lateral_heading = player.heading.clone().applyEuler(new THREE.Euler(0, 90, 0))
  185. var h = ballforce * consensus_float(axis_RH, axis_LH)
  186. body.applyForce(new Ammo.btVector3(lateral_heading.x*h, 0, lateral_heading.z*h))
  187. // tank tread style turning
  188. var turn = axis_RV - axis_LV
  189. if (Math.abs(turn) > 1) {
  190. this.heading.applyEuler(new THREE.Euler(0, turn*dt, 0))
  191. }
  192. //body.applyForce(new Ammo.btVector3(0, -50, 0))
  193. /* var velocity = new Vector3(
  194. this.ball.userData.physicsBody.getLinearVelocity().x(),
  195. this.ball.userData.physicsBody.getLinearVelocity().y(),
  196. this.ball.userData.physicsBody.getLinearVelocity().z()
  197. ) */
  198. var velocity = player.ball.position.clone().sub(this.last_position.clone())
  199. var prince_frame_velocity = player.prince.worldToLocal(velocity.clone().add(player.prince.position))
  200. //window.gizmos.line(this.ball.position, prince_frame_velocity.clone().multiplyScalar(50).add(this.ball.position) )
  201. //window.gizmos.sphere(this.ball.position.clone().add(velocity.clone().multiplyScalar(10)), 0.2, 0xFF0000)
  202. this.sun.position.copy(new Vector3(4, 51, -33).add(this.ball.position))
  203. var pp = this.ball.position.clone().add(this.heading.clone().multiplyScalar(5))
  204. var hit = staticRaycast(pp, new Vector3(0, -1, 0))
  205. if (hit){
  206. this.prince.position.lerp(hit.point, 0.5)
  207. //gizmos.sphere(hit.point, 0.2, 0xFF0000)
  208. }
  209. this.prince.lookAt(new Vector3(this.ball.position.x, this.prince.position.y, this.ball.position.z))
  210. this.prince.rotateY(3.14159)
  211. var point = this.ball.position.clone().add(this.heading.clone().multiplyScalar(15 + (this.ball_radius*2) ).add(new Vector3(0, 10, 0)))
  212. Main.camera.position.lerp(point, 0.2)
  213. Main.camera.lookAt(this.ball.position)
  214. var forward_vel = prince_frame_velocity.z*-5
  215. this.animations["push"].setEffectiveWeight(forward_vel)
  216. this.animations["pull"].setEffectiveWeight(-forward_vel)
  217. this.animations["idle"].setEffectiveWeight(1-Math.abs(forward_vel))
  218. this.animations["idle"].setEffectiveTimeScale(0.1)
  219. this.animations["push"].setEffectiveTimeScale(forward_vel*1.5)
  220. this.animations["pull"].setEffectiveTimeScale(forward_vel*1.5)
  221. if (consensus_float(axis_RH, axis_LH) < -0.2 || turn > 1){
  222. this.strafe_left = Math.min(1, this.strafe_left + 0.1)
  223. }
  224. if (consensus_float(axis_RH, axis_LH) > 0.2 || turn < -1){
  225. this.strafe_right = Math.min(1, this.strafe_right + 0.1)
  226. }
  227. this.animations["strafe"].setEffectiveWeight(this.strafe_left + this.strafe_right)
  228. this.animations["strafe"].setEffectiveTimeScale((this.strafe_left + -this.strafe_right)*4)
  229. this.strafe_left *= 0.9
  230. this.strafe_right *= 0.9
  231. this.mixer.update(dt)
  232. this.last_position = player.ball.position.clone()
  233. }
  234. }