Game for OLC Code Jam 2022
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. const $ = (s) => document.querySelector(s);
  2. const ROOT = $('#root');
  3. const LIVES_LABEL = $('#lives');
  4. const SOUND_BUTTON = $('#sound');
  5. const CANVAS = $('#canvas');
  6. const CTX = CANVAS.getContext('2d');
  7. const X_BOUND = 0.05;
  8. const GAME_OVER_BOUND = 0.9;
  9. const MAX_RAINDROPS = 100;
  10. const clouds = new Set();
  11. const bullets = new Set();
  12. const raindrops = new Set();
  13. let shootingPad = new ShootingPad();
  14. let running = false;
  15. let direction = 1;
  16. let soundEnabled = localStorage.soundEnabled !== 'false';
  17. const initCloudField = () => {
  18. for (let y = 0; y < 6; y++) {
  19. for (let x = 0; x < 11; x++) {
  20. const cloud = new Cloud(X_BOUND + 0.06 * x, 0.04 + 0.06 * y, 0.03, 0.02);
  21. clouds.add(cloud);
  22. }
  23. }
  24. }
  25. const initRainEffect = () => {
  26. noise.seed(Math.random());
  27. for (let i = 0; i < MAX_RAINDROPS; i++) {
  28. const raindrop = new BackgroundRaindrop(
  29. randRange(-0.5, 1.5),
  30. randRange(-1, -0.1),
  31. randRange(0.01, 0.03),
  32. );
  33. raindrops.add(raindrop);
  34. }
  35. }
  36. const updateSoundButton = () => {
  37. SOUND_BUTTON.textContent = soundEnabled ? '🔊' : '🔈';
  38. SOUND_BUTTON.title = `Sound ${soundEnabled ? 'enabled' : 'disabled'}`;
  39. }
  40. const start = () => {
  41. $('.result')?.remove();
  42. shootingPad?.destroy();
  43. shootingPad = new ShootingPad();
  44. bullets.forEach(c => c.destroy());
  45. bullets.clear();
  46. clouds.forEach(c => c.destroy());
  47. clouds.clear();
  48. Cloud.reset();
  49. initCloudField();
  50. running = true;
  51. }
  52. const gameOver = (result) => {
  53. const resultLabel = document.createElement('div');
  54. resultLabel.className = 'result';
  55. resultLabel.textContent = result;
  56. ROOT.appendChild(resultLabel);
  57. running = false;
  58. }
  59. const loop = () => {
  60. document.title = `${fps()} FPS | CloudInvaders | OLC Codejam 2022`;
  61. CTX.clearRect(0, 0, CANVAS.width, CANVAS.height);
  62. raindrops.forEach(d => {
  63. d.update();
  64. d.draw();
  65. });
  66. if (running) {
  67. let directionUpdated = false;
  68. let isOver = false;
  69. Array.from(clouds).forEach(c => {
  70. c.update(direction);
  71. c.draw();
  72. if (c.x < X_BOUND || c.x > 1 - X_BOUND) {
  73. directionUpdated = true;
  74. }
  75. if (c.y >= GAME_OVER_BOUND) {
  76. isOver = true;
  77. }
  78. if (!c.alive) {
  79. clouds.delete(c);
  80. }
  81. });
  82. if (directionUpdated) {
  83. direction = -direction;
  84. }
  85. if (isOver) {
  86. gameOver('Game over');
  87. }
  88. Array.from(bullets).forEach(b => {
  89. b.update();
  90. b.draw();
  91. if (!b.alive) {
  92. bullets.delete(b);
  93. }
  94. });
  95. shootingPad.update();
  96. shootingPad.draw();
  97. if (clouds.size === 0) {
  98. gameOver('WIN');
  99. }
  100. }
  101. requestAnimationFrame(loop);
  102. };
  103. SOUND_BUTTON.addEventListener('click', () => {
  104. soundEnabled = !soundEnabled;
  105. localStorage.soundEnabled = soundEnabled;
  106. updateSoundButton();
  107. });
  108. document.addEventListener('keypress', () => {
  109. if (!running) {
  110. start();
  111. }
  112. });
  113. initRainEffect();
  114. updateSoundButton();
  115. loop();
  116. gameOver('🌩 Cloud Invaders 🌩');