You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Level.js 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import structures from "./structures.js";
  2. export default class Level {
  3. constructor(canvas) {
  4. this.canvas = canvas;
  5. this.ctx = canvas.getContext("2d");
  6. this.lastTime = null;
  7. this.raf = null;
  8. this.minPhysFPS = 20;
  9. this.minFPS = 5;
  10. this.backgroundColor = "#87CEFA";
  11. this.layerOrder = {
  12. platform: 0,
  13. player: 1,
  14. };
  15. this.layers = [];
  16. this.entities = [];
  17. this.colliders = [];
  18. this.structures = [];
  19. this.evts = [];
  20. }
  21. spawnEntity(ent, x, y) {
  22. ent.bounds.pos.set(x, y);
  23. let layerIdx = this.layerOrder[ent.layerName];
  24. console.log(ent.layerName, this.layerOrder, layerIdx);
  25. if (layerIdx == null)
  26. throw new Error("Unknown layer name: "+ent.layerName);
  27. if (this.layers[layerIdx] == null)
  28. this.layers[layerIdx] = [];
  29. this.entities.push(ent);
  30. this.layers[layerIdx].push(ent);
  31. if (ent.has("collider"))
  32. this.colliders.push(ent);
  33. ent._init();
  34. }
  35. spawnStructure(structure, x, y) {
  36. structure.bounds.pos.set(x, y);
  37. this.structures.push(structure);
  38. structure.init();
  39. }
  40. physicsLayer(dt, layer) {
  41. layer.forEach(ent =>
  42. ent._update(dt));
  43. layer.forEach(ent =>
  44. ent._postUpdate(dt));
  45. }
  46. physics(dt) {
  47. // Collide with structures
  48. this.structures.forEach(s => {
  49. if (!s.attrs.wall && !s.attrs.platform)
  50. return;
  51. this.colliders.forEach(ent => {
  52. let bounds = s.collidesWith(ent.bounds);
  53. if (!bounds)
  54. return;
  55. ent.t.collider.collideStructure(s, bounds);
  56. });
  57. });
  58. // Collide with entities
  59. this.colliders.forEach(ent => {
  60. this.colliders.forEach(ent2 => {
  61. if (ent === ent2 || !ent.bounds.intersects(ent2.bounds))
  62. return;
  63. ent.t.collider.collideEntity(ent2);
  64. });
  65. });
  66. this.layers.forEach(l => this.physicsLayer(dt, l));
  67. }
  68. draw() {
  69. this.ctx.resetTransform();
  70. this.ctx.beginPath();
  71. this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
  72. this.layers.forEach(l => l.forEach(ent => ent.draw(this.ctx)));
  73. this.structures.forEach(struct => struct.draw(this.ctx));
  74. }
  75. update(time) {
  76. if (this.lastTime != null) {
  77. let dt = (time - this.lastTime) / 1000;
  78. if (1 / dt < this.minFPS) {
  79. console.log(
  80. "Too long between updates ("+dt.toFixed(2)+"s, "+
  81. (1 / dt).toFixed(2)+"FPS). Skipping.");
  82. } else {
  83. // We accept really low FPSes,
  84. // but if the FPS gets too low, we want to run multiple
  85. // physics steps in the frame.
  86. let physdt = dt;
  87. let nSteps = 1;
  88. while (1 / physdt < this.minPhysFPS) {
  89. physdt /= 2;
  90. nSteps *= 2;
  91. }
  92. if (nSteps > 1) {
  93. console.log(
  94. "Too long between frames for physics ("+dt.toFixed(2)+"s). "+
  95. "Running "+nSteps+" simulations ("+physdt.toFixed(2)+"s) in one frame.");
  96. }
  97. for (let i = 0; i < nSteps; ++i)
  98. this.physics(physdt);
  99. }
  100. this.draw();
  101. }
  102. this.lastTime = time;
  103. this.raf = requestAnimationFrame(this.update.bind(this));
  104. }
  105. start() {
  106. this.canvas.style.background = this.backgroundColor;
  107. if (this.raf == null) {
  108. this.lastTime = null;
  109. this.update();
  110. console.log("Started.");
  111. }
  112. }
  113. stop() {
  114. if (this.raf != null) {
  115. cancelAnimationFrame(this.raf);
  116. this.raf = null;
  117. console.log("Stopped.");
  118. }
  119. }
  120. }