diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 8814e2c..1694be5 --- a/README.md +++ b/README.md @@ -1,54 +1,54 @@ -# 🌍 Pulisci il Mondo - Progetto per la Giornata della Terra - -**Pulisci il Mondo** è un videogioco web in 3D nato per sensibilizzare gli utenti sulla gestione dei rifiuti e l'importanza del riciclo. Il progetto adotta un'architettura Fullstack basata su **XAMPP** per la gestione di una classifica globale e il salvataggio dei record. - ---- - -## 🛠️ Architettura Tecnica -* **Entrypoint:** `index.php` (Pagina di atterraggio con trailer e controlli). -* **Backend:** PHP + MySQL (XAMPP) per la gestione del database e dei punteggi. -* **Frontend:** **Three.js** (motore 3D), JavaScript (ES6+), CSS3. -* **Trasferimento Dati:** Utilizzo di Cookie temporanei o `localStorage` per mantenere i dati tra le diverse fasi di gioco. - ---- - -## 🗺️ Roadmap di Sviluppo - -### 🚩 Milestone Generali (Nucleo del Progetto) -- [⚠️] **Design UI & Mockup:** Finalizzazione degli asset grafici (basati sullo schema Draw.io). -- [❌] **Configurazione Database:** Creazione delle tabelle `classifica` e `records` su MySQL. -- [❌] **Logica Entrypoint:** Sviluppo di `index.php` con video di sfondo e overlay dei comandi. -- [❌] **Sistema di Trasferimento:** Implementazione logica per il passaggio dei dati dalla Fase 1 alla Fase 2. -- [❌] **Gestione Impostazioni:** Pannello per regolare il volume e inserire il nome della squadra (servira' nella classifica). -- [❌] **Pagina Dinamica:** Creazione della classifica in PHP con recupero dati in tempo reale. - -### 🏝️ Fase 1: La Raccolta (Timer: 1m) -- [⚠️] **Ambiente di gioco:** Modellazione dell'isola 3D con Three.js e gestione dei confini della mappa. -- [⚠️] **Sistema di Spawn:** Posizionamento casuale dei rifiuti su 20 coordinate casuali. -- [⚠️] **Sistema Ostacoli:** Inserimento di modelli 3D di alberi e oggetti ambientali decorativi. -- [⚠️] **Meccaniche di Raccolta:** Gestione delle collisioni e incremento del punteggio ecologico. -- [✅] **Interfaccia (HUD):** Overlay con Timer (60s), contatore rifiuti e disattivazione della pausa. -- [❌] **Rifinitura (Polish):** Animazioni di raccolta e ottimizzazione delle mesh 3D. - -### ♻️ Fase 2: Lo Smistamento (Timer: 5s * Punteggio) -- [❌] **Timer Dinamico:** Calcolo del tempo a disposizione basato sul successo della Fase 1. -- [❌] **Motore di Smistamento:** Logica di convalida (Rifiuto ↔️ Bidone corretto). -- [❌] **Interfaccia Utente:** Layout con i 6 bidoni (Plastica, Umido, Indifferenziata, Vetro, Carta, Alluminio). -- [❌] **Gestione Input:** Meccanica di interazione tramite Drag & Drop o selezione rapida. -- [❌] **Feedback Visivo:** Effetti sonori e visivi per risposte corrette o errate. - -### 🏆 Fase Finale: Classifica & Record -- [❌] **Calcolo Punteggio:** Elaborazione dei risultati finali e calcolo dei bonus velocità. -- [❌] **Verifica Record:** Confronto del punteggio con il record personale salvato. -- [❌] **Classifica Globale:** Script PHP per l'invio e la visualizzazione della Top 10 dal database. -- [❌] **Schermata Game Over:** Riepilogo statistiche e opzione per riavviare la partita. - ---- - -## 📂 Struttura delle Cartelle -* `/assets` - Modelli 3D, video loop, texture dei rifiuti e icone dei bidoni. -* `/css` - Fogli di stile per i menu e l'interfaccia di gioco (HUD). -* `/js` - Logica core e rendering (Three.js, stage1.js, stage2.js). -* `/php` - Script per il backend (`db_connect.php`, `save_score.php`). -* `/games` - Pagine HTML dedicate alle sessioni di gioco (`fase1.html`, `fase2.html`). +# 🌍 Pulisci il Mondo - Progetto per la Giornata della Terra + +**Pulisci il Mondo** è un videogioco web in 3D nato per sensibilizzare gli utenti sulla gestione dei rifiuti e l'importanza del riciclo. Il progetto adotta un'architettura Fullstack basata su **XAMPP** per la gestione di una classifica globale e il salvataggio dei record. + +--- + +## 🛠️ Architettura Tecnica +* **Entrypoint:** `index.php` (Pagina di atterraggio con trailer e controlli). +* **Backend:** PHP + MySQL (XAMPP) per la gestione del database e dei punteggi. +* **Frontend:** **Three.js** (motore 3D), JavaScript (ES6+), CSS3. +* **Trasferimento Dati:** Utilizzo di Cookie temporanei o `localStorage` per mantenere i dati tra le diverse fasi di gioco. + +--- + +## 🗺️ Roadmap di Sviluppo + +### 🚩 Milestone Generali (Nucleo del Progetto) +- [⚠️] **Design UI & Mockup:** Finalizzazione degli asset grafici (basati sullo schema Draw.io). +- [❌] **Configurazione Database:** Creazione delle tabelle `classifica` e `records` su MySQL. +- [❌] **Logica Entrypoint:** Sviluppo di `index.php` con video di sfondo e overlay dei comandi. +- [❌] **Sistema di Trasferimento:** Implementazione logica per il passaggio dei dati dalla Fase 1 alla Fase 2. +- [❌] **Gestione Impostazioni:** Pannello per regolare il volume e inserire il nome della squadra (servira' nella classifica). +- [❌] **Pagina Dinamica:** Creazione della classifica in PHP con recupero dati in tempo reale. + +### 🏝️ Fase 1: La Raccolta (Timer: 1m) +- [⚠️] **Ambiente di gioco:** Modellazione dell'isola 3D con Three.js e gestione dei confini della mappa. +- [⚠️] **Sistema di Spawn:** Posizionamento casuale dei rifiuti su 20 coordinate casuali. +- [⚠️] **Sistema Ostacoli:** Inserimento di modelli 3D di alberi e oggetti ambientali decorativi. +- [⚠️] **Meccaniche di Raccolta:** Gestione delle collisioni e incremento del punteggio ecologico. +- [✅] **Interfaccia (HUD):** Overlay con Timer (60s), contatore rifiuti e disattivazione della pausa. +- [❌] **Rifinitura (Polish):** Animazioni di raccolta e ottimizzazione delle mesh 3D. + +### ♻️ Fase 2: Lo Smistamento (Timer: 5s * Punteggio) +- [❌] **Timer Dinamico:** Calcolo del tempo a disposizione basato sul successo della Fase 1. +- [❌] **Motore di Smistamento:** Logica di convalida (Rifiuto ↔️ Bidone corretto). +- [❌] **Interfaccia Utente:** Layout con i 6 bidoni (Plastica, Umido, Indifferenziata, Vetro, Carta, Alluminio). +- [❌] **Gestione Input:** Meccanica di interazione tramite Drag & Drop o selezione rapida. +- [❌] **Feedback Visivo:** Effetti sonori e visivi per risposte corrette o errate. + +### 🏆 Fase Finale: Classifica & Record +- [❌] **Calcolo Punteggio:** Elaborazione dei risultati finali e calcolo dei bonus velocità. +- [❌] **Verifica Record:** Confronto del punteggio con il record personale salvato. +- [❌] **Classifica Globale:** Script PHP per l'invio e la visualizzazione della Top 10 dal database. +- [❌] **Schermata Game Over:** Riepilogo statistiche e opzione per riavviare la partita. + +--- + +## 📂 Struttura delle Cartelle +* `/assets` - Modelli 3D, video loop, texture dei rifiuti e icone dei bidoni. +* `/css` - Fogli di stile per i menu e l'interfaccia di gioco (HUD). +* `/js` - Logica core e rendering (Three.js, stage1.js, stage2.js). +* `/php` - Script per il backend (`db_connect.php`, `save_score.php`). +* `/games` - Pagine HTML dedicate alle sessioni di gioco (`fase1.html`, `fase2.html`). * `index.php` - Homepage e controller principale del progetto. \ No newline at end of file diff --git a/index.html b/index.html index 6420f4b..e19c41d 100755 --- a/index.html +++ b/index.html @@ -1,46 +1,59 @@ - - - - - Semplice Island FPS - - - -
- -
-
-
- titolo -
-
-
-
-

Controlli

-

WASD Movimento

-

Mouse Camera

-
-
-
- Clicca per giocare -
-
-
-
-
-
Rifiuti: 0
-
Tempo: XX:XX
-
+
- - - + + + + + Semplice Island FPS + + + +
+ +
+
+
+ titolo +
+
+
+
+

Controlli

+

Movimento

+

Freccia su Avanti

+

Freccia giù Indietro

+

Freccia destra Destra

+

Freccia sinistra Sinistra

+

Mouse Punto di vista

+
+
+
+ Clicca per giocare +
+
+
+

Istruzioni

+

Obiettivo: raccogliere tutti i rifiuti sull’isola

+

Attenzione allo scadere del tempo!

+

Usa i comandi per esplorare e ripulire

+
+
+ +
+
+
+
+
Rifiuti: 0
+
Tempo: XX:XX
+
+
+ + + \ No newline at end of file diff --git a/progetto/idea gioco.drawio b/progetto/idea gioco.drawio index 09a02d2..6840754 100755 --- a/progetto/idea gioco.drawio +++ b/progetto/idea gioco.drawio @@ -1,58 +1,58 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/progetto/progetto.txt b/progetto/progetto.txt index 233fac8..ff7eff8 100755 --- a/progetto/progetto.txt +++ b/progetto/progetto.txt @@ -1,8 +1,8 @@ -Nome gioco: Pulisci il mondo - -posizioni: 20 posizioni predefinite scelte random -tempo 1° fase: 1m -tempo 2° fase: 5s * punteggio -schermata iniziale: click + controlli + bckg trailer (loop video) + titolo -schermata intermedia: istruzioni +Nome gioco: Pulisci il mondo + +posizioni: 20 posizioni predefinite scelte random +tempo 1° fase: 1m +tempo 2° fase: 5s * punteggio +schermata iniziale: click + controlli + bckg trailer (loop video) + titolo +schermata intermedia: istruzioni schermata finale: punteggio finale + record + classifica(?) \ No newline at end of file diff --git a/script.js b/script.js index 4c6a774..d1e7f80 100755 --- a/script.js +++ b/script.js @@ -1,91 +1,91 @@ -import * as THREE from 'three'; -import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'; -import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; - -// 1. SCENA E CAMERA -const scene = new THREE.Scene(); -scene.background = new THREE.Color(0x87ceeb); -const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); -const renderer = new THREE.WebGLRenderer(); -renderer.setSize(window.innerWidth, window.innerHeight); -document.body.appendChild(renderer.domElement); -window.addEventListener('resize', function () { - renderer.setSize(window.innerWidth, window.innerHeight) -}) - -// 2. LUCI E AMBIENTE -scene.add(new THREE.AmbientLight(0xffffff, 1)); -const island = new THREE.Mesh(new THREE.CircleGeometry(20, 32), new THREE.MeshStandardMaterial({ color: 0x4df556 })); // #4df555 -island.rotation.x = -Math.PI / 2; -scene.add(island); - -// 3. CONTROLLI -function lock() { - document.getElementById('start').style.display = 'none'; - document.getElementById('punti').style.display = 'block'; - document.getElementById('tempo').style.display = 'block'; - document.getElementById('crosshair').style.display = 'block'; -} -function unlock() { - const startDiv = document.getElementById('start'); - startDiv.style.display = 'flex'; // Forza il layout Flexbox - // Nascondi l'HUD - document.getElementById('punti').style.display = 'none'; - document.getElementById('tempo').style.display = 'none'; - document.getElementById('crosshair').style.display = 'none'; -} -const controls = new PointerLockControls(camera, document.body); -document.getElementById('start').addEventListener('click', () => controls.lock()); -controls.addEventListener('lock', lock); -controls.addEventListener('unlock', unlock); - -// 4. GESTIONE RIFIUTI (20 pezzi) -const trashArray = []; -let score = 0; -const loader = new GLTFLoader(); - -loader.load('models/rifiuto.glb', (gltf) => { - const mesh = gltf.scene.getObjectByName("Trash_Pile_03_GEO"); - mesh.geometry.center(); // Centra l'oggetto per collisioni precise - for (let i = 0; i < 20; i++) { - const clone = mesh.clone(); - scene.add(clone); - trashArray.push(clone); - spawn(clone); - } -}); - -function spawn(obj) { - const a = Math.random() * Math.PI * 2; - const r = Math.random() * 18; - obj.position.set(Math.cos(a) * r, 0.3, Math.sin(a) * r); -} - -// 5. MOVIMENTO E COLLISIONI -const keys = {}; -document.onkeydown = (e) => keys[e.code] = true; -document.onkeyup = (e) => keys[e.code] = false; - -function animate() { - requestAnimationFrame(animate); - if (controls.isLocked) { - if (keys['KeyW']) controls.moveForward(0.15); - if (keys['KeyS']) controls.moveForward(-0.15); - if (keys['KeyA']) controls.moveRight(-0.15); - if (keys['KeyD']) controls.moveRight(0.15); - camera.position.y = 1.6; - - // Controllo collisioni - const pPos = camera.position.clone(); - pPos.y -= 1.0; - trashArray.forEach(t => { - if (new THREE.Box3().setFromObject(t).expandByScalar(0.3).containsPoint(pPos)) { - score++; - document.getElementById('score').innerText = score; - spawn(t); - } - }); - } - renderer.render(scene, camera); -} +import * as THREE from 'three'; +import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'; +import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; + +// 1. SCENA E CAMERA +const scene = new THREE.Scene(); +scene.background = new THREE.Color(0x87ceeb); +const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); +const renderer = new THREE.WebGLRenderer(); +renderer.setSize(window.innerWidth, window.innerHeight); +document.body.appendChild(renderer.domElement); +window.addEventListener('resize', function () { + renderer.setSize(window.innerWidth, window.innerHeight) +}) + +// 2. LUCI E AMBIENTE +scene.add(new THREE.AmbientLight(0xffffff, 1)); +const island = new THREE.Mesh(new THREE.CircleGeometry(20, 32), new THREE.MeshStandardMaterial({ color: 0x4df556 })); // #4df555 +island.rotation.x = -Math.PI / 2; +scene.add(island); + +// 3. CONTROLLI +function lock() { + document.getElementById('start').style.display = 'none'; + document.getElementById('punti').style.display = 'block'; + document.getElementById('tempo').style.display = 'block'; + document.getElementById('crosshair').style.display = 'block'; +} +function unlock() { + const startDiv = document.getElementById('start'); + startDiv.style.display = 'flex'; // Forza il layout Flexbox + // Nascondi l'HUD + document.getElementById('punti').style.display = 'none'; + document.getElementById('tempo').style.display = 'none'; + document.getElementById('crosshair').style.display = 'none'; +} +const controls = new PointerLockControls(camera, document.body); +document.getElementById('start').addEventListener('click', () => controls.lock()); +controls.addEventListener('lock', lock); +controls.addEventListener('unlock', unlock); + +// 4. GESTIONE RIFIUTI (20 pezzi) +const trashArray = []; +let score = 0; +const loader = new GLTFLoader(); + +loader.load('models/rifiuto.glb', (gltf) => { + const mesh = gltf.scene.getObjectByName("Trash_Pile_03_GEO"); + mesh.geometry.center(); // Centra l'oggetto per collisioni precise + for (let i = 0; i < 20; i++) { + const clone = mesh.clone(); + scene.add(clone); + trashArray.push(clone); + spawn(clone); + } +}); + +function spawn(obj) { + const a = Math.random() * Math.PI * 2; + const r = Math.random() * 18; + obj.position.set(Math.cos(a) * r, 0.3, Math.sin(a) * r); +} + +// 5. MOVIMENTO E COLLISIONI +const keys = {}; +document.onkeydown = (e) => keys[e.code] = true; +document.onkeyup = (e) => keys[e.code] = false; + +function animate() { + requestAnimationFrame(animate); + if (controls.isLocked) { + if (keys['KeyW']) controls.moveForward(0.15); + if (keys['KeyS']) controls.moveForward(-0.15); + if (keys['KeyA']) controls.moveRight(-0.15); + if (keys['KeyD']) controls.moveRight(0.15); + camera.position.y = 1.6; + + // Controllo collisioni + const pPos = camera.position.clone(); + pPos.y -= 1.0; + trashArray.forEach(t => { + if (new THREE.Box3().setFromObject(t).expandByScalar(0.3).containsPoint(pPos)) { + score++; + document.getElementById('score').innerText = score; + spawn(t); + } + }); + } + renderer.render(scene, camera); +} animate(); \ No newline at end of file diff --git a/style.css b/style.css index 709990e..3c54309 100755 --- a/style.css +++ b/style.css @@ -1,178 +1,214 @@ -:root { - --top: 10px; - --border: 20px; - --text: 1.5rem; -} -body { - margin: 0; - overflow: hidden; - font-family: sans-serif; -} -#punti { - position: absolute; - top: var(--top); - left: var(--border); - color: white; - font-size: var(--text); - pointer-events: none; - display: none; -} -#tempo { - position: absolute; - top: var(--top); - right: var(--border); - color: white; - font-size: var(--text); - pointer-events: none; - display: none; -} -#punti, #tempo { - position: absolute; - top: var(--top); - color: white; - font-size: var(--text); - pointer-events: none; - display: none; - /* Aggiungi questa riga */ - text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7); - font-weight: bold; -} - -/* Cursore */ -#crosshair { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - color: white; - font-size: 24px; - font-family: sans-serif; - pointer-events: none; - display: none; /* Sarà visualizzato solo durante il gioco */ - z-index: 10; -} - -/* Schermata iniziale del gioco */ -#start { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - overflow: hidden; - color: white; - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - /* Queste 4 righe garantiscono la centratura totale */ - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - z-index: 100; /* Assicurati che sia sopra al renderer di Three.js */ -} - -/* Video a tutto schermo */ -#bg-video { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: -1; - object-fit: cover; /* Riempie lo schermo senza deformare */ -} - -.overlay { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.4); - z-index: 0; - pointer-events: none; /* Il click attraversa l'overlay e arriva a #start */ -} - -.content { - position: relative; - z-index: 1; - height: 100%; - width: 100%; /* Aggiunto */ - display: flex; - flex-direction: column; -} - -/* Divisione 50% Sopra */ -.top-section { - flex: 1; - display: flex; - align-items: center; - justify-content: center; -} - -.main-title { - max-width: 80%; - height: auto; -} - -/* Divisione 50% Sotto */ -.bottom-section { - flex: 1; - display: flex; - flex-direction: row; - width: 100%; /* Forza l'espansione orizzontale */ - align-items: flex-start; - padding-top: 20px; -} - -.item { - /* flex: 1 0 33%; significa: cresci, non restringerti, base 33% */ - flex: 1 0 33.33%; - display: flex; - justify-content: center; - text-align: center; -} - -/* Styling dei Controlli (KBD) */ -.controls-box { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; -} -.controls-box h3 { - margin-bottom: 10px; - font-size: 1.2rem; - text-transform: uppercase; -} -.controls-box h3, .controls-box p, .pulse-text { - margin-top: 0; -} - -kbd { - background-color: #eee; - border-radius: 3px; - border: 1px solid #b4b4b4; - box-shadow: 0 1px 1px rgba(0,0,0,0.2), 0 2px 0 0 rgba(255,255,255,0.7) inset; - color: #333; - display: inline-block; - font-size: 0.85em; - font-weight: 700; - line-height: 1; - padding: 2px 4px; - white-space: nowrap; - margin: 0 2px; -} - -/* Animazione per "Clicca per giocare" */ -.pulse-text { - font-size: 1.5rem; - text-transform: uppercase; - letter-spacing: 2px; - animation: pulse 2s infinite; -} - -@keyframes pulse { - 0% { opacity: 1; } - 50% { opacity: 0.4; } - 100% { opacity: 1; } +:root { + --top: 10px; + --border: 20px; + --text: 1.5rem; +} +body { + margin: 0; + overflow: hidden; + font-family: sans-serif; +} +#punti { + position: absolute; + top: var(--top); + left: var(--border); + color: white; + font-size: var(--text); + pointer-events: none; + display: none; +} +#tempo { + position: absolute; + top: var(--top); + right: var(--border); + color: white; + font-size: var(--text); + pointer-events: none; + display: none; +} +#punti, #tempo { + position: absolute; + top: var(--top); + color: white; + font-size: var(--text); + pointer-events: none; + display: none; + /* Aggiungi questa riga */ + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7); + font-weight: bold; +} + +/* Cursore */ +#crosshair { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: white; + font-size: 24px; + font-family: sans-serif; + pointer-events: none; + display: none; /* Sarà visualizzato solo durante il gioco */ + z-index: 10; +} + +/* Schermata iniziale del gioco */ +#start { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + color: white; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + /* Queste 4 righe garantiscono la centratura totale */ + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + z-index: 100; /* Assicurati che sia sopra al renderer di Three.js */ +} + +/* Video a tutto schermo */ +#bg-video { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + object-fit: cover; /* Riempie lo schermo senza deformare */ +} + +.overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.4); + z-index: 0; + pointer-events: none; /* Il click attraversa l'overlay e arriva a #start */ +} + +.content { + position: relative; + z-index: 1; + height: 100%; + width: 100%; /* Aggiunto */ + display: flex; + flex-direction: column; +} + +/* Divisione 50% Sopra */ +.top-section { + flex: 1; + display: flex; + align-items: center; + justify-content: center; +} + +.main-title { + width: 25%; + height: auto; +} + + +/* Divisione 50% Sotto */ +.bottom-section { + flex: 1; + display: flex; + flex-direction: row; + width: 100%; /* Forza l'espansione orizzontale */ + align-items: flex-start; + padding-top: 20px; +} + +.item { + /* flex: 1 0 33%; significa: cresci, non restringerti, base 33% */ + flex: 1 0 33.33%; + display: flex; + justify-content: center; + text-align: center; +} + +/* Styling dei Controlli */ +.controls-box { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + color: white; + text-shadow: 2px 2px 4px rgba(0,0,0,0.8); + font-family: 'Press Start 2P', cursive; +} + +.controls-box h3 { + margin-bottom: 10px; + font-size: 1.4rem; + text-transform: uppercase; + text-align: center; +} + +.controls-box p { + margin: 6px 0; + font-size: 1.1rem; + text-align: center; +} + +/* Stile dei tasti */ +.controls-box kbd { + background-color: #5e5d5d; + color: #fff; + padding: 4px 8px; + border-radius: 4px; + font-family: monospace; + font-weight: bold; + margin: 6px 0; +} + +/* Box delle istruzioni */ +.instructions-box { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + color: white; + text-shadow: 2px 2px 4px rgba(0,0,0,0.8); + font-family: 'Press Start 2P', cursive; +} + +.instructions-box h3 { + margin-bottom: 10px; + font-size: 1.4rem; + text-transform: uppercase; + text-align: center; +} + +.instructions-box p { + margin: 6px 0; + font-size: 1.1rem; + text-align: center; +} + + + + +/* Animazione per "Clicca per giocare" */ +.pulse-text { + font-size: 1.5rem; + text-transform: uppercase; + letter-spacing: 2px; + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { opacity: 1; } + 50% { opacity: 0.4; } + 100% { opacity: 1; } } \ No newline at end of file diff --git a/titolo.png b/titolo.png new file mode 100755 index 0000000..ff979ea Binary files /dev/null and b/titolo.png differ