2 Commits

Author SHA1 Message Date
  Pabloader cd44bbe7a8 Title screen 2 years ago
  Pabloader 4bbf215d09 Sound 2 years ago
10 changed files with 111 additions and 55 deletions
  1. 15
    1
      README.md
  2. 12
    0
      css/style.css
  3. 1
    0
      index.html
  4. 8
    0
      js/cloud.js
  5. 58
    42
      js/game.js
  6. 17
    1
      js/shooting_pad.js
  7. BIN
      snd/click.ogg
  8. BIN
      snd/hit.ogg
  9. BIN
      snd/shoot.ogg
  10. 0
    11
      todo.md

+ 15
- 1
README.md View File

1
 # About
1
 # About
2
 
2
 
3
-This game is written for [OLC Code Jam 2022](https://itch.io/jam/olc-codejam-2022)
3
+This game is written for [OLC Code Jam 2022](https://itch.io/jam/olc-codejam-2022)
4
+
5
+# TODO
6
+
7
+- [x] lives
8
+- [x] shooting platform sprite
9
+- [ ] clouds should strike lightnings sometimes
10
+- [x] bg rain effect
11
+- [ ] animated gif for clouds
12
+- [ ] boss?
13
+- [ ] umbrella shields
14
+- [ ] music
15
+- [x] sounds
16
+- [x] title screen
17
+- [x] settings

+ 12
- 0
css/style.css View File

63
     font-size: 32px;
63
     font-size: 32px;
64
 }
64
 }
65
 
65
 
66
+#sound {
67
+    position: absolute;
68
+    display: block;
69
+    top: 15px;
70
+    right: 15px;
71
+    font-size: 32px;
72
+    border: none;
73
+    background: transparent;
74
+    outline: none;
75
+    cursor: pointer;
76
+}
77
+
66
 #lives:before {
78
 #lives:before {
67
     content: '❤ ';
79
     content: '❤ ';
68
     color: red;
80
     color: red;

+ 1
- 0
index.html View File

9
         <canvas id="canvas" width="1280" height="720"></canvas>
9
         <canvas id="canvas" width="1280" height="720"></canvas>
10
         <div id="root">
10
         <div id="root">
11
             <section id="lives"></section>
11
             <section id="lives"></section>
12
+            <section id="sound"></section>
12
         </div>
13
         </div>
13
         <script src="https://cdn.jsdelivr.net/gh/josephg/noisejs/perlin.js"></script>
14
         <script src="https://cdn.jsdelivr.net/gh/josephg/noisejs/perlin.js"></script>
14
         <script src="js/utils.js"></script>
15
         <script src="js/utils.js"></script>

+ 8
- 0
js/cloud.js View File

21
 
21
 
22
             this.particles.add(cloudParticle);
22
             this.particles.add(cloudParticle);
23
         }
23
         }
24
+
25
+        this.sound = new Audio();
26
+        this.sound.src = 'snd/click.ogg';
24
     }
27
     }
25
 
28
 
26
     draw() {
29
     draw() {
67
             if (Math.random() < Cloud.shootProbability) {
70
             if (Math.random() < Cloud.shootProbability) {
68
                 const raindrop = new RainDrop(this.x, this.y + 0.009);
71
                 const raindrop = new RainDrop(this.x, this.y + 0.009);
69
                 bullets.add(raindrop);
72
                 bullets.add(raindrop);
73
+
74
+                if (soundEnabled) {
75
+                    this.sound.currentTime = 0;
76
+                    this.sound.play().catch(() => { });
77
+                }
70
             }
78
             }
71
         }
79
         }
72
     }
80
     }

+ 58
- 42
js/game.js View File

1
 const $ = (s) => document.querySelector(s);
1
 const $ = (s) => document.querySelector(s);
2
-const $$ = (s) => Array.from(document.querySelectorAll(s));
2
+
3
 const ROOT = $('#root');
3
 const ROOT = $('#root');
4
+const LIVES_LABEL = $('#lives');
5
+const SOUND_BUTTON = $('#sound');
4
 const CANVAS = $('#canvas');
6
 const CANVAS = $('#canvas');
5
 const CTX = CANVAS.getContext('2d');
7
 const CTX = CANVAS.getContext('2d');
8
+
9
+
6
 const X_BOUND = 0.05;
10
 const X_BOUND = 0.05;
7
 const GAME_OVER_BOUND = 0.9;
11
 const GAME_OVER_BOUND = 0.9;
8
 const MAX_RAINDROPS = 100;
12
 const MAX_RAINDROPS = 100;
11
 const bullets = new Set();
15
 const bullets = new Set();
12
 const raindrops = new Set();
16
 const raindrops = new Set();
13
 let shootingPad = new ShootingPad();
17
 let shootingPad = new ShootingPad();
14
-let running = true;
18
+let running = false;
15
 let direction = 1;
19
 let direction = 1;
20
+let soundEnabled = localStorage.soundEnabled !== 'false';
16
 
21
 
17
 const initCloudField = () => {
22
 const initCloudField = () => {
18
     for (let y = 0; y < 6; y++) {
23
     for (let y = 0; y < 6; y++) {
36
     }
41
     }
37
 }
42
 }
38
 
43
 
44
+const updateSoundButton = () => {
45
+    SOUND_BUTTON.textContent = soundEnabled ? '🔊' : '🔈';
46
+    SOUND_BUTTON.title = `Sound ${soundEnabled ? 'enabled' : 'disabled'}`;
47
+}
48
+
39
 const start = () => {
49
 const start = () => {
40
     $('.result')?.remove();
50
     $('.result')?.remove();
41
 
51
 
51
     initCloudField();
61
     initCloudField();
52
 
62
 
53
     running = true;
63
     running = true;
54
-    loop();
55
 }
64
 }
56
 
65
 
57
 const gameOver = (result) => {
66
 const gameOver = (result) => {
63
 }
72
 }
64
 
73
 
65
 const loop = () => {
74
 const loop = () => {
66
-    if (!running) return;
67
     document.title = `${fps()} FPS | CloudInvaders | OLC Codejam 2022`;
75
     document.title = `${fps()} FPS | CloudInvaders | OLC Codejam 2022`;
68
     CTX.clearRect(0, 0, CANVAS.width, CANVAS.height);
76
     CTX.clearRect(0, 0, CANVAS.width, CANVAS.height);
69
 
77
 
70
-    let directionUpdated = false;
71
-    let isOver = false;
72
-    Array.from(clouds).forEach(c => {
73
-        c.update(direction);
74
-        c.draw();
75
-        if (c.x < X_BOUND || c.x > 1 - X_BOUND) {
76
-            directionUpdated = true;
77
-        }
78
-        if (c.y >= GAME_OVER_BOUND) {
79
-            isOver = true;
78
+    raindrops.forEach(d => {
79
+        d.update();
80
+        d.draw();
81
+    });
82
+    if (running) {
83
+        let directionUpdated = false;
84
+        let isOver = false;
85
+        Array.from(clouds).forEach(c => {
86
+            c.update(direction);
87
+            c.draw();
88
+            if (c.x < X_BOUND || c.x > 1 - X_BOUND) {
89
+                directionUpdated = true;
90
+            }
91
+            if (c.y >= GAME_OVER_BOUND) {
92
+                isOver = true;
93
+            }
94
+            if (!c.alive) {
95
+                clouds.delete(c);
96
+            }
97
+        });
98
+
99
+        if (directionUpdated) {
100
+            direction = -direction;
80
         }
101
         }
81
-        if (!c.alive) {
82
-            clouds.delete(c);
102
+
103
+        if (isOver) {
104
+            gameOver('Game over');
83
         }
105
         }
84
-    });
85
 
106
 
86
-    if (directionUpdated) {
87
-        direction = -direction;
88
-    }
107
+        Array.from(bullets).forEach(b => {
108
+            b.update();
109
+            b.draw();
89
 
110
 
90
-    if (isOver) {
91
-        gameOver('Game over');
92
-    }
111
+            if (!b.alive) {
112
+                bullets.delete(b);
113
+            }
114
+        });
93
 
115
 
94
-    Array.from(bullets).forEach(b => {
95
-        b.update();
96
-        b.draw();
116
+        shootingPad.update();
117
+        shootingPad.draw();
97
 
118
 
98
-        if (!b.alive) {
99
-            bullets.delete(b);
119
+        if (clouds.size === 0) {
120
+            gameOver('WIN');
100
         }
121
         }
101
-    });
102
-
103
-    raindrops.forEach(d => {
104
-        d.update();
105
-        d.draw();
106
-    });
107
-
108
-    shootingPad.update();
109
-    shootingPad.draw();
110
-
111
-    if (clouds.size === 0) {
112
-        gameOver('WIN');
113
     }
122
     }
114
-
115
     requestAnimationFrame(loop);
123
     requestAnimationFrame(loop);
116
 };
124
 };
117
 
125
 
126
+SOUND_BUTTON.addEventListener('click', () => {
127
+    soundEnabled = !soundEnabled;
128
+    localStorage.soundEnabled = soundEnabled;
129
+    updateSoundButton();
130
+});
131
+
118
 document.addEventListener('keypress', () => {
132
 document.addEventListener('keypress', () => {
119
     if (!running) {
133
     if (!running) {
120
         start();
134
         start();
122
 });
136
 });
123
 
137
 
124
 initRainEffect();
138
 initRainEffect();
125
-start();
139
+updateSoundButton();
140
+loop();
141
+gameOver('🌩 Cloud Invaders 🌩');

+ 17
- 1
js/shooting_pad.js View File

21
         image.src = 'img/shooting-pad.svg';
21
         image.src = 'img/shooting-pad.svg';
22
         image.onload = () => this.image = image;
22
         image.onload = () => this.image = image;
23
 
23
 
24
+        this.shootSound = new Audio();
25
+        this.shootSound.src = 'snd/shoot.ogg';
26
+
27
+        this.hitSound = new Audio();
28
+        this.hitSound.src = 'snd/hit.ogg';
29
+
24
         this.lastShootTime = Date.now();
30
         this.lastShootTime = Date.now();
25
         this.invulnerable = false;
31
         this.invulnerable = false;
26
         this.lives = MAX_LIVES;
32
         this.lives = MAX_LIVES;
44
             this.lastShootTime = Date.now();
50
             this.lastShootTime = Date.now();
45
             const bullet = new Bullet(this.x, this.y - 0.009);
51
             const bullet = new Bullet(this.x, this.y - 0.009);
46
             bullets.add(bullet);
52
             bullets.add(bullet);
53
+
54
+            if (soundEnabled) {
55
+                this.shootSound.currentTime = 0;
56
+                this.shootSound.play().catch(() => { });
57
+            }
47
         }
58
         }
48
     }
59
     }
49
 
60
 
54
     }
65
     }
55
 
66
 
56
     initLives() {
67
     initLives() {
57
-        $('#lives').textContent = `× ${this.lives}`;
68
+        LIVES_LABEL.textContent = `× ${this.lives}`;
58
     }
69
     }
59
 
70
 
60
     lifeOver() {
71
     lifeOver() {
63
         this.lives--;
74
         this.lives--;
64
         this.initLives();
75
         this.initLives();
65
 
76
 
77
+        if (soundEnabled) {
78
+            this.hitSound.currentTime = 0;
79
+            this.hitSound.play().catch(() => { });
80
+        }
81
+
66
         if (this.lives === 0) {
82
         if (this.lives === 0) {
67
             return gameOver('Game over');
83
             return gameOver('Game over');
68
         } else {
84
         } else {

BIN
snd/click.ogg View File


BIN
snd/hit.ogg View File


BIN
snd/shoot.ogg View File


+ 0
- 11
todo.md View File

1
-# TODO
2
-
3
-- [x] lives
4
-- [x] shooting platform sprite
5
-- [ ] clouds should strike lightnings sometimes
6
-- [x] bg rain effect
7
-- [ ] animated gif for clouds
8
-- [ ] boss?
9
-- [ ] umbrella shields
10
-- [ ] music & sounds
11
-

Loading…
Cancel
Save