world.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
  2. import {math} from './math.js';
  3. import {FBXLoader} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/loaders/FBXLoader.js';
  4. export const world = (() => {
  5. const START_POS = 100;
  6. const SEPARATION_DISTANCE = 20;
  7. class WorldObject {
  8. constructor(params) {
  9. this.position = new THREE.Vector3();
  10. this.quaternion = new THREE.Quaternion();
  11. this.scale = 1.0;
  12. this.collider = new THREE.Box3();
  13. this.params_ = params;
  14. this.LoadModel_();
  15. }
  16. LoadModel_() {
  17. const texLoader = new THREE.TextureLoader();
  18. const texture = texLoader.load('./resources/DesertPack/Blend/Textures/Ground.png');
  19. texture.encoding = THREE.sRGBEncoding;
  20. const loader = new FBXLoader();
  21. loader.setPath('./resources/DesertPack/FBX/');
  22. loader.load('Cactus3.fbx', (fbx) => {
  23. fbx.scale.setScalar(0.01);
  24. this.mesh = fbx;
  25. this.params_.scene.add(this.mesh);
  26. fbx.traverse(c => {
  27. if (c.geometry) {
  28. c.geometry.computeBoundingBox();
  29. }
  30. let materials = c.material;
  31. if (!(c.material instanceof Array)) {
  32. materials = [c.material];
  33. }
  34. for (let m of materials) {
  35. if (m) {
  36. if (texture) {
  37. m.map = texture;
  38. }
  39. m.specular = new THREE.Color(0x000000);
  40. }
  41. }
  42. c.castShadow = true;
  43. c.receiveShadow = true;
  44. });
  45. });
  46. }
  47. UpdateCollider_() {
  48. this.collider.setFromObject(this.mesh);
  49. }
  50. Update(timeElapsed) {
  51. if (!this.mesh) {
  52. return;
  53. }
  54. this.mesh.position.copy(this.position);
  55. this.mesh.quaternion.copy(this.quaternion);
  56. this.mesh.scale.setScalar(this.scale);
  57. this.UpdateCollider_();
  58. }
  59. }
  60. class WorldManager {
  61. constructor(params) {
  62. this.objects_ = [];
  63. this.unused_ = [];
  64. this.speed_ = 12;
  65. this.params_ = params;
  66. this.score_ = 0.0;
  67. this.scoreText_ = '00000';
  68. this.separationDistance_ = SEPARATION_DISTANCE;
  69. }
  70. GetColliders() {
  71. return this.objects_;
  72. }
  73. LastObjectPosition_() {
  74. if (this.objects_.length == 0) {
  75. return SEPARATION_DISTANCE;
  76. }
  77. return this.objects_[this.objects_.length - 1].position.x;
  78. }
  79. SpawnObj_(scale, offset) {
  80. let obj = null;
  81. if (this.unused_.length > 0) {
  82. obj = this.unused_.pop();
  83. obj.mesh.visible = true;
  84. } else {
  85. obj = new WorldObject(this.params_);
  86. }
  87. obj.quaternion.setFromAxisAngle(
  88. new THREE.Vector3(0, 1, 0), Math.random() * Math.PI * 2.0);
  89. obj.position.x = START_POS + offset;
  90. obj.scale = scale * 0.01;
  91. this.objects_.push(obj);
  92. }
  93. SpawnCluster_() {
  94. const scaleIndex = math.rand_int(0, 1);
  95. const scales = [1, 0.5];
  96. const ranges = [2, 3];
  97. const scale = scales[scaleIndex];
  98. const numObjects = math.rand_int(1, ranges[scaleIndex]);
  99. for (let i = 0; i < numObjects; ++i) {
  100. const offset = i * 1 * scale;
  101. this.SpawnObj_(scale, offset);
  102. }
  103. }
  104. MaybeSpawn_() {
  105. const closest = this.LastObjectPosition_();
  106. if (Math.abs(START_POS - closest) > this.separationDistance_) {
  107. this.SpawnCluster_();
  108. this.separationDistance_ = math.rand_range(SEPARATION_DISTANCE, SEPARATION_DISTANCE * 1.5);
  109. }
  110. }
  111. Update(timeElapsed) {
  112. this.MaybeSpawn_();
  113. this.UpdateColliders_(timeElapsed);
  114. this.UpdateScore_(timeElapsed);
  115. }
  116. UpdateScore_(timeElapsed) {
  117. this.score_ += timeElapsed * 10.0;
  118. const scoreText = Math.round(this.score_).toLocaleString(
  119. 'en-US', {minimumIntegerDigits: 5, useGrouping: false});
  120. if (scoreText == this.scoreText_) {
  121. return;
  122. }
  123. document.getElementById('score-text').innerText = scoreText;
  124. }
  125. UpdateColliders_(timeElapsed) {
  126. const invisible = [];
  127. const visible = [];
  128. for (let obj of this.objects_) {
  129. obj.position.x -= timeElapsed * this.speed_;
  130. if (obj.position.x < -20) {
  131. invisible.push(obj);
  132. obj.mesh.visible = false;
  133. } else {
  134. visible.push(obj);
  135. }
  136. obj.Update(timeElapsed);
  137. }
  138. this.objects_ = visible;
  139. this.unused_.push(...invisible);
  140. }
  141. };
  142. return {
  143. WorldManager: WorldManager,
  144. };
  145. })();