schermata iniziale

This commit is contained in:
2026-03-13 16:52:38 +01:00
parent f63b995018
commit 3edcf891ff
7 changed files with 479 additions and 430 deletions
Regular → Executable
+53 -53
View File
@@ -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.
+58 -45
View File
@@ -1,46 +1,59 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>Semplice Island FPS</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="start">
<video autoplay muted loop id="bg-video">
<source src="./video/background.mp4" type="video/mp4">
</video>
<div class="overlay"></div>
<div class="content">
<div class="top-section">
<img src="./img/titolo.png" alt="titolo" class="main-title" />
</div>
<div class="container bottom-section">
<div class="item left">
<div class="controls-box">
<h3>Controlli</h3>
<p><kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd> Movimento</p>
<p><kbd>Mouse</kbd> Camera</p>
</div>
</div>
<div class="item center">
<span class="pulse-text">Clicca per giocare</span>
</div>
<div class="item right"></div>
</div>
</div>
</div>
<div id="punti">Rifiuti: <span id="score">0</span></div>
<div id="tempo">Tempo: <span id="time">XX:XX</span></div>
<div id="crosshair">+</div>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
}
}
</script>
<script type="module" src="script.js"></script>
</body>
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>Semplice Island FPS</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="start">
<video autoplay muted loop id="bg-video">
<source src="./video/background.mp4" type="video/mp4">
</video>
<div class="overlay"></div>
<div class="content">
<div class="top-section">
<img src="titolo.png" alt="titolo" class="main-title" />
</div>
<div class="container bottom-section">
<div class="item left">
<div class="controls-box">
<h3>Controlli</h3>
<p>Movimento</p>
<p><kbd>Freccia su</kbd> Avanti</p>
<p><kbd>Freccia giù</kbd> Indietro</p>
<p><kbd>Freccia destra</kbd> Destra</p>
<p><kbd>Freccia sinistra</kbd> Sinistra</p>
<p><kbd>Mouse</kbd> Punto di vista</p>
</div>
</div>
<div class="item center">
<span class="pulse-text">Clicca per giocare</span>
</div>
<div class="item right">
<div class="instructions-box">
<h3>Istruzioni</h3>
<p>Obiettivo: raccogliere tutti i rifiuti sullisola</p>
<p>Attenzione allo scadere del tempo!</p>
<p>Usa i comandi per esplorare e ripulire</p>
</div>
</div>
<div class="item right"></div>
</div>
</div>
</div>
<div id="punti">Rifiuti: <span id="score">0</span></div>
<div id="tempo">Tempo: <span id="time">XX:XX</span></div>
<div id="crosshair">+</div>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
}
}
</script>
<script type="module" src="script.js"></script>
</body>
</html>
+58 -58
View File
@@ -1,58 +1,58 @@
<mxfile host="simonez-cloud.ddns.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36" version="29.5.2">
<diagram name="Page-1" id="i5RDAsyKeYJ5rP6QEPLP">
<mxGraphModel dx="1026" dy="507" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="hKNONnDbWQ7ccftNSymI-2" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1">
<mxGeometry height="440" width="680" x="80" y="40" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-5" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="" vertex="1">
<mxGeometry height="370" width="440" x="190" y="70" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-3" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" value="" vertex="1">
<mxGeometry height="340" width="400" x="220" y="80" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-4" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#AEBEAD;strokeColor=#567441;" value="" vertex="1">
<mxGeometry height="90" width="134" x="400" y="190" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-6" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="N° rifiuti" vertex="1">
<mxGeometry height="50" width="90" x="80" y="40" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-7" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="tempo" vertex="1">
<mxGeometry height="50" width="70" x="690" y="40" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-8" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;" value="" vertex="1">
<mxGeometry height="440" width="680" x="80" y="600" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-9" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="plastica" vertex="1">
<mxGeometry height="110" width="110" x="100" y="810" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-10" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="umido" vertex="1">
<mxGeometry height="110" width="110" x="230" y="810" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-11" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="indifferenziata" vertex="1">
<mxGeometry height="110" width="110" x="365" y="810" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-12" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="vetro" vertex="1">
<mxGeometry height="110" width="110" x="500" y="810" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-13" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="carta" vertex="1">
<mxGeometry height="110" width="110" x="630" y="810" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-14" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="aulliminio" vertex="1">
<mxGeometry height="80" width="80" x="115" y="940" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-15" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="tempo" vertex="1">
<mxGeometry height="30" width="60" x="390" y="610" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-16" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="immagine rifiuto" vertex="1">
<mxGeometry height="120" width="120" x="360" y="650" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-17" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="punteggio" vertex="1">
<mxGeometry height="30" width="60" x="90" y="610" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
<mxfile host="simonez-cloud.ddns.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36" version="29.5.2">
<diagram name="Page-1" id="i5RDAsyKeYJ5rP6QEPLP">
<mxGraphModel dx="1026" dy="507" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="hKNONnDbWQ7ccftNSymI-2" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1">
<mxGeometry height="440" width="680" x="80" y="40" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-5" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="" vertex="1">
<mxGeometry height="370" width="440" x="190" y="70" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-3" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" value="" vertex="1">
<mxGeometry height="340" width="400" x="220" y="80" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-4" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#AEBEAD;strokeColor=#567441;" value="" vertex="1">
<mxGeometry height="90" width="134" x="400" y="190" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-6" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="N° rifiuti" vertex="1">
<mxGeometry height="50" width="90" x="80" y="40" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-7" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="tempo" vertex="1">
<mxGeometry height="50" width="70" x="690" y="40" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-8" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;" value="" vertex="1">
<mxGeometry height="440" width="680" x="80" y="600" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-9" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="plastica" vertex="1">
<mxGeometry height="110" width="110" x="100" y="810" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-10" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="umido" vertex="1">
<mxGeometry height="110" width="110" x="230" y="810" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-11" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="indifferenziata" vertex="1">
<mxGeometry height="110" width="110" x="365" y="810" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-12" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="vetro" vertex="1">
<mxGeometry height="110" width="110" x="500" y="810" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-13" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="carta" vertex="1">
<mxGeometry height="110" width="110" x="630" y="810" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-14" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="aulliminio" vertex="1">
<mxGeometry height="80" width="80" x="115" y="940" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-15" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="tempo" vertex="1">
<mxGeometry height="30" width="60" x="390" y="610" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-16" parent="1" style="whiteSpace=wrap;html=1;aspect=fixed;" value="immagine rifiuto" vertex="1">
<mxGeometry height="120" width="120" x="360" y="650" as="geometry" />
</mxCell>
<mxCell id="hKNONnDbWQ7ccftNSymI-17" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="punteggio" vertex="1">
<mxGeometry height="30" width="60" x="90" y="610" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
+7 -7
View File
@@ -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(?)
+90 -90
View File
@@ -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();
+213 -177
View File
@@ -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; }
}
Executable
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB