ship.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. export class Ship {
  2. constructor(canvas, x, y, color = "crimson") {
  3. this.canvas = canvas;
  4. this.position = { x: x, y: y };
  5. this.color = color;
  6. this.velocity = { x: 0.1, y: -0.1 };
  7. this.acceleration = { x: 0, y: 0 };
  8. this.target = { x: canvas.width / 2, y: canvas.height / 2 };
  9. this.futurePoint = { x: x, y: y };
  10. this.angle = 0;
  11. this.debug = false;
  12. this.wrapDelay = 0;
  13. this.width = 10;
  14. this.height = 15;
  15. }
  16. update(delta) {
  17. this.velocity.x += this.acceleration.x;
  18. this.position.x += this.velocity.x;
  19. this.velocity.y += this.acceleration.y;
  20. this.position.y += this.velocity.y;
  21. this.velocity.x = Math.sign(this.velocity.x) * Math.min(5, Math.abs(this.velocity.x));
  22. this.velocity.y = Math.sign(this.velocity.y) * Math.min(5, Math.abs(this.velocity.y));
  23. this.acceleration = this.computeAcceleration();
  24. let wrappedPosition = this.screenWrap(this.position);
  25. if (wrappedPosition.x != this.position.x || wrappedPosition.y != this.position.y) {
  26. this.wrapDelay = 30;
  27. }
  28. this.position = wrappedPosition;
  29. }
  30. screenWrap(position) {
  31. let wrappedPosition = { x: position.x, y: position.y };
  32. if (position.x < 0 - 10) {
  33. wrappedPosition.x = this.canvas.width + 10;
  34. }
  35. if (position.x > this.canvas.width + 10) {
  36. wrappedPosition.x = 0 - 10;
  37. }
  38. if (position.y < 0 - 10) {
  39. wrappedPosition.y = this.canvas.height + 10;
  40. }
  41. if (position.y > this.canvas.height + 10) {
  42. wrappedPosition.y = 0 - 10;
  43. }
  44. return wrappedPosition;
  45. }
  46. computeAcceleration() {
  47. // let angle = Math.atan2(this.target.y - this.position.y, this.target.x - this.position.x);
  48. // let distanceToTarget = Math.hypot(this.target.y - this.position.y, this.target.x - this.position.x);
  49. // if (this.position.x != this.target.x) {
  50. // acceleration.x = 0.1 * Math.cos(angle);
  51. // }
  52. // if (this.position.y != this.target.y) {
  53. // acceleration.y = 0.1 * Math.sin(angle);
  54. // }
  55. if (this.wrapDelay > 0) {
  56. this.wrapDelay--;
  57. return this.acceleration;
  58. }
  59. let acceleration = { x: 0, y: 0 };
  60. // //predictive slowing
  61. this.futurePoint = {
  62. x: this.position.x,
  63. y: this.position.y
  64. };
  65. let futureVelocity = { x: this.velocity.x, y: this.velocity.y };
  66. let isCloseEnough = false;
  67. for (let i = 0; i < 10; i++) {
  68. this.futurePoint.x += futureVelocity.x;
  69. this.futurePoint.y += futureVelocity.y;
  70. if (this.closeEnough(this.target.x, this.futurePoint.x, this.canvas.width / 8) &&
  71. this.closeEnough(this.target.y, this.futurePoint.y, this.canvas.height / 8)) {
  72. isCloseEnough = true;
  73. break;
  74. }
  75. }
  76. this.angle = Math.atan2(this.target.y - this.position.y, this.target.x - this.position.x);
  77. let distanceToTarget = Math.hypot(this.target.y - this.position.y, this.target.x - this.position.x);
  78. if (!isCloseEnough) {
  79. acceleration.x = 0.1 * Math.cos(this.angle);
  80. acceleration.y = 0.1 * Math.sin(this.angle);
  81. }
  82. return acceleration;
  83. }
  84. closeEnough(n1, n2, amount = 0.1) {
  85. return Math.abs(n1 - n2) <= amount;
  86. }
  87. draw(context) {
  88. context.save();
  89. context.translate(this.position.x, this.position.y);
  90. context.rotate(this.angle - (Math.PI / 2));
  91. context.fillStyle = this.color;
  92. context.beginPath();
  93. this.shipShape(context);
  94. context.fill();
  95. if (this.acceleration.x != 0 || this.acceleration.y != 0) {
  96. context.fillStyle = "cyan";
  97. context.beginPath();
  98. this.shipThrustShape(context);
  99. context.fill();
  100. }
  101. context.restore();
  102. if (this.debug) {
  103. context.strokeStyle = "yellow";
  104. context.beginPath();
  105. context.moveTo(this.position.x, this.position.y);
  106. context.lineTo(this.target.x, this.target.y);
  107. context.stroke();
  108. context.strokeStyle = "red";
  109. context.beginPath();
  110. context.moveTo(this.position.x, this.position.y);
  111. context.lineTo(this.canvas.width, this.position.y);
  112. context.stroke();
  113. context.strokeStyle = "red";
  114. context.beginPath();
  115. context.moveTo(this.position.x, this.position.y);
  116. context.lineTo(0, this.position.y);
  117. context.stroke();
  118. context.strokeStyle = "cyan";
  119. context.beginPath();
  120. context.moveTo(this.position.x, this.position.y);
  121. context.lineTo(this.position.x, this.canvas.height);
  122. context.stroke();
  123. context.strokeStyle = "cyan";
  124. context.beginPath();
  125. context.moveTo(this.position.x, this.position.y);
  126. context.lineTo(this.position.x, 0);
  127. context.stroke();
  128. context.fillStyle = "yellow";
  129. context.beginPath();
  130. context.moveTo(this.position.x, this.position.y);
  131. context.arc(this.futurePoint.x, this.futurePoint.y, 2, 0, 2 * Math.PI);
  132. context.fill();
  133. }
  134. }
  135. shipShape(context) {
  136. context.moveTo(this.width / 2, -this.height / 2);
  137. context.lineTo(0, this.height / 2);
  138. context.lineTo(-this.width / 2, -this.height / 2);
  139. context.lineTo(this.width / 2, -this.height / 2);
  140. }
  141. shipThrustShape(context) {
  142. context.moveTo(-this.width / 4, -this.height / 2);
  143. context.lineTo(this.width / 4, -this.height / 2);
  144. context.lineTo(0, -1.2 * this.height);
  145. context.lineTo(-this.width / 4, -this.height / 2);
  146. }
  147. setTarget(x, y) {
  148. this.target.x = x;
  149. this.target.y = y;
  150. }
  151. }