kolaslab commited on
Commit
ca3b1e9
1 Parent(s): 815e811

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +290 -255
index.html CHANGED
@@ -1,314 +1,349 @@
1
  <!DOCTYPE html>
2
- <html>
3
  <head>
4
  <meta charset="UTF-8">
5
- <title>Space Shooter</title>
 
 
6
  <style>
7
- body {
8
  margin: 0;
9
- overflow: hidden;
10
- background: black;
 
 
 
 
 
 
 
11
  display: flex;
12
  justify-content: center;
13
  align-items: center;
14
- height: 100vh;
15
- font-family: Arial;
16
- color: white;
17
  }
18
 
19
- #gameCanvas {
20
- border: 2px solid white;
21
- background-image:
22
- radial-gradient(white 1px, transparent 0),
23
- radial-gradient(white 1px, transparent 0);
24
- background-size: 50px 50px;
25
- background-position: 0 0, 25px 25px;
26
- display: none;
27
  }
28
 
29
- #menu {
30
  text-align: center;
 
 
 
 
31
  }
32
 
33
- .difficulty-btn {
34
- padding: 10px 20px;
35
- margin: 10px;
36
- font-size: 18px;
37
- cursor: pointer;
38
- background: #333;
39
- color: white;
40
- border: 2px solid white;
41
- border-radius: 5px;
42
  }
43
 
44
- .difficulty-btn:hover {
45
- background: #555;
 
 
 
46
  }
47
 
48
- #score {
49
- position: absolute;
50
- top: 20px;
51
- left: 20px;
52
- font-size: 24px;
 
 
 
 
53
  }
54
 
55
- #gameOver {
56
- position: absolute;
57
- font-size: 48px;
58
- text-align: center;
59
- display: none;
60
  }
61
- </style>
62
- </head>
63
- <body>
64
- <div id="menu">
65
- <h1>Space Shooter</h1>
66
- <h2>Select Difficulty:</h2>
67
- <button class="difficulty-btn" onclick="startGame('easy')">Easy</button>
68
- <button class="difficulty-btn" onclick="startGame('medium')">Medium</button>
69
- <button class="difficulty-btn" onclick="startGame('hard')">Hard</button>
70
- </div>
71
- <div id="score">Score: <span id="scoreValue">0</span></div>
72
- <canvas id="gameCanvas" width="800" height="600"></canvas>
73
- <div id="gameOver">Game Over!<br>Press R to Restart</div>
74
 
75
- <script>
76
- const canvas = document.getElementById('gameCanvas');
77
- const ctx = canvas.getContext('2d');
78
- const scoreEl = document.getElementById('scoreValue');
79
- const gameOverEl = document.getElementById('gameOver');
80
- const menuEl = document.getElementById('menu');
81
-
82
- let audioCtx;
83
- let player = {
84
- x: canvas.width/2,
85
- y: 500,
86
- speed: 5,
87
- size: 30
88
- };
89
- let bullets = [];
90
- let enemies = [];
91
- let score = 0;
92
- let gameOver = false;
93
- let keys = {
94
- left: false,
95
- right: false
96
- };
97
-
98
- let gameConfig = {
99
- easy: {
100
- enemySpeed: 2,
101
- spawnRate: 0.01,
102
- playerSpeed: 5
103
- },
104
- medium: {
105
- enemySpeed: 3,
106
- spawnRate: 0.02,
107
- playerSpeed: 6
108
- },
109
- hard: {
110
- enemySpeed: 4,
111
- spawnRate: 0.03,
112
- playerSpeed: 7
113
- }
114
- };
115
-
116
- let currentConfig;
117
-
118
- function startGame(difficulty) {
119
- menuEl.style.display = 'none';
120
- canvas.style.display = 'block';
121
- currentConfig = gameConfig[difficulty];
122
- player.speed = currentConfig.playerSpeed;
123
- resetGame();
124
- if (!audioCtx) {
125
- audio = setupAudio();
126
- }
127
  }
128
 
129
- function setupAudio() {
130
- audioCtx = new (window.AudioContext || window.webkitAudioContext)();
131
-
132
- function playNote(freq, startTime, duration) {
133
- const osc = audioCtx.createOscillator();
134
- const gain = audioCtx.createGain();
135
-
136
- osc.connect(gain);
137
- gain.connect(audioCtx.destination);
138
-
139
- osc.type = 'square';
140
- osc.frequency.setValueAtTime(freq, startTime);
141
-
142
- gain.gain.setValueAtTime(0.1, startTime);
143
- gain.gain.exponentialRampToValueAtTime(0.01, startTime + duration);
144
-
145
- osc.start(startTime);
146
- osc.stop(startTime + duration);
147
- }
148
-
149
- function playBGM() {
150
- const notes = [440, 523, 659, 784];
151
- setInterval(() => {
152
- if (!gameOver) {
153
- playNote(notes[Math.floor(Math.random() * notes.length)],
154
- audioCtx.currentTime,
155
- 0.1);
156
- }
157
- }, 200);
158
- }
159
-
160
- function playShoot() {
161
- playNote(880, audioCtx.currentTime, 0.1);
162
- }
163
-
164
- playBGM();
165
- return { playShoot };
166
  }
167
 
168
- function drawPlayer() {
169
- ctx.fillStyle = 'yellow';
170
- ctx.beginPath();
171
- for (let i = 0; i < 5; i++) {
172
- const angle = (i * 4 * Math.PI) / 5 - Math.PI / 2;
173
- const x = player.x + player.size * Math.cos(angle);
174
- const y = player.y + player.size * Math.sin(angle);
175
- i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
176
- }
177
- ctx.closePath();
178
- ctx.fill();
179
  }
180
 
181
- function createEnemy() {
182
- if (Math.random() < currentConfig.spawnRate) {
183
- enemies.push({
184
- x: Math.random() * (canvas.width - 30) + 15,
185
- y: -30,
186
- speed: currentConfig.enemySpeed
187
- });
188
- }
189
  }
190
 
191
- function updatePlayer() {
192
- if (keys.left) {
193
- player.x = Math.max(player.size, player.x - player.speed);
194
- }
195
- if (keys.right) {
196
- player.x = Math.min(canvas.width - player.size, player.x + player.speed);
197
- }
 
 
 
 
198
  }
199
 
200
- function updateGame() {
201
- updatePlayer();
202
-
203
- bullets = bullets.filter(bullet => {
204
- bullet.y -= 8;
205
- return bullet.y > 0;
206
- });
207
 
208
- enemies = enemies.filter(enemy => {
209
- enemy.y += enemy.speed;
 
 
 
 
210
 
211
- if (Math.abs(enemy.x - player.x) < 30 &&
212
- Math.abs(enemy.y - player.y) < 30) {
213
- gameOver = true;
214
- gameOverEl.style.display = 'block';
215
- }
216
 
217
- return enemy.y < canvas.height;
218
- });
 
 
219
 
220
- bullets.forEach((bullet, bi) => {
221
- enemies.forEach((enemy, ei) => {
222
- if (Math.abs(bullet.x - enemy.x) < 30 &&
223
- Math.abs(bullet.y - enemy.y) < 30) {
224
- bullets.splice(bi, 1);
225
- enemies.splice(ei, 1);
226
- score += 100;
227
- scoreEl.textContent = score;
228
- }
229
- });
230
- });
231
 
232
- createEnemy();
 
 
 
 
 
 
 
233
  }
234
 
235
- function draw() {
236
- ctx.clearRect(0, 0, canvas.width, canvas.height);
237
-
238
- drawPlayer();
239
 
240
- ctx.fillStyle = 'white';
241
- bullets.forEach(bullet => {
242
- ctx.fillRect(bullet.x - 2, bullet.y - 8, 4, 16);
243
- });
 
244
 
245
- ctx.font = '30px Arial';
246
- enemies.forEach(enemy => {
247
- ctx.fillText('🐱', enemy.x - 15, enemy.y + 10);
248
- });
 
249
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
 
251
- function gameLoop() {
252
- if (!gameOver) {
253
- updateGame();
254
- draw();
255
- requestAnimationFrame(gameLoop);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  }
257
  }
258
 
259
- function resetGame() {
260
- player.x = canvas.width/2;
261
- bullets = [];
262
- enemies = [];
263
- score = 0;
264
- gameOver = false;
265
- scoreEl.textContent = '0';
266
- gameOverEl.style.display = 'none';
267
- gameLoop();
268
  }
269
 
270
- function returnToMenu() {
271
- canvas.style.display = 'none';
272
- menuEl.style.display = 'block';
273
- gameOverEl.style.display = 'none';
 
 
 
 
 
 
 
 
274
  }
275
 
276
- let audio;
 
 
 
 
 
277
 
278
- document.addEventListener('keydown', (e) => {
279
- if (gameOver) {
280
- if (e.key.toLowerCase() === 'r') {
281
- returnToMenu();
282
- }
283
- return;
284
- }
 
 
 
285
 
286
- switch(e.key) {
287
- case 'ArrowLeft':
288
- keys.left = true;
289
- break;
290
- case 'ArrowRight':
291
- keys.right = true;
292
- break;
293
- case ' ':
294
- bullets.push({
295
- x: player.x,
296
- y: player.y - 20
297
- });
298
- if (audio) audio.playShoot();
299
- break;
300
  }
 
301
  });
302
 
303
- document.addEventListener('keyup', (e) => {
304
- switch(e.key) {
305
- case 'ArrowLeft':
306
- keys.left = false;
307
- break;
308
- case 'ArrowRight':
309
- keys.right = false;
310
- break;
311
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  });
313
  </script>
314
  </body>
 
1
  <!DOCTYPE html>
2
+ <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>8-Bit Game Music Player</title>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
8
  <style>
9
+ * {
10
  margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ background: #000;
17
+ color: #fff;
18
+ font-family: 'Courier New', monospace;
19
+ min-height: 100vh;
20
  display: flex;
21
  justify-content: center;
22
  align-items: center;
23
+ background-image: radial-gradient(#0f0 1px, transparent 1px);
24
+ background-size: 30px 30px;
 
25
  }
26
 
27
+ .player {
28
+ background: rgba(0, 20, 0, 0.95);
29
+ border: 2px solid #0f0;
30
+ padding: 2rem;
31
+ border-radius: 15px;
32
+ width: 90%;
33
+ max-width: 400px;
34
+ box-shadow: 0 0 30px rgba(0, 255, 0, 0.2);
35
  }
36
 
37
+ .title {
38
  text-align: center;
39
+ color: #0f0;
40
+ text-shadow: 0 0 10px #0f0;
41
+ margin-bottom: 1.5rem;
42
+ font-size: 1.5rem;
43
  }
44
 
45
+ .time-display {
46
+ text-align: center;
47
+ font-size: 1.2rem;
48
+ color: #0f0;
49
+ margin: 1rem 0;
50
+ font-family: 'Digital-7', monospace;
 
 
 
51
  }
52
 
53
+ .controls {
54
+ display: flex;
55
+ justify-content: center;
56
+ gap: 1rem;
57
+ margin: 1rem 0;
58
  }
59
 
60
+ .btn {
61
+ background: none;
62
+ border: 2px solid #0f0;
63
+ color: #0f0;
64
+ padding: 0.5rem 1rem;
65
+ cursor: pointer;
66
+ border-radius: 5px;
67
+ font-family: inherit;
68
+ transition: all 0.3s;
69
  }
70
 
71
+ .btn:hover {
72
+ background: #0f0;
73
+ color: #000;
74
+ box-shadow: 0 0 15px #0f0;
 
75
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
+ .volume-control {
78
+ margin: 1.5rem 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
80
 
81
+ .volume-slider {
82
+ width: 100%;
83
+ -webkit-appearance: none;
84
+ height: 5px;
85
+ background: #0f0;
86
+ border-radius: 5px;
87
+ opacity: 0.7;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
 
90
+ .volume-slider::-webkit-slider-thumb {
91
+ -webkit-appearance: none;
92
+ width: 15px;
93
+ height: 15px;
94
+ background: #0f0;
95
+ border-radius: 50%;
96
+ cursor: pointer;
97
+ box-shadow: 0 0 10px #0f0;
 
 
 
98
  }
99
 
100
+ .track-list {
101
+ margin-top: 1.5rem;
 
 
 
 
 
 
102
  }
103
 
104
+ .track {
105
+ background: rgba(0, 50, 0, 0.3);
106
+ margin: 0.5rem 0;
107
+ padding: 1rem;
108
+ border: 1px solid #0f0;
109
+ border-radius: 8px;
110
+ cursor: pointer;
111
+ display: flex;
112
+ justify-content: space-between;
113
+ align-items: center;
114
+ transition: all 0.3s;
115
  }
116
 
117
+ .track:hover {
118
+ background: rgba(0, 100, 0, 0.3);
119
+ transform: translateX(5px);
120
+ }
 
 
 
121
 
122
+ .track.playing {
123
+ background: rgba(0, 255, 0, 0.2);
124
+ border-color: #0f0;
125
+ box-shadow: 0 0 15px rgba(0, 255, 0, 0.3);
126
+ animation: pulse 2s infinite;
127
+ }
128
 
129
+ .track-info {
130
+ flex: 1;
131
+ }
 
 
132
 
133
+ .track-title {
134
+ font-weight: bold;
135
+ color: #0f0;
136
+ }
137
 
138
+ .track-duration {
139
+ font-size: 0.8em;
140
+ color: #0a0;
141
+ }
 
 
 
 
 
 
 
142
 
143
+ .status-indicator {
144
+ width: 12px;
145
+ height: 12px;
146
+ border-radius: 50%;
147
+ margin-left: 1rem;
148
+ background: transparent;
149
+ border: 2px solid #0f0;
150
+ transition: all 0.3s;
151
  }
152
 
153
+ .playing .status-indicator {
154
+ background: #0f0;
155
+ box-shadow: 0 0 10px #0f0;
156
+ }
157
 
158
+ @keyframes pulse {
159
+ 0% { border-color: rgba(0, 255, 0, 0.5); }
160
+ 50% { border-color: rgba(0, 255, 0, 1); }
161
+ 100% { border-color: rgba(0, 255, 0, 0.5); }
162
+ }
163
 
164
+ @media (max-width: 480px) {
165
+ .player {
166
+ width: 95%;
167
+ padding: 1rem;
168
+ }
169
  }
170
+ </style>
171
+ </head>
172
+ <body>
173
+ <div class="player">
174
+ <h1 class="title">8-BIT GAME MUSIC</h1>
175
+
176
+ <div class="time-display">
177
+ <span id="current-time">00:00</span> / <span id="total-time">00:00</span>
178
+ </div>
179
+
180
+ <div class="controls">
181
+ <button class="btn" id="play-pause">PLAY</button>
182
+ <button class="btn" id="stop">STOP</button>
183
+ </div>
184
+
185
+ <div class="volume-control">
186
+ <input type="range" class="volume-slider" min="0" max="1" step="0.01" value="0.7">
187
+ </div>
188
+
189
+ <div class="track-list">
190
+ <div class="track" data-song="space">
191
+ <div class="track-info">
192
+ <div class="track-title">SPACE ADVENTURE</div>
193
+ <div class="track-duration">1:30</div>
194
+ </div>
195
+ <div class="status-indicator"></div>
196
+ </div>
197
+ <div class="track" data-song="battle">
198
+ <div class="track-info">
199
+ <div class="track-title">BATTLE ZONE</div>
200
+ <div class="track-duration">1:45</div>
201
+ </div>
202
+ <div class="status-indicator"></div>
203
+ </div>
204
+ <div class="track" data-song="tetris">
205
+ <div class="track-info">
206
+ <div class="track-title">TETRIS THEME</div>
207
+ <div class="track-duration">2:00</div>
208
+ </div>
209
+ <div class="status-indicator"></div>
210
+ </div>
211
+ </div>
212
+ </div>
213
 
214
+ <script>
215
+ let currentTime = 0;
216
+ let isPlaying = false;
217
+ let currentTrack = null;
218
+ let currentTrackElement = null;
219
+
220
+ const synth = new Tone.PolySynth(Tone.Synth, {
221
+ oscillator: { type: "square8" },
222
+ envelope: {
223
+ attack: 0.02,
224
+ decay: 0.1,
225
+ sustain: 0.3,
226
+ release: 0.1
227
+ }
228
+ }).connect(new Tone.Volume(-12).toDestination());
229
+
230
+ // 우주 테마
231
+ const spaceSequence = [
232
+ ["E4", "8n"], ["G4", "8n"], ["C5", "4n"], ["G4", "8n"],
233
+ ["C5", "8n"], ["E5", "4n"], ["C5", "8n"], ["G4", "8n"],
234
+ ["E4", "8n"], ["C4", "4n"], ["G4", "8n"], ["E4", "8n"],
235
+ ["C4", "2n"]
236
+ ];
237
+
238
+ // 전쟁 테마
239
+ const battleSequence = [
240
+ ["C4", "16n"], ["C4", "16n"], ["G4", "8n"], ["E4", "16n"],
241
+ ["F4", "16n"], ["G4", "8n"], ["C4", "8n"], ["G3", "8n"],
242
+ ["C4", "4n"], ["E4", "8n"], ["D4", "8n"], ["C4", "2n"]
243
+ ];
244
+
245
+ // 테트리스 테마
246
+ const tetrisSequence = [
247
+ ["E5", "4n"], ["B4", "8n"], ["C5", "8n"], ["D5", "4n"],
248
+ ["C5", "8n"], ["B4", "8n"], ["A4", "4n"], ["A4", "8n"],
249
+ ["C5", "8n"], ["E5", "4n"], ["D5", "8n"], ["C5", "8n"],
250
+ ["B4", "4n"], ["C5", "8n"], ["D5", "4n"], ["E5", "4n"],
251
+ ["C5", "4n"], ["A4", "4n"], ["A4", "2n"]
252
+ ];
253
+
254
+ function updateTimer() {
255
+ if (isPlaying) {
256
+ currentTime += 0.1;
257
+ document.getElementById('current-time').textContent =
258
+ formatTime(currentTime);
259
+ setTimeout(updateTimer, 100);
260
  }
261
  }
262
 
263
+ function formatTime(time) {
264
+ const minutes = Math.floor(time / 60);
265
+ const seconds = Math.floor(time % 60);
266
+ return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
 
 
 
 
 
267
  }
268
 
269
+ function stopCurrentTrack() {
270
+ if (currentTrack) {
271
+ currentTrack.stop();
272
+ currentTrack.dispose();
273
+ }
274
+ if (currentTrackElement) {
275
+ currentTrackElement.classList.remove('playing');
276
+ }
277
+ isPlaying = false;
278
+ currentTime = 0;
279
+ document.getElementById('current-time').textContent = '00:00';
280
+ document.getElementById('play-pause').textContent = 'PLAY';
281
  }
282
 
283
+ function createAndPlaySequence(sequence, element) {
284
+ stopCurrentTrack();
285
+
286
+ currentTrack = new Tone.Part((time, note) => {
287
+ synth.triggerAttackRelease(note[0], note[1], time);
288
+ }, sequence.map((note, i) => [i * 0.25, note]));
289
 
290
+ currentTrack.loop = true;
291
+ currentTrack.start(0);
292
+ Tone.Transport.start();
293
+
294
+ currentTrackElement = element;
295
+ element.classList.add('playing');
296
+ isPlaying = true;
297
+ document.getElementById('play-pause').textContent = 'PAUSE';
298
+ updateTimer();
299
+ }
300
 
301
+ document.getElementById('play-pause').addEventListener('click', () => {
302
+ if (!currentTrack) return;
303
+
304
+ if (isPlaying) {
305
+ Tone.Transport.pause();
306
+ document.getElementById('play-pause').textContent = 'PLAY';
307
+ } else {
308
+ Tone.Transport.start();
309
+ document.getElementById('play-pause').textContent = 'PAUSE';
310
+ updateTimer();
 
 
 
 
311
  }
312
+ isPlaying = !isPlaying;
313
  });
314
 
315
+ document.getElementById('stop').addEventListener('click', stopCurrentTrack);
316
+
317
+ document.querySelector('.volume-slider').addEventListener('input', (e) => {
318
+ synth.volume.value = Tone.gainToDb(parseFloat(e.target.value));
319
+ });
320
+
321
+ document.querySelectorAll('.track').forEach(track => {
322
+ track.addEventListener('click', async () => {
323
+ await Tone.start();
324
+
325
+ const songType = track.dataset.song;
326
+
327
+ if (currentTrackElement === track && isPlaying) {
328
+ stopCurrentTrack();
329
+ return;
330
+ }
331
+
332
+ switch(songType) {
333
+ case 'space':
334
+ document.getElementById('total-time').textContent = '01:30';
335
+ createAndPlaySequence(spaceSequence, track);
336
+ break;
337
+ case 'battle':
338
+ document.getElementById('total-time').textContent = '01:45';
339
+ createAndPlaySequence(battleSequence, track);
340
+ break;
341
+ case 'tetris':
342
+ document.getElementById('total-time').textContent = '02:00';
343
+ createAndPlaySequence(tetrisSequence, track);
344
+ break;
345
+ }
346
+ });
347
  });
348
  </script>
349
  </body>