Spaces:
Running
Running
Add 2 files
Browse files- index.html +431 -1
- prompts.txt +2 -1
index.html
CHANGED
@@ -217,6 +217,61 @@
|
|
217 |
0% { transform: scale(0); opacity: 1; }
|
218 |
100% { transform: scale(3); opacity: 0; }
|
219 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
220 |
</style>
|
221 |
</head>
|
222 |
<body class="bg-gray-900 text-white font-sans overflow-hidden">
|
@@ -240,11 +295,114 @@
|
|
240 |
<button id="startButton" class="bg-gradient-to-r from-yellow-500 to-yellow-600 hover:from-yellow-400 hover:to-yellow-500 text-4xl text-gray-900 font-bold py-5 px-16 rounded-full transition-all transform hover:scale-105 shadow-lg glow relative z-10">
|
241 |
START COOKING
|
242 |
</button>
|
|
|
|
|
|
|
243 |
<div class="absolute bottom-8 text-gray-400 text-sm z-10">
|
244 |
Tap the beat when ingredients reach the counter
|
245 |
</div>
|
246 |
</div>
|
247 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
248 |
<!-- Game Screen -->
|
249 |
<div id="gameScreen" class="fixed inset-0 hidden flex flex-col">
|
250 |
<!-- Kitchen ambient light -->
|
@@ -258,6 +416,9 @@
|
|
258 |
<div id="comboContainer" class="text-4xl font-bold combo-text hidden">
|
259 |
🔥 <span id="combo">0</span>x
|
260 |
</div>
|
|
|
|
|
|
|
261 |
</div>
|
262 |
|
263 |
<!-- Game Area -->
|
@@ -361,6 +522,28 @@
|
|
361 |
const energyWaves = document.getElementById('energyWaves');
|
362 |
const screenFlash = document.getElementById('screenFlash');
|
363 |
const startScreenBg = document.getElementById('startScreenBg');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
364 |
|
365 |
// Initialize audio context
|
366 |
function initAudio() {
|
@@ -595,7 +778,7 @@
|
|
595 |
particle.style.transition = `all ${duration}s ease-out`;
|
596 |
particle.style.boxShadow = `0 0 ${size/2}px ${color}`;
|
597 |
|
598 |
-
document.body.appendChild(
|
599 |
|
600 |
setTimeout(() => {
|
601 |
particle.style.opacity = '0';
|
@@ -1064,6 +1247,136 @@
|
|
1064 |
}
|
1065 |
}
|
1066 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1067 |
// Initialize game
|
1068 |
function initGame() {
|
1069 |
initAudio();
|
@@ -1083,6 +1396,12 @@
|
|
1083 |
startScreenBg.appendChild(element);
|
1084 |
}
|
1085 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1086 |
// Start button
|
1087 |
startButton.addEventListener('click', () => {
|
1088 |
startScreen.classList.add('opacity-0');
|
@@ -1099,6 +1418,117 @@
|
|
1099 |
startSequence();
|
1100 |
});
|
1101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1102 |
// Tap input
|
1103 |
gameArea.addEventListener('click', handleTap);
|
1104 |
|
|
|
217 |
0% { transform: scale(0); opacity: 1; }
|
218 |
100% { transform: scale(3); opacity: 0; }
|
219 |
}
|
220 |
+
|
221 |
+
/* Settings panel */
|
222 |
+
.settings-panel {
|
223 |
+
transition: all 0.3s ease;
|
224 |
+
transform: translateY(100%);
|
225 |
+
}
|
226 |
+
|
227 |
+
.settings-panel.open {
|
228 |
+
transform: translateY(0);
|
229 |
+
}
|
230 |
+
|
231 |
+
.settings-overlay {
|
232 |
+
background-color: rgba(0, 0, 0, 0.7);
|
233 |
+
}
|
234 |
+
|
235 |
+
/* Range slider styling */
|
236 |
+
input[type="range"] {
|
237 |
+
-webkit-appearance: none;
|
238 |
+
height: 8px;
|
239 |
+
background: #4a5568;
|
240 |
+
border-radius: 4px;
|
241 |
+
}
|
242 |
+
|
243 |
+
input[type="range"]::-webkit-slider-thumb {
|
244 |
+
-webkit-appearance: none;
|
245 |
+
appearance: none;
|
246 |
+
width: 20px;
|
247 |
+
height: 20px;
|
248 |
+
border-radius: 50%;
|
249 |
+
background: #f6ad55;
|
250 |
+
cursor: pointer;
|
251 |
+
}
|
252 |
+
|
253 |
+
input[type="range"]::-moz-range-thumb {
|
254 |
+
width: 20px;
|
255 |
+
height: 20px;
|
256 |
+
border-radius: 50%;
|
257 |
+
background: #f6ad55;
|
258 |
+
cursor: pointer;
|
259 |
+
}
|
260 |
+
|
261 |
+
/* Preset buttons */
|
262 |
+
.preset-btn {
|
263 |
+
transition: all 0.2s ease;
|
264 |
+
}
|
265 |
+
|
266 |
+
.preset-btn:hover {
|
267 |
+
transform: translateY(-2px);
|
268 |
+
}
|
269 |
+
|
270 |
+
.preset-btn.active {
|
271 |
+
background-color: #f6ad55;
|
272 |
+
color: #1a202c;
|
273 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
274 |
+
}
|
275 |
</style>
|
276 |
</head>
|
277 |
<body class="bg-gray-900 text-white font-sans overflow-hidden">
|
|
|
295 |
<button id="startButton" class="bg-gradient-to-r from-yellow-500 to-yellow-600 hover:from-yellow-400 hover:to-yellow-500 text-4xl text-gray-900 font-bold py-5 px-16 rounded-full transition-all transform hover:scale-105 shadow-lg glow relative z-10">
|
296 |
START COOKING
|
297 |
</button>
|
298 |
+
<button id="settingsButton" class="absolute top-4 right-4 bg-gray-800 hover:bg-gray-700 text-yellow-400 text-2xl p-3 rounded-full transition-all transform hover:scale-110 shadow-lg">
|
299 |
+
⚙️
|
300 |
+
</button>
|
301 |
<div class="absolute bottom-8 text-gray-400 text-sm z-10">
|
302 |
Tap the beat when ingredients reach the counter
|
303 |
</div>
|
304 |
</div>
|
305 |
|
306 |
+
<!-- Settings Panel -->
|
307 |
+
<div id="settingsPanel" class="fixed inset-0 z-20 flex items-end settings-overlay hidden">
|
308 |
+
<div class="settings-panel bg-gray-800 w-full rounded-t-3xl p-6 max-h-[80vh] overflow-y-auto">
|
309 |
+
<div class="flex justify-between items-center mb-6">
|
310 |
+
<h3 class="text-3xl font-bold text-yellow-400">Game Settings</h3>
|
311 |
+
<button id="closeSettings" class="text-2xl p-2 rounded-full hover:bg-gray-700">
|
312 |
+
✕
|
313 |
+
</button>
|
314 |
+
</div>
|
315 |
+
|
316 |
+
<div class="space-y-8">
|
317 |
+
<!-- Presets Section -->
|
318 |
+
<div>
|
319 |
+
<h4 class="text-xl font-semibold mb-4 text-gray-300">Presets</h4>
|
320 |
+
<div class="grid grid-cols-3 gap-3">
|
321 |
+
<button data-preset="easy" class="preset-btn bg-gray-700 hover:bg-gray-600 py-3 rounded-lg">
|
322 |
+
Easy
|
323 |
+
</button>
|
324 |
+
<button data-preset="medium" class="preset-btn bg-gray-700 hover:bg-gray-600 py-3 rounded-lg active">
|
325 |
+
Medium
|
326 |
+
</button>
|
327 |
+
<button data-preset="hard" class="preset-btn bg-gray-700 hover:bg-gray-600 py-3 rounded-lg">
|
328 |
+
Hard
|
329 |
+
</button>
|
330 |
+
</div>
|
331 |
+
</div>
|
332 |
+
|
333 |
+
<!-- Speed Settings -->
|
334 |
+
<div>
|
335 |
+
<div class="flex justify-between items-center mb-2">
|
336 |
+
<label for="speedRange" class="text-lg font-medium">Speed: <span id="speedValue">2.0</span>s</label>
|
337 |
+
</div>
|
338 |
+
<input type="range" id="speedRange" min="0.5" max="4" step="0.1" value="2" class="w-full">
|
339 |
+
</div>
|
340 |
+
|
341 |
+
<!-- BPM Settings -->
|
342 |
+
<div>
|
343 |
+
<div class="flex justify-between items-center mb-2">
|
344 |
+
<label for="bpmRange" class="text-lg font-medium">Tempo: <span id="bpmValue">128</span> BPM</label>
|
345 |
+
</div>
|
346 |
+
<input type="range" id="bpmRange" min="60" max="200" step="1" value="128" class="w-full">
|
347 |
+
</div>
|
348 |
+
|
349 |
+
<!-- Perfect Window -->
|
350 |
+
<div>
|
351 |
+
<div class="flex justify-between items-center mb-2">
|
352 |
+
<label for="perfectRange" class="text-lg font-medium">Perfect Window: <span id="perfectValue">80</span>ms</label>
|
353 |
+
</div>
|
354 |
+
<input type="range" id="perfectRange" min="20" max="150" step="5" value="80" class="w-full">
|
355 |
+
</div>
|
356 |
+
|
357 |
+
<!-- Good Window -->
|
358 |
+
<div>
|
359 |
+
<div class="flex justify-between items-center mb-2">
|
360 |
+
<label for="goodRange" class="text-lg font-medium">Good Window: <span id="goodValue">160</span>ms</label>
|
361 |
+
</div>
|
362 |
+
<input type="range" id="goodRange" min="50" max="300" step="5" value="160" class="w-full">
|
363 |
+
</div>
|
364 |
+
|
365 |
+
<!-- Number of Ingredients -->
|
366 |
+
<div>
|
367 |
+
<div class="flex justify-between items-center mb-2">
|
368 |
+
<label for="ingredientsRange" class="text-lg font-medium">Ingredients: <span id="ingredientsValue">10</span></label>
|
369 |
+
</div>
|
370 |
+
<input type="range" id="ingredientsRange" min="4" max="20" step="1" value="10" class="w-full">
|
371 |
+
</div>
|
372 |
+
|
373 |
+
<!-- Sequence Length -->
|
374 |
+
<div>
|
375 |
+
<div class="flex justify-between items-center mb-2">
|
376 |
+
<label for="sequenceRange" class="text-lg font-medium">Sequence Length: <span id="sequenceValue">16</span> beats</label>
|
377 |
+
</div>
|
378 |
+
<input type="range" id="sequenceRange" min="8" max="32" step="4" value="16" class="w-full">
|
379 |
+
</div>
|
380 |
+
|
381 |
+
<!-- Save/Load Section -->
|
382 |
+
<div class="pt-4 border-t border-gray-700">
|
383 |
+
<h4 class="text-xl font-semibold mb-4 text-gray-300">Save/Load</h4>
|
384 |
+
<div class="grid grid-cols-2 gap-4">
|
385 |
+
<div>
|
386 |
+
<label for="presetName" class="block text-sm font-medium mb-1">Preset Name</label>
|
387 |
+
<input type="text" id="presetName" placeholder="e.g. Level1" class="w-full bg-gray-700 rounded px-3 py-2 text-white">
|
388 |
+
</div>
|
389 |
+
<div class="flex items-end">
|
390 |
+
<button id="savePreset" class="bg-yellow-600 hover:bg-yellow-500 text-white px-4 py-2 rounded w-full">
|
391 |
+
Save
|
392 |
+
</button>
|
393 |
+
</div>
|
394 |
+
</div>
|
395 |
+
<div class="mt-4">
|
396 |
+
<label for="loadPreset" class="block text-sm font-medium mb-1">Load Preset</label>
|
397 |
+
<select id="loadPreset" class="w-full bg-gray-700 rounded px-3 py-2 text-white">
|
398 |
+
<option value="">Select a preset</option>
|
399 |
+
</select>
|
400 |
+
</div>
|
401 |
+
</div>
|
402 |
+
</div>
|
403 |
+
</div>
|
404 |
+
</div>
|
405 |
+
|
406 |
<!-- Game Screen -->
|
407 |
<div id="gameScreen" class="fixed inset-0 hidden flex flex-col">
|
408 |
<!-- Kitchen ambient light -->
|
|
|
416 |
<div id="comboContainer" class="text-4xl font-bold combo-text hidden">
|
417 |
🔥 <span id="combo">0</span>x
|
418 |
</div>
|
419 |
+
<button id="gameSettingsButton" class="bg-gray-800 hover:bg-gray-700 text-yellow-400 text-xl p-2 rounded-full transition-all transform hover:scale-110 shadow-lg">
|
420 |
+
⚙️
|
421 |
+
</button>
|
422 |
</div>
|
423 |
|
424 |
<!-- Game Area -->
|
|
|
522 |
const energyWaves = document.getElementById('energyWaves');
|
523 |
const screenFlash = document.getElementById('screenFlash');
|
524 |
const startScreenBg = document.getElementById('startScreenBg');
|
525 |
+
const settingsButton = document.getElementById('settingsButton');
|
526 |
+
const gameSettingsButton = document.getElementById('gameSettingsButton');
|
527 |
+
const settingsPanel = document.getElementById('settingsPanel');
|
528 |
+
const closeSettings = document.getElementById('closeSettings');
|
529 |
+
|
530 |
+
// Settings elements
|
531 |
+
const speedRange = document.getElementById('speedRange');
|
532 |
+
const speedValue = document.getElementById('speedValue');
|
533 |
+
const bpmRange = document.getElementById('bpmRange');
|
534 |
+
const bpmValue = document.getElementById('bpmValue');
|
535 |
+
const perfectRange = document.getElementById('perfectRange');
|
536 |
+
const perfectValue = document.getElementById('perfectValue');
|
537 |
+
const goodRange = document.getElementById('goodRange');
|
538 |
+
const goodValue = document.getElementById('goodValue');
|
539 |
+
const ingredientsRange = document.getElementById('ingredientsRange');
|
540 |
+
const ingredientsValue = document.getElementById('ingredientsValue');
|
541 |
+
const sequenceRange = document.getElementById('sequenceRange');
|
542 |
+
const sequenceValue = document.getElementById('sequenceValue');
|
543 |
+
const presetName = document.getElementById('presetName');
|
544 |
+
const savePreset = document.getElementById('savePreset');
|
545 |
+
const loadPreset = document.getElementById('loadPreset');
|
546 |
+
const presetButtons = document.querySelectorAll('[data-preset]');
|
547 |
|
548 |
// Initialize audio context
|
549 |
function initAudio() {
|
|
|
778 |
particle.style.transition = `all ${duration}s ease-out`;
|
779 |
particle.style.boxShadow = `0 0 ${size/2}px ${color}`;
|
780 |
|
781 |
+
document.body.appendChild(article);
|
782 |
|
783 |
setTimeout(() => {
|
784 |
particle.style.opacity = '0';
|
|
|
1247 |
}
|
1248 |
}
|
1249 |
|
1250 |
+
// Load presets from localStorage
|
1251 |
+
function loadPresets() {
|
1252 |
+
const presets = JSON.parse(localStorage.getItem('rhythmChefPresets')) || {};
|
1253 |
+
const select = document.getElementById('loadPreset');
|
1254 |
+
|
1255 |
+
// Clear existing options except the first one
|
1256 |
+
while (select.options.length > 1) {
|
1257 |
+
select.remove(1);
|
1258 |
+
}
|
1259 |
+
|
1260 |
+
// Add presets to dropdown
|
1261 |
+
for (const [name, preset] of Object.entries(presets)) {
|
1262 |
+
const option = document.createElement('option');
|
1263 |
+
option.value = name;
|
1264 |
+
option.textContent = name;
|
1265 |
+
select.appendChild(option);
|
1266 |
+
}
|
1267 |
+
}
|
1268 |
+
|
1269 |
+
// Save preset to localStorage
|
1270 |
+
function savePresetToStorage(name) {
|
1271 |
+
const presets = JSON.parse(localStorage.getItem('rhythmChefPresets')) || {};
|
1272 |
+
|
1273 |
+
presets[name] = {
|
1274 |
+
bpm: gameState.bpm,
|
1275 |
+
cueSpeed: gameState.cueSpeed,
|
1276 |
+
perfectWindow: gameState.perfectWindow,
|
1277 |
+
goodWindow: gameState.goodWindow,
|
1278 |
+
ingredients: gameState.ingredients,
|
1279 |
+
sequence: gameState.sequence
|
1280 |
+
};
|
1281 |
+
|
1282 |
+
localStorage.setItem('rhythmChefPresets', JSON.stringify(presets));
|
1283 |
+
loadPresets();
|
1284 |
+
}
|
1285 |
+
|
1286 |
+
// Load preset from localStorage
|
1287 |
+
function loadPresetFromStorage(name) {
|
1288 |
+
const presets = JSON.parse(localStorage.getItem('rhythmChefPresets')) || {};
|
1289 |
+
const preset = presets[name];
|
1290 |
+
|
1291 |
+
if (preset) {
|
1292 |
+
gameState.bpm = preset.bpm;
|
1293 |
+
gameState.cueSpeed = preset.cueSpeed;
|
1294 |
+
gameState.perfectWindow = preset.perfectWindow;
|
1295 |
+
gameState.goodWindow = preset.goodWindow;
|
1296 |
+
gameState.ingredients = preset.ingredients;
|
1297 |
+
gameState.sequence = preset.sequence;
|
1298 |
+
|
1299 |
+
// Update UI to match
|
1300 |
+
bpmRange.value = gameState.bpm;
|
1301 |
+
bpmValue.textContent = gameState.bpm;
|
1302 |
+
speedRange.value = gameState.cueSpeed;
|
1303 |
+
speedValue.textContent = gameState.cueSpeed.toFixed(1);
|
1304 |
+
perfectRange.value = gameState.perfectWindow;
|
1305 |
+
perfectValue.textContent = gameState.perfectWindow;
|
1306 |
+
goodRange.value = gameState.goodWindow;
|
1307 |
+
goodValue.textContent = gameState.goodWindow;
|
1308 |
+
ingredientsRange.value = gameState.ingredients.length;
|
1309 |
+
ingredientsValue.textContent = gameState.ingredients.length;
|
1310 |
+
sequenceRange.value = gameState.sequence.length;
|
1311 |
+
sequenceValue.textContent = gameState.sequence.length;
|
1312 |
+
|
1313 |
+
// Update preset name field
|
1314 |
+
presetName.value = name;
|
1315 |
+
|
1316 |
+
return true;
|
1317 |
+
}
|
1318 |
+
return false;
|
1319 |
+
}
|
1320 |
+
|
1321 |
+
// Apply preset settings
|
1322 |
+
function applyPreset(presetName) {
|
1323 |
+
switch(presetName) {
|
1324 |
+
case 'easy':
|
1325 |
+
gameState.bpm = 100;
|
1326 |
+
gameState.cueSpeed = 2.5;
|
1327 |
+
gameState.perfectWindow = 100;
|
1328 |
+
gameState.goodWindow = 200;
|
1329 |
+
gameState.ingredients = ['🍅', '🧀', '🍄', '🥩', '🥬', '🍞'];
|
1330 |
+
gameState.sequence = Array(16).fill().map((_, i) => ({ beat: i + 1, type: 'tap' }));
|
1331 |
+
break;
|
1332 |
+
case 'medium':
|
1333 |
+
gameState.bpm = 128;
|
1334 |
+
gameState.cueSpeed = 2;
|
1335 |
+
gameState.perfectWindow = 80;
|
1336 |
+
gameState.goodWindow = 160;
|
1337 |
+
gameState.ingredients = ['🍅', '🧀', '🍄', '🥩', '🥬', '🍞', '🥚', '🦐', '🌽', '🧅'];
|
1338 |
+
gameState.sequence = [
|
1339 |
+
{ beat: 1, type: 'tap' }, { beat: 2, type: 'tap' }, { beat: 3, type: 'tap' }, { beat: 4, type: 'tap' },
|
1340 |
+
{ beat: 5.5, type: 'tap' }, { beat: 6.5, type: 'tap' }, { beat: 7, type: 'tap' }, { beat: 8, type: 'tap' },
|
1341 |
+
{ beat: 9, type: 'tap' }, { beat: 10, type: 'tap' }, { beat: 11.5, type: 'tap' }, { beat: 12.5, type: 'tap' },
|
1342 |
+
{ beat: 13, type: 'tap' }, { beat: 14, type: 'tap' }, { beat: 15, type: 'tap' }, { beat: 16, type: 'tap' }
|
1343 |
+
];
|
1344 |
+
break;
|
1345 |
+
case 'hard':
|
1346 |
+
gameState.bpm = 160;
|
1347 |
+
gameState.cueSpeed = 1.5;
|
1348 |
+
gameState.perfectWindow = 60;
|
1349 |
+
gameState.goodWindow = 120;
|
1350 |
+
gameState.ingredients = ['🍅', '🧀', '🍄', '🥩', '🥬', '🍞', '🥚', '🦐', '🌽', '🧅', '🥕', '🍠', '🥦', '🥒', '🍆'];
|
1351 |
+
gameState.sequence = [
|
1352 |
+
{ beat: 1, type: 'tap' }, { beat: 1.5, type: 'tap' }, { beat: 2, type: 'tap' }, { beat: 2.5, type: 'tap' },
|
1353 |
+
{ beat: 3, type: 'tap' }, { beat: 3.5, type: 'tap' }, { beat: 4, type: 'tap' }, { beat: 4.5, type: 'tap' },
|
1354 |
+
{ beat: 5, type: 'tap' }, { beat: 5.5, type: 'tap' }, { beat: 6, type: 'tap' }, { beat: 6.5, type: 'tap' },
|
1355 |
+
{ beat: 7, type: 'tap' }, { beat: 7.5, type: 'tap' }, { beat: 8, type: 'tap' }, { beat: 8.5, type: 'tap' }
|
1356 |
+
];
|
1357 |
+
break;
|
1358 |
+
}
|
1359 |
+
|
1360 |
+
// Update UI to match
|
1361 |
+
bpmRange.value = gameState.bpm;
|
1362 |
+
bpmValue.textContent = gameState.bpm;
|
1363 |
+
speedRange.value = gameState.cueSpeed;
|
1364 |
+
speedValue.textContent = gameState.cueSpeed.toFixed(1);
|
1365 |
+
perfectRange.value = gameState.perfectWindow;
|
1366 |
+
perfectValue.textContent = gameState.perfectWindow;
|
1367 |
+
goodRange.value = gameState.goodWindow;
|
1368 |
+
goodValue.textContent = gameState.goodWindow;
|
1369 |
+
ingredientsRange.value = gameState.ingredients.length;
|
1370 |
+
ingredientsValue.textContent = gameState.ingredients.length;
|
1371 |
+
sequenceRange.value = gameState.sequence.length;
|
1372 |
+
sequenceValue.textContent = gameState.sequence.length;
|
1373 |
+
|
1374 |
+
// Update active preset button
|
1375 |
+
presetButtons.forEach(btn => {
|
1376 |
+
btn.classList.toggle('active', btn.dataset.preset === presetName);
|
1377 |
+
});
|
1378 |
+
}
|
1379 |
+
|
1380 |
// Initialize game
|
1381 |
function initGame() {
|
1382 |
initAudio();
|
|
|
1396 |
startScreenBg.appendChild(element);
|
1397 |
}
|
1398 |
|
1399 |
+
// Load presets
|
1400 |
+
loadPresets();
|
1401 |
+
|
1402 |
+
// Apply medium preset by default
|
1403 |
+
applyPreset('medium');
|
1404 |
+
|
1405 |
// Start button
|
1406 |
startButton.addEventListener('click', () => {
|
1407 |
startScreen.classList.add('opacity-0');
|
|
|
1418 |
startSequence();
|
1419 |
});
|
1420 |
|
1421 |
+
// Settings button
|
1422 |
+
settingsButton.addEventListener('click', () => {
|
1423 |
+
settingsPanel.classList.remove('hidden');
|
1424 |
+
setTimeout(() => {
|
1425 |
+
settingsPanel.querySelector('.settings-panel').classList.add('open');
|
1426 |
+
}, 10);
|
1427 |
+
});
|
1428 |
+
|
1429 |
+
// Game settings button
|
1430 |
+
gameSettingsButton.addEventListener('click', () => {
|
1431 |
+
settingsPanel.classList.remove('hidden');
|
1432 |
+
setTimeout(() => {
|
1433 |
+
settingsPanel.querySelector('.settings-panel').classList.add('open');
|
1434 |
+
}, 10);
|
1435 |
+
});
|
1436 |
+
|
1437 |
+
// Close settings
|
1438 |
+
closeSettings.addEventListener('click', () => {
|
1439 |
+
settingsPanel.querySelector('.settings-panel').classList.remove('open');
|
1440 |
+
setTimeout(() => {
|
1441 |
+
settingsPanel.classList.add('hidden');
|
1442 |
+
}, 300);
|
1443 |
+
});
|
1444 |
+
|
1445 |
+
// Preset buttons
|
1446 |
+
presetButtons.forEach(btn => {
|
1447 |
+
btn.addEventListener('click', () => {
|
1448 |
+
applyPreset(btn.dataset.preset);
|
1449 |
+
});
|
1450 |
+
});
|
1451 |
+
|
1452 |
+
// Range inputs
|
1453 |
+
speedRange.addEventListener('input', () => {
|
1454 |
+
gameState.cueSpeed = parseFloat(speedRange.value);
|
1455 |
+
speedValue.textContent = gameState.cueSpeed.toFixed(1);
|
1456 |
+
});
|
1457 |
+
|
1458 |
+
bpmRange.addEventListener('input', () => {
|
1459 |
+
gameState.bpm = parseInt(bpmRange.value);
|
1460 |
+
bpmValue.textContent = gameState.bpm;
|
1461 |
+
});
|
1462 |
+
|
1463 |
+
perfectRange.addEventListener('input', () => {
|
1464 |
+
gameState.perfectWindow = parseInt(perfectRange.value);
|
1465 |
+
perfectValue.textContent = gameState.perfectWindow;
|
1466 |
+
});
|
1467 |
+
|
1468 |
+
goodRange.addEventListener('input', () => {
|
1469 |
+
gameState.goodWindow = parseInt(goodRange.value);
|
1470 |
+
goodValue.textContent = gameState.goodWindow;
|
1471 |
+
});
|
1472 |
+
|
1473 |
+
ingredientsRange.addEventListener('input', () => {
|
1474 |
+
const count = parseInt(ingredientsRange.value);
|
1475 |
+
ingredientsValue.textContent = count;
|
1476 |
+
|
1477 |
+
// Basic ingredients that are always available
|
1478 |
+
const baseIngredients = ['🍅', '🧀', '🍄', '🥩', '🥬', '🍞', '🥚'];
|
1479 |
+
|
1480 |
+
// Additional ingredients that can be added
|
1481 |
+
const extraIngredients = ['🦐', '🌽', '🧅', '🥕', '🍠', '🥦', '🥒', '🍆', '🍍', '🥑'];
|
1482 |
+
|
1483 |
+
// Create the ingredient list based on count
|
1484 |
+
gameState.ingredients = [...baseIngredients];
|
1485 |
+
if (count > baseIngredients.length) {
|
1486 |
+
const needed = count - baseIngredients.length;
|
1487 |
+
gameState.ingredients.push(...extraIngredients.slice(0, Math.min(needed, extraIngredients.length)));
|
1488 |
+
} else {
|
1489 |
+
gameState.ingredients = gameState.ingredients.slice(0, count);
|
1490 |
+
}
|
1491 |
+
});
|
1492 |
+
|
1493 |
+
sequenceRange.addEventListener('input', () => {
|
1494 |
+
const length = parseInt(sequenceRange.value);
|
1495 |
+
sequenceValue.textContent = length;
|
1496 |
+
|
1497 |
+
// Create a simple sequence with the specified length
|
1498 |
+
gameState.sequence = Array(length).fill().map((_, i) => {
|
1499 |
+
// Alternate between whole beats and half beats for variety
|
1500 |
+
const beat = i % 2 === 0 ? i + 1 : i + 0.5;
|
1501 |
+
return { beat, type: 'tap' };
|
1502 |
+
});
|
1503 |
+
});
|
1504 |
+
|
1505 |
+
// Save preset
|
1506 |
+
savePreset.addEventListener('click', () => {
|
1507 |
+
const name = presetName.value.trim();
|
1508 |
+
if (name) {
|
1509 |
+
savePresetToStorage(name);
|
1510 |
+
presetName.value = '';
|
1511 |
+
|
1512 |
+
// Show confirmation
|
1513 |
+
const feedback = document.createElement('div');
|
1514 |
+
feedback.textContent = 'Preset saved!';
|
1515 |
+
feedback.className = 'text-green-400 text-sm mt-2';
|
1516 |
+
savePreset.parentNode.appendChild(feedback);
|
1517 |
+
|
1518 |
+
setTimeout(() => {
|
1519 |
+
feedback.remove();
|
1520 |
+
}, 2000);
|
1521 |
+
}
|
1522 |
+
});
|
1523 |
+
|
1524 |
+
// Load preset
|
1525 |
+
loadPreset.addEventListener('change', () => {
|
1526 |
+
if (loadPreset.value) {
|
1527 |
+
loadPresetFromStorage(loadPreset.value);
|
1528 |
+
presetName.value = loadPreset.value;
|
1529 |
+
}
|
1530 |
+
});
|
1531 |
+
|
1532 |
// Tap input
|
1533 |
gameArea.addEventListener('click', handleTap);
|
1534 |
|
prompts.txt
CHANGED
@@ -1,3 +1,4 @@
|
|
1 |
Okay, here is a detailed, implementation-grade plan for building an interactive demonstrator for the core game mechanics of **Rhythm Chef: Beat Bites**. This focuses on creating a minimal viable product (MVP) specifically for user feedback on the core rhythm interaction feel, keeping the codebase limited. **Assumptions:** * **Engine:** Unity (common choice for hypercasual, good for rapid prototyping). * **Language:** C#. * **Target Platform:** Android (easy for distributing APKs for testing). * **Input Focus:** Start with **Tap** only for simplicity. Hold/Swipe can be added later if the Tap feels good. **I. Demonstrator Objectives** 1. Implement the core rhythm interaction: spawning cues, moving cues, detecting timed player input within a target zone. 2. Provide immediate, clear visual and audio feedback for successful hits and misses. 3. Synchronize cue movement and required input timing to a consistent beat (can be a simple metronome track or internal timer). 4. Allow a user to play through a short, predefined sequence of cues. 5. Keep the scope strictly limited to the core mechanic loop for fast iteration and focused feedback. **II. Scope Definition** * **IN SCOPE:** * Single, non-scrolling conveyor belt or lane for cues. * One type of cue requiring a **Tap** input. * One clearly defined target zone. * Precise timing mechanism for cue spawning and hit detection. * Simple visual representation of cues (e.g., colored circles/squares representing ingredients/actions). * Visual feedback (e.g., color change, simple particle effect, text popup like "Perfect!"/"Miss!"). * Basic audio feedback (hit sound, miss sound, beat/metronome sound). * A predefined, short sequence (~15-30 seconds) of Tap cues. * Minimal UI: Start Button, potentially a simple score/combo counter (optional but helpful). * **OUT OF SCOPE:** * Multiple lanes or complex patterns. * Hold or Swipe input types. * Multiple cue types/actions (chop, fry, etc.). * Any meta-game (restaurant, currency, upgrades). * Scoring beyond basic hit/miss feedback (no stars, complex calculations). * Multiple songs or difficulty levels. * Polished art assets (use placeholders). * Advanced UI, menus, settings. * Saving/loading progress. * Performance optimization beyond basic functionality. **III. Key Components & Implementation Details** 1. **`GameManager.cs`** * **Purpose:** Controls the overall state, timing, and sequence playback. * **Properties:** * `float bpm`: Beats per minute for the rhythm. * `float cueSpeed`: Speed at which cues travel towards the target. * `float targetZonePosition`: Y-coordinate (or X if horizontal) of the target zone center. * `float spawnPosition`: Y-coordinate (or X) where cues appear. * `GameObject cuePrefab`: Prefab for the visual cue object. * `Transform cueSpawnPoint`: Transform where cues are instantiated. * `AudioSource metronomeSource`: Optional, for playing a beat sound. * `List<CueData> sequence`: Holds the predefined sequence of cues (timing). * `float songStartTime`: Time when the sequence began (using `AudioSettings.dspTime` or `Time.timeSinceLevelLoad`). * `int currentSequenceIndex`: Tracks the next cue to spawn. * `bool isPlaying`: Game state flag. * **Methods:** * `StartSequence()`: Initializes timing, resets index, sets `isPlaying = true`. Starts metronome if used. * `Update()`: * If `isPlaying`, check `sequence` if it's time to spawn the `currentSequenceIndex` cue based on `bpm` and `songStartTime`. * If spawning, instantiate `cuePrefab` at `cueSpawnPoint`, calculate its `targetTime` (arrival time at target zone), and pass necessary data to the cue's script. Increment `currentSequenceIndex`. * Handles end of sequence. * `StopSequence()`: Sets `isPlaying = false`. 2. **`CueData.cs` (or Struct)** * **Purpose:** Simple data structure to define a single cue in the sequence. * **Properties:** * `float beatTimestamp`: The beat number (e.g., 1, 1.5, 2) within the sequence when this cue should *hit* the target zone. * `CueType type`: Enum (e.g., `Tap`). (Initially only Tap needed). 3. **`CueObject.cs` (attached to `cuePrefab`)** * **Purpose:** Represents a single moving cue. * **Properties:** * `float speed`: Movement speed (set by `GameManager`). * `float targetTime`: The exact game time (`Time.timeSinceLevelLoad` or `dspTime`) this cue should ideally be hit. * `bool isHit`: Flag to prevent multiple hits. * `bool canBeHit`: Flag to indicate if it's currently within the hittable window around the target zone. * **Methods:** * `Initialize(float targetTime, float speed)`: Called by `GameManager` on spawn. * `Update()`: * Move the cue downwards (or towards the target) at `speed * Time.deltaTime`. * Check if the cue has passed the target zone *without* being hit and *after* its hittable window - trigger a Miss condition if so and destroy/disable self. * `OnTriggerEnter/Exit2D(Collider2D other)`: If using physics triggers for the target zone, set `canBeHit = true/false`. * `ProcessHit()`: Called by `InputHandler` when a hit is registered on this cue. Sets `isHit = true`, triggers success feedback, potentially disables the cue visually, and schedules destruction. 4. **`TargetZone.cs` (attached to a GameObject with a Collider2D)** * **Purpose:** Defines the area where input is registered and cues are evaluated. * **Properties:** * `float perfectWindow`: Time window (+/- seconds) for a perfect hit. * `float okWindow`: Time window (+/- seconds) for an acceptable (non-miss) hit. (Keep simple first: just Perfect/Miss). * **Methods:** * `OnTriggerEnter2D/Exit2D(Collider2D other)`: Detects `CueObject` entering/leaving the zone's collider. Could be used by `CueObject` to set its `canBeHit` flag. * **Note:** Hit timing validation logic might live more centrally in `InputHandler` or `GameManager` rather than distributed here. 5. **`InputHandler.cs`** * **Purpose:** Detects player input and checks if it corresponds to a hittable cue. * **Properties:** * `TargetZone targetZone`: Reference to the target zone script/object. * `LayerMask cueLayer`: Physics layer for cues. * **Methods:** * `Update()`: * Check for Tap input (`Input.GetMouseButtonDown(0)` or `Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began`). * If Tap detected: * Determine the current game time (`Time.timeSinceLevelLoad` or `AudioSettings.dspTime`). * Find the "closest" active `CueObject` that is currently `canBeHit` (e.g., using Physics2D overlap checks near the target zone, or iterating through active cues and checking position/timing). * If a potential cue is found: * Calculate `timeDifference = currentTime - cue.targetTime`. * If `Mathf.Abs(timeDifference) <= targetZone.perfectWindow`: * Call `cue.ProcessHit()`. * Trigger "Perfect" feedback via `FeedbackManager`. * Else (if outside window but maybe an OK window exists later): * Trigger "Miss" feedback via `FeedbackManager` (or OK if implementing). * Potentially destroy/disable the cue to prevent late hits. * Else (no cue was hittable): * Optional: Trigger a generic "Miss" feedback for tapping empty space. 6. **`FeedbackManager.cs` (Singleton or easily accessible)** * **Purpose:** Centralized handler for triggering visual/audio feedback. * **Properties:** * `GameObject perfectHitEffectPrefab`: Particle effect/animation for perfect hits. * `GameObject missEffectPrefab`: Effect for misses. * `AudioClip hitSound`: Sound for successful hit. * `AudioClip missSound`: Sound for miss/error. * `AudioSource feedbackAudioSource`: Source to play feedback sounds. * `TextMeshProUGUI feedbackText`: UI Text element to display "Perfect!"/"Miss!". * **Methods:** * `ShowHitFeedback(Vector3 position)`: Instantiate `perfectHitEffectPrefab` at `position`, play `hitSound`, show "Perfect!" text briefly. * `ShowMissFeedback(Vector3 position)`: Instantiate `missEffectPrefab` at `position`, play `missSound`, show "Miss!" text briefly. **IV. Timing Implementation (CRITICAL)** * **Consistency is Key:** Use a consistent time source. * **Option A (Simpler):** `Time.timeSinceLevelLoad`. Easier for basic logic but can drift slightly from audio. Good enough for a demonstrator. * **Option B (More Accurate):** `AudioSettings.dspTime`. Provides highly accurate timing synced with the audio engine. Requires careful handling, especially when scheduling future events based on audio playback. * **Calculations:** * Convert `bpm` to seconds per beat (`60f / bpm`). * Calculate cue `targetTime`: `songStartTime + cue.beatTimestamp * secondsPerBeat`. * Calculate spawn time: `targetTime - travelDuration`, where `travelDuration` is `distance / cueSpeed`. Spawn the cue at this calculated spawn time. **V. UI Implementation (Minimal)** * Canvas with: * `Button` to call `GameManager.StartSequence()`. * `TextMeshProUGUI` (optional) for Score/Combo (incremented in `FeedbackManager` on hit, reset on miss). * `TextMeshProUGUI` for "Perfect!"/"Miss!" feedback text (controlled by `FeedbackManager`). **VI. Placeholder Assets** * **Cue:** Simple white Circle or Square sprite. * **Target Zone:** Semi-transparent rectangle/line across the lane. * **Hit Effect:** Simple particle burst (e.g., Unity's default particle system emitting a few sparks) or a quick scaling/fading circle. * **Miss Effect:** Red flash or different particle effect. * **Audio:** * Metronome: Simple "tick" sound. * Hit Sound: Short, satisfying "click" or "pop". * Miss Sound: Dull "thud" or "buzz". **VII. Build & Test Plan** 1. Implement core components (`GameManager`, `CueObject`, `InputHandler`, `TargetZone`, `FeedbackManager`). 2. Define a simple `sequence` in `GameManager` (e.g., 10 taps on consecutive beats). 3. Use precise timing based on `Time.timeSinceLevelLoad` initially. 4. Integrate basic visual and audio feedback. 5. Test internally: Does it *feel* responsive? Is the timing right? 6. Refine timing windows (`perfectWindow`) based on feel. 7. Build an Android APK. 8. Distribute APK to testers. 9. Gather feedback specifically on: * Timing sensitivity (too strict, too lenient?). * Clarity of visual cues. * Effectiveness of feedback (did they know if they hit/missed?). * Overall satisfaction/fun of the core interaction. **VIII. Success Criteria for Demonstrator** * The demonstrator runs smoothly on a target Android device. * Cues spawn and travel predictably, synchronized with the beat. * User taps near the target zone trigger immediate hit/miss feedback. * Feedback (visual and audio) is clear and correctly corresponds to input timing accuracy. * Testers can understand the core mechanic and provide feedback on its "feel" within 5 minutes of playing. * The codebase is minimal and focused solely on the described scope. Okay, here's an ASCII art representation of the UI and user flow for the **Rhythm Chef: Beat Bites** core mechanics demonstrator. **1. User Flow Diagram** ```ascii +-------------------+ +----------------------+ +----------------------+ | Start Screen | ---+->| Gameplay Loop |---+->| End Screen (Simple)| | (Title, [Start]) | User | (Cues, Target, Input)| User | (Score?, [Restart])| +-------------------+ Taps | | Finishes Sequence +----------------------+ Start +--<-------------------+ Or Fails | (Optional) | | | | Back to Start Screen | +----------------------+ +----------------------+ ``` **2. UI Mockups (ASCII Art)** **A. Start Screen** ```ascii +-----------------------------------------+ | | | // RHYTHM CHEF: BEAT BITES // | | (Core Demo) | | | | | | | | | | +-----------+ | | | [ START ]| | | +-----------+ | | | | | +-----------------------------------------+ User taps [ START ] --> Transitions to Gameplay Screen ``` **B. Gameplay Screen (Mid-Sequence)** * Imagine cues `(*)` moving downwards `v` towards the `TARGET ZONE`. * The player needs to tap when a cue is inside the `TARGET ZONE`. ```ascii +-----------------------------------------+ | SCORE: 005 COMBO: 3x | <--- Optional Score/Combo Display |-----------------------------------------| | | | | | (*) | <--- Incoming Cue 1 | v | | | | | | (*) | <--- Incoming Cue 2 | v | | | | ===================================== | \ | | TARGET ZONE | | <--- The Area to Tap In | ===================================== | / | v | | (*) | <--- Cue that just passed (or was missed) | | | | | | |-----------------------------------------| | FEEDBACK: | <--- Area for text feedback +-----------------------------------------+ ``` **C. Gameplay Screen (Moment of Tap - Perfect Hit)** * Player taps as `(*)` enters the `TARGET ZONE`. * Visual & text feedback appears. ```ascii +-----------------------------------------+ | SCORE: 006 COMBO: 4x | <--- Score/Combo Updated |-----------------------------------------| | | | | | (*) | | v | | | | | | (*) | | v | | | | ===================================== | | | > > > PERFECT! < < < | | <--- Cue hit inside, feedback shown | ===================================== | | | | | (Effect) | <--- Visual effect (e.g., ASCII explosion *`*`) | | | | |-----------------------------------------| | FEEDBACK: Perfect! | <--- Text feedback updated +-----------------------------------------+ ``` **D. Gameplay Screen (Moment of Tap - Miss)** * Player taps too early/late, or misses a cue. ```ascii +-----------------------------------------+ | SCORE: 006 COMBO: 0x | <--- Combo Reset |-----------------------------------------| | | | | | (*) | | v | | | | | | (*) | | v | | | | ===================================== | | | TARGET ZONE | | | ===================================== | | v | | (X) | <--- Missed cue indication / effect | | | | |-----------------------------------------| | FEEDBACK: Miss! | <--- Text feedback updated +-----------------------------------------+ ``` **E. End Screen (Simple Demo End)** * Appears after the predefined sequence finishes. ```ascii +-----------------------------------------+ | | | SEQUENCE COMPLETE! | | | | (Final Score: 025) | <--- Optional final score | | | | | +-------------+ | | | [ RESTART ] | | | +-------------+ | | | | | +-----------------------------------------+ User taps [ RESTART ] --> Transitions back to Gameplay Screen (restarts sequence) (Or could go back to the Start Screen) ``` This ASCII representation outlines the essential visual elements and the flow between them for the core mechanics demonstrator, focusing on clarity for user feedback on the rhythm interaction. Okay, here are specific pixel-based dimensions and positions for the UI elements based on a **reference screen resolution of 1080x1920 pixels (Width x Height - Portrait Orientation)**. **Important Considerations:** * **Reference Only:** These are *fixed pixel values* for the specified reference resolution. In a real implementation (e.g., using Unity Canvas), you would use anchors, pivots, and potentially scaling components (like Canvas Scaler UI Scale Mode) to ensure the UI adapts correctly to different screen sizes and aspect ratios. * **Origin Point:** Assume the (0, 0) coordinate is the **bottom-left** corner of the screen, common in some UI systems, or **top-left** (common in others - I will specify based on typical Unity Canvas usage: **(0,0) is Bottom-Left, (1080, 1920) is Top-Right** unless stated otherwise). Positions usually refer to the object's **pivot point** (often the center, unless specified). * **Font Sizes:** Pixel sizes for fonts are approximate and depend heavily on the specific font file used. * **Actors:** Actors like Cues and Effects have positions that change dynamically or are instantiated at specific world/UI coordinates. --- **A. Start Screen (1080x1920)** * **Widget:** `Title Text` (e.g., "RHYTHM CHEF: BEAT BITES") * **Position (Pivot: Center):** (540, 1600) `(Center X, ~83% Height)` * **Dimensions:** Auto-sized by text, constrained if needed. * **Font Size:** 90px * **Anchor Preset (Unity):** Top-Center * **Widget:** `Subtitle Text` (e.g., "(Core Demo)") * **Position (Pivot: Center):** (540, 1480) `(Center X, below Title)` * **Dimensions:** Auto-sized by text. * **Font Size:** 40px * **Anchor Preset (Unity):** Top-Center * **Widget:** `Start Button` * **Position (Pivot: Center):** (540, 400) `(Center X, ~21% Height)` * **Dimensions:** 450px (Width) x 150px (Height) * **Anchor Preset (Unity):** Bottom-Center * **Widget:** `Start Button Text` (e.g., "[ START ]") * **Position:** Centered within the Start Button. * **Font Size:** 60px --- **B. Gameplay Screen (1080x1920)** * **Widget:** `Score Text Label` (e.g., "SCORE:") * **Position (Pivot: Top-Left):** (40, 1880) `(Padding Left, Padding Top)` * **Dimensions:** Auto-sized by text. * **Font Size:** 50px * **Anchor Preset (Unity):** Top-Left * **Widget:** `Score Value Text` (e.g., "000") * **Position (Pivot: Top-Left):** (200, 1880) `(Right of Label, Same Top)` * **Dimensions:** Auto-sized by text (allow space for growth). * **Font Size:** 50px * **Anchor Preset (Unity):** Top-Left * **Widget:** `Combo Text Label` (e.g., "COMBO:") * **Position (Pivot: Top-Right):** (900, 1880) `(Approx position, adjust based on Value width)` * **Dimensions:** Auto-sized by text. * **Font Size:** 50px * **Anchor Preset (Unity):** Top-Right * **Widget:** `Combo Value Text` (e.g., "0x") * **Position (Pivot: Top-Right):** (1040, 1880) `(Padding Right, Same Top)` * **Dimensions:** Auto-sized by text (allow space for growth). * **Font Size:** 50px * **Anchor Preset (Unity):** Top-Right * **Area:** `Cue Travel Area` (Logical, no visual widget) * **Bounds:** Approx X = Center (e.g., 540), Y from 1800 (Spawn) down to ~200 (Despawn below target). * **Widget:** `Target Zone Visual` (The horizontal bar indicator) * **Position (Pivot: Center):** (540, 400) `(Center X, Defined Y)` * **Dimensions:** 900px (Width) x 80px (Height) * **Anchor Preset (Unity):** Position manually or Bottom-Center with Y offset. * **Note:** The *logical* hit detection center Y might be exactly 400. The timing windows (`perfectWindow`, `okWindow`) define the tolerance around this point in *time*, not necessarily pixels vertically (though they are related via cue speed). * **Actor:** `Cue Object` (Visual representation) * **Position:** Dynamic. Spawns near Y=1800, travels downwards to Y=400 (target). X is typically centered (540). * **Dimensions:** 80px (Width) x 80px (Height) (Example, adjust for visual clarity) * **Widget:** `Feedback Text` (e.g., "Perfect!", "Miss!") * **Position (Pivot: Center):** (540, 600) `(Center X, Above Target Zone)` * **Dimensions:** Auto-sized by text. * **Font Size:** 70px * **Anchor Preset (Unity):** Position manually or relative to Target Zone. (Appears temporarily). * **Actor:** `Hit/Miss Feedback Effect` (Particles/Animation) * **Position:** Instantiated at the Cue's position *when* the hit/miss occurs (approx X=540, Y=400). * **Dimensions:** Dynamic based on the effect design (e.g., expands to 150x150px briefly). --- **C. End Screen (1080x1920)** * **Widget:** `Completion Text` (e.g., "SEQUENCE COMPLETE!") * **Position (Pivot: Center):** (540, 1200) `(Center X, ~62% Height)` * **Dimensions:** Auto-sized by text. * **Font Size:** 70px * **Anchor Preset (Unity):** Center * **Widget:** `Final Score Text` (Optional, e.g., "Final Score: 025") * **Position (Pivot: Center):** (540, 1050) `(Center X, below Completion text)` * **Dimensions:** Auto-sized by text. * **Font Size:** 50px * **Anchor Preset (Unity):** Center * **Widget:** `Restart Button` * **Position (Pivot: Center):** (540, 400) `(Center X, ~21% Height - same as Start Button)` * **Dimensions:** 450px (Width) x 150px (Height) * **Anchor Preset (Unity):** Bottom-Center * **Widget:** `Restart Button Text` (e.g., "[ RESTART ]") * **Position:** Centered within the Restart Button. * **Font Size:** 60px --- Remember to implement proper UI scaling and anchoring in your chosen game engine to handle various device resolutions effectively beyond this 1080x1920 reference.
|
2 |
Make the look and feel 100 times better, employ procedural graphics and emojis as sprites.
|
3 |
-
Act as graphical effect engineer and add many more cool effect to the game play, make it explode with cool effects, particles etc., background effect ...
|
|
|
|
1 |
Okay, here is a detailed, implementation-grade plan for building an interactive demonstrator for the core game mechanics of **Rhythm Chef: Beat Bites**. This focuses on creating a minimal viable product (MVP) specifically for user feedback on the core rhythm interaction feel, keeping the codebase limited. **Assumptions:** * **Engine:** Unity (common choice for hypercasual, good for rapid prototyping). * **Language:** C#. * **Target Platform:** Android (easy for distributing APKs for testing). * **Input Focus:** Start with **Tap** only for simplicity. Hold/Swipe can be added later if the Tap feels good. **I. Demonstrator Objectives** 1. Implement the core rhythm interaction: spawning cues, moving cues, detecting timed player input within a target zone. 2. Provide immediate, clear visual and audio feedback for successful hits and misses. 3. Synchronize cue movement and required input timing to a consistent beat (can be a simple metronome track or internal timer). 4. Allow a user to play through a short, predefined sequence of cues. 5. Keep the scope strictly limited to the core mechanic loop for fast iteration and focused feedback. **II. Scope Definition** * **IN SCOPE:** * Single, non-scrolling conveyor belt or lane for cues. * One type of cue requiring a **Tap** input. * One clearly defined target zone. * Precise timing mechanism for cue spawning and hit detection. * Simple visual representation of cues (e.g., colored circles/squares representing ingredients/actions). * Visual feedback (e.g., color change, simple particle effect, text popup like "Perfect!"/"Miss!"). * Basic audio feedback (hit sound, miss sound, beat/metronome sound). * A predefined, short sequence (~15-30 seconds) of Tap cues. * Minimal UI: Start Button, potentially a simple score/combo counter (optional but helpful). * **OUT OF SCOPE:** * Multiple lanes or complex patterns. * Hold or Swipe input types. * Multiple cue types/actions (chop, fry, etc.). * Any meta-game (restaurant, currency, upgrades). * Scoring beyond basic hit/miss feedback (no stars, complex calculations). * Multiple songs or difficulty levels. * Polished art assets (use placeholders). * Advanced UI, menus, settings. * Saving/loading progress. * Performance optimization beyond basic functionality. **III. Key Components & Implementation Details** 1. **`GameManager.cs`** * **Purpose:** Controls the overall state, timing, and sequence playback. * **Properties:** * `float bpm`: Beats per minute for the rhythm. * `float cueSpeed`: Speed at which cues travel towards the target. * `float targetZonePosition`: Y-coordinate (or X if horizontal) of the target zone center. * `float spawnPosition`: Y-coordinate (or X) where cues appear. * `GameObject cuePrefab`: Prefab for the visual cue object. * `Transform cueSpawnPoint`: Transform where cues are instantiated. * `AudioSource metronomeSource`: Optional, for playing a beat sound. * `List<CueData> sequence`: Holds the predefined sequence of cues (timing). * `float songStartTime`: Time when the sequence began (using `AudioSettings.dspTime` or `Time.timeSinceLevelLoad`). * `int currentSequenceIndex`: Tracks the next cue to spawn. * `bool isPlaying`: Game state flag. * **Methods:** * `StartSequence()`: Initializes timing, resets index, sets `isPlaying = true`. Starts metronome if used. * `Update()`: * If `isPlaying`, check `sequence` if it's time to spawn the `currentSequenceIndex` cue based on `bpm` and `songStartTime`. * If spawning, instantiate `cuePrefab` at `cueSpawnPoint`, calculate its `targetTime` (arrival time at target zone), and pass necessary data to the cue's script. Increment `currentSequenceIndex`. * Handles end of sequence. * `StopSequence()`: Sets `isPlaying = false`. 2. **`CueData.cs` (or Struct)** * **Purpose:** Simple data structure to define a single cue in the sequence. * **Properties:** * `float beatTimestamp`: The beat number (e.g., 1, 1.5, 2) within the sequence when this cue should *hit* the target zone. * `CueType type`: Enum (e.g., `Tap`). (Initially only Tap needed). 3. **`CueObject.cs` (attached to `cuePrefab`)** * **Purpose:** Represents a single moving cue. * **Properties:** * `float speed`: Movement speed (set by `GameManager`). * `float targetTime`: The exact game time (`Time.timeSinceLevelLoad` or `dspTime`) this cue should ideally be hit. * `bool isHit`: Flag to prevent multiple hits. * `bool canBeHit`: Flag to indicate if it's currently within the hittable window around the target zone. * **Methods:** * `Initialize(float targetTime, float speed)`: Called by `GameManager` on spawn. * `Update()`: * Move the cue downwards (or towards the target) at `speed * Time.deltaTime`. * Check if the cue has passed the target zone *without* being hit and *after* its hittable window - trigger a Miss condition if so and destroy/disable self. * `OnTriggerEnter/Exit2D(Collider2D other)`: If using physics triggers for the target zone, set `canBeHit = true/false`. * `ProcessHit()`: Called by `InputHandler` when a hit is registered on this cue. Sets `isHit = true`, triggers success feedback, potentially disables the cue visually, and schedules destruction. 4. **`TargetZone.cs` (attached to a GameObject with a Collider2D)** * **Purpose:** Defines the area where input is registered and cues are evaluated. * **Properties:** * `float perfectWindow`: Time window (+/- seconds) for a perfect hit. * `float okWindow`: Time window (+/- seconds) for an acceptable (non-miss) hit. (Keep simple first: just Perfect/Miss). * **Methods:** * `OnTriggerEnter2D/Exit2D(Collider2D other)`: Detects `CueObject` entering/leaving the zone's collider. Could be used by `CueObject` to set its `canBeHit` flag. * **Note:** Hit timing validation logic might live more centrally in `InputHandler` or `GameManager` rather than distributed here. 5. **`InputHandler.cs`** * **Purpose:** Detects player input and checks if it corresponds to a hittable cue. * **Properties:** * `TargetZone targetZone`: Reference to the target zone script/object. * `LayerMask cueLayer`: Physics layer for cues. * **Methods:** * `Update()`: * Check for Tap input (`Input.GetMouseButtonDown(0)` or `Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began`). * If Tap detected: * Determine the current game time (`Time.timeSinceLevelLoad` or `AudioSettings.dspTime`). * Find the "closest" active `CueObject` that is currently `canBeHit` (e.g., using Physics2D overlap checks near the target zone, or iterating through active cues and checking position/timing). * If a potential cue is found: * Calculate `timeDifference = currentTime - cue.targetTime`. * If `Mathf.Abs(timeDifference) <= targetZone.perfectWindow`: * Call `cue.ProcessHit()`. * Trigger "Perfect" feedback via `FeedbackManager`. * Else (if outside window but maybe an OK window exists later): * Trigger "Miss" feedback via `FeedbackManager` (or OK if implementing). * Potentially destroy/disable the cue to prevent late hits. * Else (no cue was hittable): * Optional: Trigger a generic "Miss" feedback for tapping empty space. 6. **`FeedbackManager.cs` (Singleton or easily accessible)** * **Purpose:** Centralized handler for triggering visual/audio feedback. * **Properties:** * `GameObject perfectHitEffectPrefab`: Particle effect/animation for perfect hits. * `GameObject missEffectPrefab`: Effect for misses. * `AudioClip hitSound`: Sound for successful hit. * `AudioClip missSound`: Sound for miss/error. * `AudioSource feedbackAudioSource`: Source to play feedback sounds. * `TextMeshProUGUI feedbackText`: UI Text element to display "Perfect!"/"Miss!". * **Methods:** * `ShowHitFeedback(Vector3 position)`: Instantiate `perfectHitEffectPrefab` at `position`, play `hitSound`, show "Perfect!" text briefly. * `ShowMissFeedback(Vector3 position)`: Instantiate `missEffectPrefab` at `position`, play `missSound`, show "Miss!" text briefly. **IV. Timing Implementation (CRITICAL)** * **Consistency is Key:** Use a consistent time source. * **Option A (Simpler):** `Time.timeSinceLevelLoad`. Easier for basic logic but can drift slightly from audio. Good enough for a demonstrator. * **Option B (More Accurate):** `AudioSettings.dspTime`. Provides highly accurate timing synced with the audio engine. Requires careful handling, especially when scheduling future events based on audio playback. * **Calculations:** * Convert `bpm` to seconds per beat (`60f / bpm`). * Calculate cue `targetTime`: `songStartTime + cue.beatTimestamp * secondsPerBeat`. * Calculate spawn time: `targetTime - travelDuration`, where `travelDuration` is `distance / cueSpeed`. Spawn the cue at this calculated spawn time. **V. UI Implementation (Minimal)** * Canvas with: * `Button` to call `GameManager.StartSequence()`. * `TextMeshProUGUI` (optional) for Score/Combo (incremented in `FeedbackManager` on hit, reset on miss). * `TextMeshProUGUI` for "Perfect!"/"Miss!" feedback text (controlled by `FeedbackManager`). **VI. Placeholder Assets** * **Cue:** Simple white Circle or Square sprite. * **Target Zone:** Semi-transparent rectangle/line across the lane. * **Hit Effect:** Simple particle burst (e.g., Unity's default particle system emitting a few sparks) or a quick scaling/fading circle. * **Miss Effect:** Red flash or different particle effect. * **Audio:** * Metronome: Simple "tick" sound. * Hit Sound: Short, satisfying "click" or "pop". * Miss Sound: Dull "thud" or "buzz". **VII. Build & Test Plan** 1. Implement core components (`GameManager`, `CueObject`, `InputHandler`, `TargetZone`, `FeedbackManager`). 2. Define a simple `sequence` in `GameManager` (e.g., 10 taps on consecutive beats). 3. Use precise timing based on `Time.timeSinceLevelLoad` initially. 4. Integrate basic visual and audio feedback. 5. Test internally: Does it *feel* responsive? Is the timing right? 6. Refine timing windows (`perfectWindow`) based on feel. 7. Build an Android APK. 8. Distribute APK to testers. 9. Gather feedback specifically on: * Timing sensitivity (too strict, too lenient?). * Clarity of visual cues. * Effectiveness of feedback (did they know if they hit/missed?). * Overall satisfaction/fun of the core interaction. **VIII. Success Criteria for Demonstrator** * The demonstrator runs smoothly on a target Android device. * Cues spawn and travel predictably, synchronized with the beat. * User taps near the target zone trigger immediate hit/miss feedback. * Feedback (visual and audio) is clear and correctly corresponds to input timing accuracy. * Testers can understand the core mechanic and provide feedback on its "feel" within 5 minutes of playing. * The codebase is minimal and focused solely on the described scope. Okay, here's an ASCII art representation of the UI and user flow for the **Rhythm Chef: Beat Bites** core mechanics demonstrator. **1. User Flow Diagram** ```ascii +-------------------+ +----------------------+ +----------------------+ | Start Screen | ---+->| Gameplay Loop |---+->| End Screen (Simple)| | (Title, [Start]) | User | (Cues, Target, Input)| User | (Score?, [Restart])| +-------------------+ Taps | | Finishes Sequence +----------------------+ Start +--<-------------------+ Or Fails | (Optional) | | | | Back to Start Screen | +----------------------+ +----------------------+ ``` **2. UI Mockups (ASCII Art)** **A. Start Screen** ```ascii +-----------------------------------------+ | | | // RHYTHM CHEF: BEAT BITES // | | (Core Demo) | | | | | | | | | | +-----------+ | | | [ START ]| | | +-----------+ | | | | | +-----------------------------------------+ User taps [ START ] --> Transitions to Gameplay Screen ``` **B. Gameplay Screen (Mid-Sequence)** * Imagine cues `(*)` moving downwards `v` towards the `TARGET ZONE`. * The player needs to tap when a cue is inside the `TARGET ZONE`. ```ascii +-----------------------------------------+ | SCORE: 005 COMBO: 3x | <--- Optional Score/Combo Display |-----------------------------------------| | | | | | (*) | <--- Incoming Cue 1 | v | | | | | | (*) | <--- Incoming Cue 2 | v | | | | ===================================== | \ | | TARGET ZONE | | <--- The Area to Tap In | ===================================== | / | v | | (*) | <--- Cue that just passed (or was missed) | | | | | | |-----------------------------------------| | FEEDBACK: | <--- Area for text feedback +-----------------------------------------+ ``` **C. Gameplay Screen (Moment of Tap - Perfect Hit)** * Player taps as `(*)` enters the `TARGET ZONE`. * Visual & text feedback appears. ```ascii +-----------------------------------------+ | SCORE: 006 COMBO: 4x | <--- Score/Combo Updated |-----------------------------------------| | | | | | (*) | | v | | | | | | (*) | | v | | | | ===================================== | | | > > > PERFECT! < < < | | <--- Cue hit inside, feedback shown | ===================================== | | | | | (Effect) | <--- Visual effect (e.g., ASCII explosion *`*`) | | | | |-----------------------------------------| | FEEDBACK: Perfect! | <--- Text feedback updated +-----------------------------------------+ ``` **D. Gameplay Screen (Moment of Tap - Miss)** * Player taps too early/late, or misses a cue. ```ascii +-----------------------------------------+ | SCORE: 006 COMBO: 0x | <--- Combo Reset |-----------------------------------------| | | | | | (*) | | v | | | | | | (*) | | v | | | | ===================================== | | | TARGET ZONE | | | ===================================== | | v | | (X) | <--- Missed cue indication / effect | | | | |-----------------------------------------| | FEEDBACK: Miss! | <--- Text feedback updated +-----------------------------------------+ ``` **E. End Screen (Simple Demo End)** * Appears after the predefined sequence finishes. ```ascii +-----------------------------------------+ | | | SEQUENCE COMPLETE! | | | | (Final Score: 025) | <--- Optional final score | | | | | +-------------+ | | | [ RESTART ] | | | +-------------+ | | | | | +-----------------------------------------+ User taps [ RESTART ] --> Transitions back to Gameplay Screen (restarts sequence) (Or could go back to the Start Screen) ``` This ASCII representation outlines the essential visual elements and the flow between them for the core mechanics demonstrator, focusing on clarity for user feedback on the rhythm interaction. Okay, here are specific pixel-based dimensions and positions for the UI elements based on a **reference screen resolution of 1080x1920 pixels (Width x Height - Portrait Orientation)**. **Important Considerations:** * **Reference Only:** These are *fixed pixel values* for the specified reference resolution. In a real implementation (e.g., using Unity Canvas), you would use anchors, pivots, and potentially scaling components (like Canvas Scaler UI Scale Mode) to ensure the UI adapts correctly to different screen sizes and aspect ratios. * **Origin Point:** Assume the (0, 0) coordinate is the **bottom-left** corner of the screen, common in some UI systems, or **top-left** (common in others - I will specify based on typical Unity Canvas usage: **(0,0) is Bottom-Left, (1080, 1920) is Top-Right** unless stated otherwise). Positions usually refer to the object's **pivot point** (often the center, unless specified). * **Font Sizes:** Pixel sizes for fonts are approximate and depend heavily on the specific font file used. * **Actors:** Actors like Cues and Effects have positions that change dynamically or are instantiated at specific world/UI coordinates. --- **A. Start Screen (1080x1920)** * **Widget:** `Title Text` (e.g., "RHYTHM CHEF: BEAT BITES") * **Position (Pivot: Center):** (540, 1600) `(Center X, ~83% Height)` * **Dimensions:** Auto-sized by text, constrained if needed. * **Font Size:** 90px * **Anchor Preset (Unity):** Top-Center * **Widget:** `Subtitle Text` (e.g., "(Core Demo)") * **Position (Pivot: Center):** (540, 1480) `(Center X, below Title)` * **Dimensions:** Auto-sized by text. * **Font Size:** 40px * **Anchor Preset (Unity):** Top-Center * **Widget:** `Start Button` * **Position (Pivot: Center):** (540, 400) `(Center X, ~21% Height)` * **Dimensions:** 450px (Width) x 150px (Height) * **Anchor Preset (Unity):** Bottom-Center * **Widget:** `Start Button Text` (e.g., "[ START ]") * **Position:** Centered within the Start Button. * **Font Size:** 60px --- **B. Gameplay Screen (1080x1920)** * **Widget:** `Score Text Label` (e.g., "SCORE:") * **Position (Pivot: Top-Left):** (40, 1880) `(Padding Left, Padding Top)` * **Dimensions:** Auto-sized by text. * **Font Size:** 50px * **Anchor Preset (Unity):** Top-Left * **Widget:** `Score Value Text` (e.g., "000") * **Position (Pivot: Top-Left):** (200, 1880) `(Right of Label, Same Top)` * **Dimensions:** Auto-sized by text (allow space for growth). * **Font Size:** 50px * **Anchor Preset (Unity):** Top-Left * **Widget:** `Combo Text Label` (e.g., "COMBO:") * **Position (Pivot: Top-Right):** (900, 1880) `(Approx position, adjust based on Value width)` * **Dimensions:** Auto-sized by text. * **Font Size:** 50px * **Anchor Preset (Unity):** Top-Right * **Widget:** `Combo Value Text` (e.g., "0x") * **Position (Pivot: Top-Right):** (1040, 1880) `(Padding Right, Same Top)` * **Dimensions:** Auto-sized by text (allow space for growth). * **Font Size:** 50px * **Anchor Preset (Unity):** Top-Right * **Area:** `Cue Travel Area` (Logical, no visual widget) * **Bounds:** Approx X = Center (e.g., 540), Y from 1800 (Spawn) down to ~200 (Despawn below target). * **Widget:** `Target Zone Visual` (The horizontal bar indicator) * **Position (Pivot: Center):** (540, 400) `(Center X, Defined Y)` * **Dimensions:** 900px (Width) x 80px (Height) * **Anchor Preset (Unity):** Position manually or Bottom-Center with Y offset. * **Note:** The *logical* hit detection center Y might be exactly 400. The timing windows (`perfectWindow`, `okWindow`) define the tolerance around this point in *time*, not necessarily pixels vertically (though they are related via cue speed). * **Actor:** `Cue Object` (Visual representation) * **Position:** Dynamic. Spawns near Y=1800, travels downwards to Y=400 (target). X is typically centered (540). * **Dimensions:** 80px (Width) x 80px (Height) (Example, adjust for visual clarity) * **Widget:** `Feedback Text` (e.g., "Perfect!", "Miss!") * **Position (Pivot: Center):** (540, 600) `(Center X, Above Target Zone)` * **Dimensions:** Auto-sized by text. * **Font Size:** 70px * **Anchor Preset (Unity):** Position manually or relative to Target Zone. (Appears temporarily). * **Actor:** `Hit/Miss Feedback Effect` (Particles/Animation) * **Position:** Instantiated at the Cue's position *when* the hit/miss occurs (approx X=540, Y=400). * **Dimensions:** Dynamic based on the effect design (e.g., expands to 150x150px briefly). --- **C. End Screen (1080x1920)** * **Widget:** `Completion Text` (e.g., "SEQUENCE COMPLETE!") * **Position (Pivot: Center):** (540, 1200) `(Center X, ~62% Height)` * **Dimensions:** Auto-sized by text. * **Font Size:** 70px * **Anchor Preset (Unity):** Center * **Widget:** `Final Score Text` (Optional, e.g., "Final Score: 025") * **Position (Pivot: Center):** (540, 1050) `(Center X, below Completion text)` * **Dimensions:** Auto-sized by text. * **Font Size:** 50px * **Anchor Preset (Unity):** Center * **Widget:** `Restart Button` * **Position (Pivot: Center):** (540, 400) `(Center X, ~21% Height - same as Start Button)` * **Dimensions:** 450px (Width) x 150px (Height) * **Anchor Preset (Unity):** Bottom-Center * **Widget:** `Restart Button Text` (e.g., "[ RESTART ]") * **Position:** Centered within the Restart Button. * **Font Size:** 60px --- Remember to implement proper UI scaling and anchoring in your chosen game engine to handle various device resolutions effectively beyond this 1080x1920 reference.
|
2 |
Make the look and feel 100 times better, employ procedural graphics and emojis as sprites.
|
3 |
+
Act as graphical effect engineer and add many more cool effect to the game play, make it explode with cool effects, particles etc., background effect ...
|
4 |
+
Add directly into the game handles, toggles etc. allowing to fine-tune the gameplay such as the speed, number of items, toleralnce, all key aspect allowing the user to find sweet spot for the game play, and store the values along with a tag such as level1 into the local store for re-use across gameplays
|